Skip to content

Instantly share code, notes, and snippets.

@rightfold
Created March 1, 2014 21:04
Show Gist options
  • Save rightfold/7bf37ec2b4c0c92d7f1b to your computer and use it in GitHub Desktop.
Save rightfold/7bf37ec2b4c0c92d7f1b to your computer and use it in GitHub Desktop.
function range(end: number) {
var result = [];
for (var i = 0; i < end; ++i) {
result.push(i);
}
return result;
}
enum Key {
MoveNorthern = 87,
MoveSouthern = 83,
MoveWestern = 65,
MoveEastern = 68,
}
class Character {
x: number;
y: number;
speed: number;
constructor(x: number, y: number, speed: number) {
this.x = x;
this.y = y;
this.speed = speed;
}
moveNorthern(dt: number, collisionArea: Area) {
this.move(0, -Math.round(dt * this.speed), collisionArea);
}
moveSouthern(dt: number, collisionArea: Area) {
this.move(0, Math.round(dt * this.speed), collisionArea);
}
moveWestern(dt: number, collisionArea: Area) {
this.move(-Math.round(dt * this.speed), 0, collisionArea);
}
moveEastern(dt: number, collisionArea: Area) {
this.move(Math.round(dt * this.speed), 0, collisionArea);
}
move(dx: number, dy: number, collisionArea: Area) {
var x = this.x + dx;
var y = this.y + dy;
if (!collisionArea.containsRectangle(x - 2, y - 2, 4, 4)) {
this.x = x;
this.y = y;
}
}
draw(context: CanvasRenderingContext2D) {
throw new Error('You must override draw(context: CanvasRenderingContext2D): void');
}
}
class PlayerCharacter extends Character {
constructor(x: number, y: number) {
super(x, y, 150);
}
draw(context: CanvasRenderingContext2D) {
context.fillStyle = '#f00';
context.fillRect(this.x - 2, this.y - 2, 4, 4);
}
}
class Zombie extends Character {
constructor(x: number, y: number) {
super(x, y, 100);
}
moveTowardsPoint(dt: number, x: number, y: number, collisionArea: Area) {
var angle = Math.atan2(this.x - x, this.y - y);
var relativeX = -Math.round(dt * this.speed * Math.sin(angle));
var relativeY = -Math.round(dt * this.speed * Math.cos(angle));
this.move(relativeX, relativeY, collisionArea);
}
draw(context: CanvasRenderingContext2D) {
context.fillStyle = '#0f0';
context.fillRect(this.x - 2, this.y - 2, 4, 4);
}
}
interface Area {
containsRectangle(x: number, y: number, width: number, height: number): boolean;
}
class NullArea implements Area {
containsRectangle(x: number, y: number, width: number, height: number) {
return false;
}
}
class RectangularArea implements Area {
private x: number;
private y: number;
private width: number;
private height: number;
constructor(x: number, y: number, width: number, height: number) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
containsRectangle(x: number, y: number, width: number, height: number) {
return x >= this.x && x + width <= this.x + this.width
&& y >= this.y && y + height <= this.y + this.height;
}
}
class AggregationArea implements Area {
private areas: Area[];
constructor(areas: Area[]) {
this.areas = areas;
}
containsRectangle(x: number, y: number, width: number, height: number) {
for (var i = 0; i < this.areas.length; ++i) {
if (this.areas[i].containsRectangle(x, y, width, height)) {
return true;
}
}
return false;
}
}
interface Scene {
step(dt: number, input: any): void;
draw(context: CanvasRenderingContext2D): void;
}
class MainScene implements Scene {
private player: Character;
private zombies: Zombie[];
constructor() {
this.player = new PlayerCharacter(20, 20);
this.zombies = range(20).map(() => {
var x = Math.round(Math.random() * 640);
var y = Math.round(Math.random() * 480);
return new Zombie(x, y);
});
}
step(dt: number, input: any) {
var playerCollisionArea = new NullArea();
if (input.keys[Key.MoveNorthern]) this.player.moveNorthern(dt, playerCollisionArea);
if (input.keys[Key.MoveSouthern]) this.player.moveSouthern(dt, playerCollisionArea);
if (input.keys[Key.MoveWestern]) this.player.moveWestern(dt, playerCollisionArea);
if (input.keys[Key.MoveEastern]) this.player.moveEastern(dt, playerCollisionArea);
this.zombies.forEach(zombie => {
var zombieCollisionAreas: Area[] = [];
this.zombies.forEach(otherZombie => {
if (otherZombie !== zombie) {
var zombieCollisionArea = new RectangularArea(otherZombie.x - 6, otherZombie.y - 6, 10, 10);
zombieCollisionAreas.push(zombieCollisionArea);
}
});
var zombieCollisionArea = new AggregationArea(zombieCollisionAreas);
zombie.moveTowardsPoint(dt, this.player.x, this.player.y, zombieCollisionArea)
});
}
draw(context: CanvasRenderingContext2D) {
this.player.draw(context);
this.zombies.forEach(zombie => zombie.draw(context));
}
}
class Game {
scene: Scene;
constructor() {
this.scene = new MainScene();
}
step(dt: number, input: any) {
this.scene.step(dt, input);
}
draw(context: CanvasRenderingContext2D) {
this.scene.draw(context);
}
}
window.addEventListener('load', function() {
var canvas = <HTMLCanvasElement>document.getElementById('canvas');
var context = canvas.getContext('2d');
var input = { keys: { } };
document.addEventListener('keydown', function(ev: Event) {
input.keys[(<any>ev).keyCode] = true;
});
document.addEventListener('keyup', function(ev: Event) {
input.keys[(<any>ev).keyCode] = false;
});
var game = new Game();
var lastTime = 0.0;
(function render(time) {
var dt = (time - lastTime) / 1000;
lastTime = time;
canvas.width = canvas.width;
game.step(dt, input);
game.draw(context);
window.requestAnimationFrame(render);
})(lastTime);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment