リズムゲームは、音楽に合わせてタイミングよくボタンを押すゲームです。このチュートリアルでは、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%の位置に判定ラインを置くことを意味します。判定の範囲は距離(ピクセル)で定義し、判定ラインから近いほど高得点です。この数値を調整することで、ゲームの難易度を変えられます。
譜面(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で実際の音楽を再生したり、長押しノーツ(ロングノート)を追加したり、譜面エディターを作ったりしてみましょう。エフェクトやパーティクルを追加すると、見た目もぐっとかっこよくなりますよ。
A: はい、大丈夫です。このチュートリアルではステップごとにコードを書いていくので、初めての方でも順番に進めれば完成できます。わからないところがあれば、ひなテックの教室で質問もできますよ。
A: 基本部分は約30分〜1時間で作れます。見た目をこだわったり機能を追加すると、さらに楽しく発展させられます。