A lightweight JavaScript game engine
git clone git://soophie.de/game-engine
blob: 0114159156db7a7956ae4a0528b72e6cb3f639b9 (3514 bytes)[doc][blame][raw]
1
import { scaleCanvas } from './canvas.js';2
import * as Collision from './collision.js';4
export class Game {5
constructor(width, height, { fps } = {}) {6
this.width = width || 900;7
this.height = height || 600;8
this.fps = 1000 / (fps || 60);9
this.then = Date.now();10
this.canvas = document.createElement('canvas');11
scaleCanvas(this.canvas, this.width, this.height);12
this.g = this.canvas.getContext('2d');13
this.events = [];14
}15
start() {16
requestAnimationFrame(this.loop.bind(this));17
this.update();18
this.render(this.g);19
}20
loop() {21
requestAnimationFrame(this.loop.bind(this));22
this.now = Date.now();23
this.elapsed = this.now - this.then;24
if(this.elapsed > this.fps) {25
this.then = this.now - (this.elapsed % this.fps);26
for(const event of this.events) {27
if(this['on' + event.type]) {28
this['on' + event.type](event);29
}30
}31
this.update();32
this.render(this.g);33
this.events.length = 0;34
}35
}36
update() {37
// nothing38
}39
render() {40
this.g.clearRect(0, 0, this.width, this.height);41
}42
resize(width, height) {43
this.width = width;44
this.height = height;45
scaleCanvas(this.canvas, this.width, this.height);46
}47
bindEvent(element, type) {48
element['on' + type] = event => this.events.push(event);49
}50
}52
export class Group {53
constructor() {54
this.items = [];55
}56
update() {57
for(let item of this.items) {58
item.update();59
}60
}61
render(g) {62
for(let item of this.items) {63
item.render(g);64
}65
}66
causeEvent(type, events) {67
for(let item of this.items) {68
item.causeEvent(type, events);69
}70
}71
collide() {72
for(const i of this.items) {73
i.items = [];74
i.collided = false;75
}76
const duplicate = [];77
for(const a of this.items) {78
for(const b of this.items) {79
if(!duplicate.includes(b)) {80
if(a !== b) {81
let collided = false;82
if(a.shape instanceof Shape.Rect && b.shape instanceof Shape.Rect) {83
collided = Collision.collideRect(a, b);84
}85
else if(a.shape instanceof Shape.Circle && b.shape instanceof Shape.Circle) {86
collided = Collision.collideCircle(a, b);87
}88
else {89
collided = Collision.collideRectAndCircle(a, b);90
}91
if(!a.collided) {92
a.collided = collided;93
}94
if(!b.collided) {95
b.collided = collided;96
}97
if(collided) {98
if(!a.items.includes(b)) {99
a.items.push(b);100
}101
if(!b.items.includes(a)) {102
b.items.push(a);103
}104
}105
}106
}107
}108
duplicate.push(a);109
}110
}111
add(item) {112
this.items.push(item);113
}114
remove(item) {115
const i = this.items.findIndex(n => n === item);116
if(i !== -1) {117
this.items.splice(i, 1);118
}119
}120
[Symbol.iterator]() {121
return this.items.values();122
}123
}125
export class Surface {126
constructor(x, y, shape) {127
this.x = x;128
this.y = y;129
this.shape = shape;130
}131
update() {132
// nothing133
}134
render(g) {135
if(this.shape instanceof Shape.Rect) {136
g.beginPath();137
g.rect(this.x, this.y, this.shape.width, this.shape.height);138
g.stroke();139
}140
if(this.shape instanceof Shape.Circle) {141
g.beginPath();142
g.arc(this.x, this.y, this.shape.radius, 2 * Math.PI, 0);143
g.stroke();144
}145
}146
causeEvent(type, events) {147
if(this['on' + type]) {148
for(const event of events) {149
if(event.type === type) {150
this['on' + type](event);151
}152
}153
}154
}155
}157
class Rect {158
constructor(width, height) {159
this.width = width;160
this.height = height;161
}162
}164
class Circle {165
constructor(radius) {166
this.radius = radius;167
}168
}