タワーディフェンスの作り方 — JavaScriptでブラウザゲームを作ろう

このゲームで遊ぶ → ソースコード全文 →

このチュートリアルで学べること

タワーディフェンスは、決められた経路を進む敵を、設置したタワーで迎え撃つ戦略ゲームです。マップ上の経路に沿って敵を移動させる仕組み、タワーが射程内の敵を自動で攻撃するAI、ウェーブごとに強くなる敵の管理など、ゲームプログラミングの面白いテクニックがたくさん登場します。

HTMLとCanvasの準備をしよう

タワーディフェンスはCanvasを使ってマップ、タワー、敵、弾をリアルタイムに描画します。requestAnimationFrameでゲームループを回し、毎フレーム更新と描画を行いましょう。

var canvas = document.getElementById('game-canvas');
var ctx = canvas.getContext('2d');
var canvasW, canvasH, cellW, cellH;

function resizeCanvas() {
  var wrapper = canvas.parentElement;
  var rect = wrapper.getBoundingClientRect();
  var dpr = window.devicePixelRatio || 1;
  canvasW = rect.width;
  canvasH = rect.height;
  cellW = canvasW / MAP_COLS;
  cellH = canvasH / MAP_ROWS;
  canvas.width = canvasW * dpr;
  canvas.height = canvasH * dpr;
  ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
}

function gameLoop() {
  update();  // ゲームの状態を更新
  draw();    // 画面を描画
  if (gameRunning) {
    animFrameId = requestAnimationFrame(gameLoop);
  }
}

マップの列数・行数からセルの幅と高さを計算します。gameLoop関数が毎フレーム呼ばれて、更新と描画を繰り返します。

マップと敵の経路を作ろう

2次元配列でマップを定義する

マップは2次元配列で定義します。0が空地(タワーを置ける場所)、1が道(敵が通る場所)、2がスタート、3がゴールです。敵が通る経路は、座標の配列として事前に定義しておきます。

var MAP_COLS = 12;
var MAP_ROWS = 9;
var MAP = [
  [0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0],
  [0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0],
  [0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
  [0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0],
  [0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0],
  [0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0],
  [0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0]
];

// 敵の通る経路(座標のリスト)
var PATH = [
  {r:0,c:2}, {r:1,c:2}, {r:2,c:2}, {r:3,c:2},
  {r:3,c:3}, {r:3,c:4}, {r:3,c:5}, {r:2,c:5},
  // ... 続く
  {r:8,c:2}  // ゴール
];

敵はPATH配列のポイントを1つずつたどって移動します。現在の位置と次のポイントの方向を計算して、滑らかに動かしましょう。

敵の移動処理

function createEnemy() {
  var hp = ENEMY_BASE_HP + wave * 1.5;
  var speed = ENEMY_BASE_SPEED + wave * 0.001;
  var start = cellCenter(PATH[0].r, PATH[0].c);
  return {
    x: start.x, y: start.y,
    pathIndex: 0, hp: hp, maxHp: hp,
    speed: speed, baseSpeed: speed
  };
}

// 更新ループ内で敵を移動
var target = cellCenter(PATH[e.pathIndex + 1].r, PATH[e.pathIndex + 1].c);
var dx = target.x - e.x;
var dy = target.y - e.y;
var d = Math.sqrt(dx * dx + dy * dy);
if (d < 2) {
  e.pathIndex++;
} else {
  e.x += (dx / d) * e.speed * cellW;
  e.y += (dy / d) * e.speed * cellW;
}

次のポイントまでの距離と方向を計算し、速度に応じて少しずつ近づけます。ポイントに十分近づいたら次のポイントに切り替えます。

タワーAIと攻撃処理を実装しよう

タワーは射程内に敵が入ると自動で攻撃します。複数の敵がいる場合は、経路を最も進んでいる敵(ゴールに近い敵)を優先的に狙いましょう。タワーの種類ごとに射程、ダメージ、攻撃速度が異なります。

var TOWER_TYPES = {
  normal: { cost: 20, range: 2.2, damage: 1,
            fireRate: 50, color: '#3498DB', name: 'ノーマル' },
  fast:   { cost: 35, range: 1.8, damage: 0.6,
            fireRate: 18, color: '#2ECC71', name: 'はやい' },
  strong: { cost: 50, range: 2.5, damage: 3.5,
            fireRate: 90, color: '#E74C3C', name: 'つよい' }
};

// タワーの攻撃処理(毎フレーム実行)
for (var t = 0; t < towers.length; t++) {
  var tower = towers[t];
  tower.cooldown--;

  // 射程内で最も進んだ敵を探す
  var bestTarget = null;
  var bestProgress = -1;
  for (var ei = 0; ei < enemies.length; ei++) {
    var en = enemies[ei];
    var d = dist(tower.x, tower.y, en.x, en.y);
    if (d <= tower.range && en.pathIndex > bestProgress) {
      bestProgress = en.pathIndex;
      bestTarget = en;
    }
  }

  if (bestTarget && tower.cooldown <= 0) {
    tower.cooldown = tower.fireRate;
    bullets.push({
      x: tower.x, y: tower.y,
      target: bestTarget,
      damage: tower.damage,
      speed: 5
    });
  }
}

cooldownで攻撃の間隔を管理します。pathIndexが大きい敵ほどゴールに近いので優先して攻撃します。弾は毎フレーム敵を追いかけ、当たったらダメージを与えます。

ウェーブ管理を実装しよう

敵は「ウェーブ」(波)ごとにまとまって出現します。ウェーブが進むにつれて敵の数とHPが増え、難しくなっていきます。5ウェーブごとにボス敵を出すと、より戦略性が増します。

function startWave() {
  waveInProgress = true;
  enemiesToSpawn = ENEMIES_PER_WAVE_BASE + Math.floor(wave * 1.5);
  spawnTimer = 0;
}

// 更新ループ内でウェーブ完了をチェック
if (waveInProgress && enemiesToSpawn === 0 && enemies.length === 0) {
  waveInProgress = false;
  wave++;
  waveDelay = 90; // 次のウェーブまで少し待つ
}

// ボス敵の生成
var isBoss = (wave % 5 === 0) && enemiesToSpawn === 1;
if (isBoss) {
  hp *= 5;     // HPが5倍
  speed *= 0.6; // 移動が遅い
}

現在のウェーブの全敵を倒すと、少しの休憩を挟んで次のウェーブが始まります。wave * 1.5で敵の数を段階的に増やし、5ウェーブごとのボスがゲームにメリハリをつけます。

まとめ — 次のステップ

このチュートリアルでは、マップと経路の定義、敵の経路追従、タワーAIの攻撃ロジック、ウェーブ管理の仕組みを学びました。さらに発展させるなら、タワーのアップグレード機能、新しいマップの追加、スプラッシュダメージ(範囲攻撃)、スロウ効果(敵の減速)、マップエディタなどに挑戦してみましょう。

よくある質問

Q: プログラミング初心者でも作れますか?

A: はい、大丈夫です。このチュートリアルではステップごとにコードを書いていくので、初めての方でも順番に進めれば完成できます。わからないところがあれば、ひなテックの教室で質問もできますよ。

Q: どのくらい時間がかかりますか?

A: 基本部分は約30分〜1時間で作れます。見た目をこだわったり機能を追加すると、さらに楽しく発展させられます。