Created
February 19, 2013 16:51
-
-
Save takatama/4987660 to your computer and use it in GitHub Desktop.
TypeScript入門 シューティングゲーム編
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
TypeScriptを使ってシューティングゲームを作ってみた。 | |
敵に当たるとゲームオーバー。 | |
<操作方法> | |
・左右キーで移動。スペースキーで発射。 | |
<開発メモ> | |
・当たり判定にVisitorパターンを使っている。 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body { background-color: #DDDDDD; font: 30px sans-serif; } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<canvas id="stage" width="400" height="400"></canvas> | |
<br> | |
<input type="button" id="stop" value="stop"> | |
<input type="button" id="restart" value="restart"> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// forked from takatama's "2013-02-04 1st" http://jsdo.it/takatama/uoPT | |
interface Entity { | |
x: number; | |
y: number; | |
radius: number; | |
isHit(entity: Entity): bool; | |
draw(context: CanvasRenderingContext2D): void; | |
move(): void; | |
destroy(): void; | |
accept(visitor: Visitor): void; | |
} | |
class BaseEntity implements Entity { | |
constructor(public stage: Stage, public x: number, public y: number, public radius: number, public dx: number, public dy: number) { | |
} | |
isHit(entity: Entity): bool { | |
var distance = Math.sqrt(Math.pow((this.x - entity.x), 2) + Math.pow((this.y - entity.y), 2)); | |
return (distance < this.radius + entity.radius); | |
} | |
draw(context): void { | |
context.beginPath(); | |
context.arc(this.x, this.y, this.radius, 0, Math.PI*2, false); | |
context.stroke(); | |
} | |
move(): void { | |
this.x += this.dx; | |
if (this.x < 0) { | |
this.x = this.stage.getWidth(); | |
} else if (this.x > this.stage.getWidth()) { | |
this.x = 0; | |
} | |
this.y += this.dy; | |
if (this. y < 0) { | |
this.y = this.stage.getHeight(); | |
} else if (this.y > this.stage.getHeight()) { | |
this.y = 0; | |
} | |
} | |
destroy(): void {} | |
accept(visitor: Visitor): void {} | |
} | |
class MyBall extends BaseEntity { | |
move(): void { | |
this.x += this.dx; | |
if (this.x < 0) { | |
this.x = this.stage.getWidth(); | |
} else if (this.x > this.stage.getWidth()) { | |
this.x = 0; | |
} | |
this.y += this.dy; | |
if (this. y < 0 || this.y > this.stage.getHeight()) { | |
this.stage.removeMyBall(this); | |
} | |
} | |
destroy(): void { | |
this.stage.removeMyBall(this); | |
} | |
} | |
class MyFighter extends BaseEntity { | |
destroy(): void { | |
this.stage.removeMyFighter(this); | |
} | |
fire(): void { | |
this.stage.addMyBall(new MyBall(this.stage, this.x, this.y, 2, 0, -15)); | |
} | |
turnLeft(): void { | |
this.dx = -10; | |
} | |
turnRight(): void { | |
this.dx = 10; | |
} | |
} | |
class EnemyFighter extends BaseEntity { | |
move(): void { | |
this.x += this.dx; | |
if (this.x < 0) { | |
this.x = this.stage.getWidth(); | |
} else if (this.x > this.stage.getWidth()) { | |
this.x = 0; | |
} | |
this.y += this.dy; | |
if (this. y > this.stage.getHeight()) { | |
this.stage.removeEnemy(this); | |
} | |
} | |
destroy(): void { | |
this.stage.removeEnemy(this); | |
} | |
accept(visitor: Visitor): void { | |
visitor.visitEnemy(this); | |
} | |
} | |
interface Visitor { | |
visitEnemy(enemy: Entity): void; | |
} | |
class FighterVisitor implements Visitor { | |
constructor(private stage: Stage, private fighter: Entity) { | |
} | |
visitEnemy(enemy: Entity): void { | |
if (this.fighter) { | |
if (this.fighter.isHit(enemy)) { | |
alert('game over'); | |
this.stage.restart(); | |
} | |
} | |
} | |
} | |
class BallVisitor implements Visitor { | |
constructor(private stage: Stage, private ball: Entity) { | |
} | |
visitEnemy(enemy: Entity): void { | |
if (this.ball.isHit(enemy)) { | |
enemy.destroy(); | |
} | |
} | |
} | |
class KeyController { | |
constructor(private fighter: MyFighter) { | |
var space = 32, left = 37, up = 38, right = 39, down = 40; | |
document.onkeydown = function (event) { | |
var chCode = (event.keyCode) ? event.keyCode : event.charCode; | |
switch(chCode) { | |
case space: | |
fighter.fire(); | |
break; | |
case left: | |
fighter.turnLeft(); | |
break; | |
case right: | |
fighter.turnRight(); | |
break; | |
} | |
} | |
} | |
} | |
class Stage { | |
private myFighter: Entity; | |
private myBalls: Entity[] = []; | |
private enemies: Entity[] = []; | |
private context: CanvasRenderingContext2D; | |
private width: number; | |
private height: number; | |
private time: number = 0; | |
private timer: number; | |
constructor(private canvas: HTMLCanvasElement) { | |
this.context = canvas.getContext('2d'); | |
this.width = canvas.width; | |
this.height = canvas.height; | |
} | |
restart(): void { | |
this.stop(); | |
this.myBalls = []; | |
this.enemies = []; | |
var that = this; | |
this.timer = setInterval(function () { | |
that.tick(); | |
}, 100); | |
} | |
stop(): void { | |
if (this.timer) { | |
clearTimeout(this.timer); | |
} | |
} | |
tick(): void { | |
this.time++; | |
this.createEnemy(); | |
this.moveAll(); | |
this.hittingAll(); | |
this.drawAll(); | |
} | |
createEnemy(): void { | |
if (this.time % 10 === 0) { | |
this.addEnemy(new EnemyFighter(this, Stage.random(this.width), 0, 15, 0, 3)); | |
} | |
} | |
static random(max: number): number { | |
return Math.floor(Math.random() * max); | |
} | |
moveAll(): void { | |
this.moveEntities(this.enemies); | |
this.moveEntities(this.myBalls); | |
if (this.myFighter) { | |
this.myFighter.move(); | |
} | |
} | |
moveEntities(entities: Entity[]): void { | |
var i, len; | |
for (i = 0, len = entities.length; i < len; i++) { | |
if (entities[i]) { | |
entities[i].move(); | |
} | |
} | |
} | |
hittingAll(): void { | |
this.processMyFighterHitting(); | |
this.processMyBallsHitting(); | |
} | |
processMyFighterHitting() { | |
this.visitEnemies(new FighterVisitor(this, this.myFighter)); | |
} | |
visitEnemies(visitor: Visitor): void { | |
var i, len | |
for (i = 0, len = this.enemies.length; i < len; i++) { | |
if (this.enemies[i]) { | |
this.enemies[i].accept(visitor); | |
} | |
} | |
} | |
processMyBallsHitting() { | |
var i, len; | |
for (i = 0, len = this.myBalls.length; i < len; i++) { | |
if (this.myBalls[i]) { | |
this.visitEnemies(new BallVisitor(this, this.myBalls[i])); | |
} | |
} | |
} | |
drawAll(): void { | |
this.drawFrame(); | |
this.drawEntities(this.enemies); | |
this.drawEntities(this.myBalls); | |
if (this.myFighter) { | |
this.myFighter.draw(this.context); | |
} | |
} | |
drawFrame(): void { | |
this.context.clearRect(0, 0, this.width, this.height); | |
this.context.strokeRect(0, 0, this.width, this.height); | |
} | |
drawEntities(entities: Entity[]): void { | |
var i, j, len = entities.length; | |
for (i = 0; i < len; i++) { | |
if (entities[i]) { | |
entities[i].draw(this.context); | |
} | |
} | |
} | |
setMyFighter(fighter: MyFighter): void { | |
this.myFighter = fighter; | |
new KeyController(fighter); | |
} | |
addEnemy(enemy: Entity): void { | |
this.add(this.enemies, enemy); | |
} | |
addMyBall(ball: MyBall): void { | |
this.add(this.myBalls, ball); | |
} | |
add(entities: Entity[], entity: Entity): void { | |
var i, len; | |
for (i = 0, len = entities.length; i < len; i++) { | |
if (entities[i] == null) { | |
entities[i] = entity; | |
return; | |
} | |
} | |
entities.push(entity); | |
} | |
removeMyFighter(fighter: Entity): void { | |
this.myFighter = null; | |
} | |
removeEnemy(enemy: Entity): void { | |
this.remove(this.enemies, enemy); | |
} | |
removeMyBall(ball: Entity): void { | |
this.remove(this.myBalls, ball); | |
} | |
remove(entities: Entity[], entity: Entity): void { | |
var i, len; | |
for (i = 0, len = entities.length; i < len; i++) { | |
if (entity === entities[i]) { | |
entities[i] = null; | |
return; | |
} | |
} | |
} | |
getWidth(): number { | |
return this.width; | |
} | |
getHeight(): number { | |
return this.height; | |
} | |
} | |
/* Main */ | |
var stage: Stage = new Stage(<HTMLCanvasElement>document.getElementById('stage')); | |
stage.setMyFighter(new MyFighter(stage, 150, 380, 10, 10, 0)); | |
document.getElementById('stop').onclick = function() { | |
stage.stop(); | |
}; | |
document.getElementById('restart').onclick = function() { | |
stage.restart(); | |
}; | |
stage.restart(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment