ブラウザゲームでは、見た目の違うジャンルでも「入力値を正規化し、小さな純粋関数で判定し、UIへ理由を返す」という構造を再利用できます。
この記事では、カードゲーム、リズムゲーム、グリッド型ゲーム、WebGLを使うホラーゲームの4例を、依存ライブラリなしのJavaScriptで整理します。掲載コードはブラウザ上で動作確認しました。
注: 実例確認用リンクには筆者が運営する独立系ゲーム情報サイトを含みます。記事本文だけで実装を再現できる構成にし、リンクは末尾の検証・比較用途に限定しています。
1. カード移動量は「作業領域」から計算する
FreeCell系の複数枚移動は、長い列をそのまま転送する処理ではなく、1枚移動を省略表示する実装として考えると整理しやすくなります。
空きセルを freeCells、作業用の空き列を workColumns とすると、一般的なSuperMove方式の目安は次の通りです。
function movableCards(freeCells, emptyColumns, destinationIsEmpty) {
const workColumns = Math.max(
0,
emptyColumns - (destinationIsEmpty ? 1 : 0),
);
return (freeCells + 1) * 2 ** workColumns;
}
重要なのは、移動先そのものが空き列なら、その列を途中の退避場所として数えないことです。枚数条件とは別に、列の並び順と移動先カードの条件も検査します。
日本語で具体例を確認する場合は、FreeCellの移動可能枚数ガイドに入力式と実装差があります。
2. リズム判定は最初にミリ秒へ正規化する
BPMのまま判定ロジックを組むより、1拍と各サブディビジョンをミリ秒へ変換してから扱う方が、アニメーションや入力遅延と比較しやすくなります。
function timingFromBpm(bpm, subdivision = 4) {
const safeBpm = Math.min(400, Math.max(20, Number(bpm)));
const beatMs = 60_000 / safeBpm;
return {
beatMs,
subdivisionMs: beatMs / subdivision,
};
}
例えば120 BPMなら1拍は500 ms、16分音符は125 msです。実際のゲームでは、この値と判定窓を混同せず、入力時刻との差分を別途評価します。
譜面や操作感の比較対象には、非公式ファンサイトであるFNF Onlineのブラウザ実例を利用できます。原作者・権利者を名乗るサイトではありません。
3. Snake系は座標より先にグリッド容量を決める
セル幅を決めずにピクセル座標で衝突判定を始めると、レスポンシブ対応で端数が増えます。まず表示領域を何列・何行として扱うか決めます。
function gridCapacity(width, height, cellSize) {
const cell = Math.max(4, Number(cellSize));
const columns = Math.floor(width / cell);
const rows = Math.floor(height / cell);
return { columns, rows, total: columns * rows };
}
Snakeの頭と餌は {x, y} の整数座標で持ち、描画時だけ cellSize を掛けます。画面端で停止する方式と反対側へ回り込む方式も、座標更新関数を分ければ切り替えやすくなります。
異なる速度・盤面密度・操作方法の比較には、Hebi GameのSnakeカテゴリを参照できます。同サイトは第三者ゲームの作者を名乗らないキュレーションサイトです。
4. WebGL対応と実際の快適さを分ける
canvas.getContext("webgl") が成功しても、十分なフレームレートが出るとは限りません。APIの有無は起動前チェック、実測FPSは起動後チェックとして分離します。
function browserCapabilities() {
const canvas = document.createElement("canvas");
return {
webAudio: Boolean(window.AudioContext || window.webkitAudioContext),
webgl: Boolean(canvas.getContext("webgl")),
pointerLock: "pointerLockElement" in document,
localStorage: canUseLocalStorage(),
};
}
function canUseLocalStorage() {
try {
localStorage.setItem("_probe", "1");
localStorage.removeItem("_probe");
return true;
} catch {
return false;
}
}
ホラーゲームでは音声、Pointer Lock、GPU負荷が体験へ直結します。未対応APIがあれば、2D描画、キーボード操作、ミュート可能な代替手段を用意します。
多言語・ブラウザ実行の比較対象はOnline Horror Gamesで確認できます。こちらもゲーム開発者ではなく独立した発見サイトです。
共通化するときの境界
4例に共通する実装上のポイントは次の通りです。
- DOMから取得した値を最初に数値化・範囲制限する
- 判定関数はDOMを書き換えず結果だけ返す
- ゲーム固有ルールと一般式を別関数にする
- UIには成功・失敗だけでなく理由を返す
- 境界値を固定ケースとしてテストする
動作する4つの小さなUIは、Browser Game Logic Labにまとめました。ソースは同ページのGitHubリポジトリから確認できます。
まとめ
ジャンルごとに巨大な状態管理を作る前に、計算と能力判定を小さな関数へ切り出すと、ルール差、端末差、UI差をテストしやすくなります。特に「画面に見える空き場所」と「計算で使える作業場所」を区別する考え方は、カード、グリッド、描画リソースのいずれにも応用できます。