👾
HTML + Canvas + TypeScript で作成するインベーダーゲーム
TypeScriptで作るインベーダーゲーム
概要
TypeScriptを使用してシンプルなインベーダーゲームを作成しました。プレイヤーは矢印キーで左右に移動し、スペースキーで弾を発射します。敵を倒すとスコアが加算され、すべての敵を倒すと次のウェーブへ進みます。
それぞれの役割
index.html
)
1. HTML (-
<canvas>
要素を定義し、ゲームの描画領域を作成。 -
game.js
(コンパイルされた TypeScript)を読み込んでゲームを動作させる。
canvas.getContext("2d")
)
2. Canvas API (-
canvas
要素を取得し、2D 描画用のコンテキストを使用。 -
fillText()
やfillRect()
などの関数を使って、プレイヤーや敵、弾を描画。
game.ts
)
3. TypeScript (-
Player
、Enemy
、Bullet
などのクラスを定義し、オブジェクト指向で管理。 - イベントリスナーでプレイヤーの操作を処理。
-
requestAnimationFrame()
を使ってゲームループを実装。
初期セットアップ
$ pnpm install -g typescript
コード解説
html.index
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>TypeScript Space Invaders</title>
<link
rel="icon"
type="image/vnd.microsoft.ico"
href="assets/image/favicon.ico"
/>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<canvas id="gameCanvas"></canvas>
<script src="game.js"></script>
</body>
</html>
game.ts
1. キャンバスの設定
const canvas = document.getElementById("gameCanvas");
const ctx = canvas.getContext("2d");
canvas.width = 800;
canvas.height = 600;
canvas
要素を取得し、描画コンテキストを取得します。このctx
を使ってゲーム画面の描画を行います。
2. ゲームの状態管理
let gameOver = false;
let moveLeft = false;
let moveRight = false;
let player;
let enemies = [];
let score = 0;
let wave = 1;
let previousEnemyCount = 3;
let spawningNewWave = false;
-
gameOver
: ゲームオーバー状態かどうかを管理するフラグ。 -
moveLeft
,moveRight
: プレイヤーが移動しているかどうかを判定するフラグ。 -
player
: プレイヤーオブジェクト。 -
enemies
: 敵の配列。 -
score
: 現在のスコア。 -
wave
: 現在のウェーブ。 -
previousEnemyCount
: 前のウェーブで登場した敵の数。 -
spawningNewWave
: 新しいウェーブの敵を生成中かどうかを管理するフラグ。
3. プレイヤークラス
class Player {
constructor() {
this.speed = 5;
this.bullets = [];
this.x = canvas.width / 2;
this.y = canvas.height - 40;
}
move() {
if (moveLeft) this.x -= this.speed;
if (moveRight) this.x += this.speed;
this.x = Math.max(0, Math.min(canvas.width - 40, this.x));
}
shoot() {
this.bullets.push(new Bullet(this.x, this.y - 10));
}
update() {
this.move();
this.bullets = this.bullets.filter(bullet => bullet.y > 0);
this.bullets.forEach(bullet => bullet.update());
}
draw() {
ctx.fillText("🚀", this.x, this.y);
this.bullets.forEach(bullet => bullet.draw());
}
}
-
move()
: 左右の移動を処理し、画面外に出ないよう制限を加えます。 -
shoot()
: 弾を発射し、bullets
配列に追加します。 -
update()
: プレイヤーの移動と弾の更新を処理します。 -
draw()
: プレイヤーの表示と発射した弾の描画を行います。
4. 弾クラス
class Bullet {
constructor(x, y) {
this.speed = 5;
this.width = 5;
this.height = 10;
this.x = x - this.width / 2;
this.y = y;
}
update() {
this.y -= this.speed;
}
draw() {
ctx.fillStyle = "red";
ctx.fillRect(this.x, this.y, this.width, this.height);
}
}
-
update()
: 弾を上方向に移動させます。 -
draw()
: 弾の描画を行います。
5. 敵クラス
class Enemy {
constructor(x, y, speed) {
this.width = 40;
this.height = 30;
this.direction = 1;
this.bullets = [];
this.x = x;
this.y = y;
this.speed = speed;
}
update() {
this.x += this.speed * this.direction;
if (this.x < 0 || this.x + this.width > canvas.width) {
this.direction *= -1;
this.y += this.height;
}
if (Math.random() < 0.01) this.shoot();
this.bullets.forEach(bullet => bullet.update());
}
shoot() {
this.bullets.push(new EnemyBullet(this.x + this.width / 2, this.y + this.height));
}
draw() {
ctx.fillText("👾", this.x, this.y);
this.bullets.forEach(bullet => bullet.draw());
}
}
-
update()
: 敵の移動を制御し、ランダムに弾を発射します。 -
shoot()
: 弾を発射し、bullets
配列に追加します。 -
draw()
: 敵と弾の描画を行います。
6. ゲームの進行
function spawnEnemies() {
enemies = [];
let startY = 50;
let enemySpeed = 1 + wave * 0.2;
for (let i = 0; i < previousEnemyCount; i++) {
let x = (i % 6) * 80 + 50;
let y = startY + Math.floor(i / 6) * 40;
enemies.push(new Enemy(x, y, enemySpeed));
}
}
-
spawnEnemies()
: ウェーブごとに敵を生成します。
7. ゲームループ
function gameLoop() {
if (gameOver) {
ctx.fillText("GAME OVER", canvas.width / 2, canvas.height / 2);
return;
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
player.update();
player.draw();
enemies.forEach(enemy => enemy.update());
enemies.forEach(enemy => enemy.draw());
requestAnimationFrame(gameLoop);
}
-
gameLoop()
: 画面を更新し、各要素の描画と状態更新を行います。
コンパイル
$ tsc
まとめ
TypeScriptを使って基本的なインベーダーゲームを作成しました。
レベルの調整がとても難しいです。
ゲーム制作者には頭が下がります。
Discussion