リズムゲームの作り方 — JavaScriptでブラウザゲームを作ろう

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

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

リズムゲームは、音楽に合わせてタイミングよくボタンを押すゲームです。このチュートリアルでは、JavaScriptとCanvasを使ってリズムゲームを作る方法を学びましょう。譜面データの管理、タイミング判定の仕組み、ノーツのアニメーションなど、タイミングベースのゲーム開発に必要な技術を身につけられます。

レーンとノーツの基本構造を準備しよう

リズムゲームの画面は複数の「レーン」に分かれていて、ノーツ(音符)が上から下に流れてきます。まず、レーンの数やノーツの速度、判定の範囲といった定数を定義しましょう。

var LANE_COUNT = 4;
var JUDGE_Y_RATIO = 0.82;
var NOTE_SPEED = 3;
var PERFECT_RANGE = 20;
var GREAT_RANGE = 40;
var GOOD_RANGE = 65;
var MISS_RANGE = 90;

var SCORE_PERFECT = 300;
var SCORE_GREAT = 200;
var SCORE_GOOD = 100;

var LANE_COLORS = ['#E74C3C', '#3498DB', '#2ECC71', '#F1C40F'];
var LANE_KEYS = ['KeyD', 'KeyF', 'KeyJ', 'KeyK'];

4つのレーンにそれぞれ色とキーを割り当てています。JUDGE_Y_RATIOは画面の82%の位置に判定ラインを置くことを意味します。判定の範囲は距離(ピクセル)で定義し、判定ラインから近いほど高得点です。この数値を調整することで、ゲームの難易度を変えられます。

譜面データの管理とノーツ生成を作ろう

BPMベースの自動譜面生成

譜面(chart)はノーツの出現タイミングとレーン番号のリストです。BPM(テンポ)から1拍の時間を計算し、パターンベースで譜面を自動生成しましょう。

var BPM = 130;
var BEAT_MS = 60000 / BPM;

function generateChart() {
  chart = [];
  var totalBeats = 128;
  var patterns = [
    [[0], [], [2], []],         // 基本パターン
    [[0], [1], [2], [3]],       // 階段
    [[0, 2], [], [1, 3], []],   // 同時押し
    [[3], [2], [1], [0]],       // 逆階段
  ];

  var beat = 4; // イントロ休み
  while (beat < totalBeats - 4) {
    var difficulty = Math.min(beat / totalBeats, 1);
    var pat = patterns[Math.floor(Math.random() * patterns.length)];

    for (var i = 0; i < pat.length; i++) {
      var lanes = pat[i];
      for (var j = 0; j < lanes.length; j++) {
        chart.push({ time: beat * BEAT_MS, lane: lanes[j] });
      }
      beat += 1;
    }
  }
  chart.sort(function (a, b) { return a.time - b.time; });
}

BEAT_MSでBPM130なら1拍あたり約461ミリ秒と計算できます。パターン配列の各要素が1拍分に対応し、空配列[]は休符を表します。曲の進行に合わせてdifficultyを上げていくことで、後半ほど複雑なパターンが出やすくなります。

タイミング判定とコンボシステムを実装しよう

プレイヤーがキーを押したとき、そのレーンにある最も近いノーツとの距離で判定を行います。距離が近いほど高評価、遠いとMISS。連続成功でコンボが増え、ボーナスポイントが加算されるしくみです。

function hitLane(lane) {
  if (!running) return;
  var judgeY = H * JUDGE_Y_RATIO;

  // 最も近いノーツを探す
  var closest = null;
  var closestDist = Infinity;
  for (var i = 0; i < notes.length; i++) {
    var n = notes[i];
    if (n.lane !== lane || n.hit) continue;
    var dist = Math.abs(n.y - judgeY);
    if (dist < closestDist) {
      closestDist = dist;
      closest = n;
    }
  }
  if (!closest) return;

  if (closestDist <= PERFECT_RANGE) {
    points = SCORE_PERFECT; combo++;
  } else if (closestDist <= GREAT_RANGE) {
    points = SCORE_GREAT; combo++;
  } else if (closestDist <= GOOD_RANGE) {
    points = SCORE_GOOD; combo++;
  } else if (closestDist <= MISS_RANGE) {
    points = 0; combo = 0;
  }
  // コンボボーナス
  points += Math.floor(combo * 5);
  score += points;
  closest.hit = true;
}

判定ラインとノーツのY座標の差(距離)で判定を分岐しています。コンボが続くほどボーナスが増えるので、連続でPERFECTを出すのが高得点のカギになります。ノーツにhit = trueをセットして二重判定を防いでいるのもポイントです。

まとめ — 次のステップ

このチュートリアルでは、レーン構造の設計、BPMベースの譜面生成、タイミング判定とコンボシステムの3つを学びました。発展として、Web Audio APIで実際の音楽を再生したり、長押しノーツ(ロングノート)を追加したり、譜面エディターを作ったりしてみましょう。エフェクトやパーティクルを追加すると、見た目もぐっとかっこよくなりますよ。

よくある質問

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

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

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

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