| @@ -0,0 +1,3 @@ | |||
| { | |||
| "presets": ["env"] | |||
| } | |||
| @@ -0,0 +1,2 @@ | |||
| public/bundle.js | |||
| node_modules | |||
| @@ -0,0 +1,51 @@ | |||
| import Rect from './Rect'; | |||
| import Vec2 from './Vec2'; | |||
| export class Trait { | |||
| constructor(entity, name) { | |||
| this.name = name; | |||
| this.entity = entity; | |||
| this.enabled = true; | |||
| } | |||
| init() {} | |||
| update(dt) {} | |||
| } | |||
| export default class Entity { | |||
| constructor(level) { | |||
| this.level = level; | |||
| this.bounds = new Rect(); | |||
| this.velocity = new Vec2(); | |||
| this.t = {}; | |||
| this.traits = []; | |||
| } | |||
| init() { | |||
| for (let trait of this.traits) { | |||
| trait.init(); | |||
| } | |||
| } | |||
| update(dt) { | |||
| for (let trait of this.traits) { | |||
| if (trait.enabled) | |||
| trait.update(dt); | |||
| } | |||
| this.bounds.pos.x += this.velocity.x * dt; | |||
| this.bounds.pos.y += this.velocity.y * dt; | |||
| } | |||
| draw(ctx) {} | |||
| has(name) { | |||
| let t = this.t[name]; | |||
| return t != null && t.enabled; | |||
| } | |||
| addTrait(t) { | |||
| this.t[t.name] = t; | |||
| this.traits.push(t); | |||
| } | |||
| } | |||
| @@ -0,0 +1,65 @@ | |||
| import Player from './entities/Player'; | |||
| export default class Level { | |||
| constructor(canvas) { | |||
| this.step = 1 / 120; | |||
| this.canvas = canvas; | |||
| this.ctx = canvas.getContext("2d"); | |||
| this.lastTime = null; | |||
| this.raf = null; | |||
| this.timeAcc = 0; | |||
| this.entities = []; | |||
| } | |||
| spawn(ent, x, y) { | |||
| ent.bounds.pos.set(x, y); | |||
| this.entities.push(ent); | |||
| ent.init(); | |||
| } | |||
| physics(dt) { | |||
| this.entities.forEach(ent => | |||
| ent.update(dt)); | |||
| this.entities.forEach(ent => { | |||
| ent.bounds.pos.x += ent.velocity.x * dt; | |||
| ent.bounds.pos.y += ent.velocity.y * dt; | |||
| }); | |||
| } | |||
| draw() { | |||
| this.canvas.width = window.innerWidth; | |||
| this.canvas.height = window.innerHeight; | |||
| this.entities.forEach(ent => ent.draw(this.ctx)); | |||
| } | |||
| update(time) { | |||
| if (this.lastTime != null) { | |||
| let dt = (time - this.lastTime) / 1000; | |||
| this.timeAcc += dt; | |||
| while (this.timeAcc > this.step) { | |||
| this.physics(this.step); | |||
| this.timeAcc -= this.step; | |||
| } | |||
| this.draw(); | |||
| } | |||
| this.lastTime = time; | |||
| this.raf = requestAnimationFrame(time => this.update(time)); | |||
| } | |||
| start() { | |||
| this.stop(); | |||
| this.lastTime = null; | |||
| this.update(); | |||
| } | |||
| stop() { | |||
| if (this.raf != null) { | |||
| cancelAnimationFrame(this.raf); | |||
| this.raf = null; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,45 @@ | |||
| import Vec2 from './Vec2'; | |||
| export default class Rect { | |||
| constructor(pos = new Vec2(), size = new Vec2()) { | |||
| this.pos = pos; | |||
| this.size = size; | |||
| } | |||
| draw(ctx) { | |||
| ctx.moveTo(this.left, this.top); | |||
| ctx.lineTo(this.right, this.top); | |||
| ctx.lineTo(this.right, this.bottom); | |||
| ctx.lineTo(this.left, this.bottom); | |||
| ctx.closePath(); | |||
| ctx.stroke(); | |||
| } | |||
| get top() { | |||
| return this.pos.y; | |||
| } | |||
| set top(n) { | |||
| this.pos.y = n; | |||
| } | |||
| get bottom() { | |||
| return this.pos.y + this.size.y; | |||
| } | |||
| set bottom(n) { | |||
| this.pos.y = n - this.size.y; | |||
| } | |||
| get left() { | |||
| return this.pos.x; | |||
| } | |||
| set left(n) { | |||
| this.pos.x = n; | |||
| } | |||
| get right() { | |||
| return this.pos.x + this.size.x; | |||
| } | |||
| set right(n) { | |||
| this.pos.x = n - this.size.x; | |||
| } | |||
| } | |||
| @@ -0,0 +1,11 @@ | |||
| export default class Vec2 { | |||
| constructor(x = 0, y = 0) { | |||
| this.x = x; | |||
| this.y = y; | |||
| } | |||
| set(x, y) { | |||
| this.x = x; | |||
| this.y = y; | |||
| } | |||
| } | |||
| @@ -0,0 +1,17 @@ | |||
| import Entity from '../Entity'; | |||
| import Vec2 from '../Vec2'; | |||
| import KeyboardControls from '../traits/KeyboardControls'; | |||
| export default class Player extends Entity { | |||
| constructor(level) { | |||
| super(level); | |||
| this.bounds.size.set(20, 20); | |||
| this.addTrait(new KeyboardControls(this)); | |||
| } | |||
| draw(ctx) { | |||
| this.bounds.draw(ctx); | |||
| } | |||
| } | |||
| @@ -0,0 +1,9 @@ | |||
| import Level from './Level'; | |||
| import Player from './entities/Player'; | |||
| import Vec2 from './Vec2'; | |||
| let level = new Level(document.getElementById("canvas")); | |||
| level.spawn(new Player(level), 20, 20); | |||
| level.start(); | |||
| @@ -0,0 +1,34 @@ | |||
| import {Trait} from '../Entity'; | |||
| export default class KeyboardControls extends Trait { | |||
| constructor(entity) { | |||
| super(entity, "keyboardControls"); | |||
| this.speed = 500; | |||
| this.map = { | |||
| KeyA: 'left', | |||
| KeyD: 'right', | |||
| }; | |||
| this.pressed = {}; | |||
| } | |||
| onkey(evt) { | |||
| let name = this.map[evt.code]; | |||
| if (name == null) return; | |||
| evt.preventDefault(); | |||
| this.pressed[name] = evt.type === 'keydown'; | |||
| } | |||
| init() { | |||
| window.addEventListener("keydown", e => this.onkey(e)); | |||
| window.addEventListener("keyup", e => this.onkey(e)); | |||
| } | |||
| update(dt) { | |||
| if (this.pressed.left) | |||
| this.entity.velocity.x -= this.speed * dt; | |||
| if (this.pressed.right) | |||
| this.entity.velocity.x += this.speed * dt; | |||
| } | |||
| } | |||
| @@ -0,0 +1,19 @@ | |||
| { | |||
| "name": "smartgame", | |||
| "version": "1.0.0", | |||
| "description": "", | |||
| "main": "index.js", | |||
| "scripts": { | |||
| "test": "echo \"Error: no test specified\" && exit 1", | |||
| "build-dbg": "browserify js/main.js -t [ babelify --sourceMap ] --debug --outfile public/bundle.js", | |||
| "build-prod": "browserify js/main.js -t [ babelify --sourceMap ] --outfile public/bundle.js" | |||
| }, | |||
| "author": "", | |||
| "license": "ISC", | |||
| "devDependencies": { | |||
| "babel-core": "^6.26.0", | |||
| "babel-preset-env": "^1.6.1", | |||
| "babelify": "^8.0.0", | |||
| "browserify": "^14.5.0" | |||
| } | |||
| } | |||
| @@ -0,0 +1,20 @@ | |||
| <!DOCTYPE html> | |||
| <html> | |||
| <head> | |||
| <meta charset="utf-8"> | |||
| <title>Game</title> | |||
| <style> | |||
| body { | |||
| margin: 0; | |||
| } | |||
| canvas { | |||
| display: block; | |||
| } | |||
| </style> | |||
| </head> | |||
| <body> | |||
| <canvas id="canvas"></canvas> | |||
| <script src="bundle.js"></script> | |||
| </body> | |||
| </html> | |||