「NEON VELOCITY」は、左右へレーンを移るだけの60秒レースです。アクセルもブレーキもありません。それでも、遊んだ人には「自分で速度を上げた」「ぎりぎりで危険を抜けた」と感じてほしい。そこで私たちは、速さを単なる背景スクロールではなく、判断が連続した結果として設計しました。

操作を減らすと、ゲームが薄くなるのではありません。ひとつの判断に返ってくる反応を増やせます。

この記事では、3レーンという小さな空間をどう状態として表し、入力をどう安全に受け取り、60秒の難易度をどう組み立てたかを、ゲームを作ったことがない人にも追える順番で説明します。後半では、当たり判定、スポーン間隔、乱数の再現性といった少し専門的な話にも踏み込みます。

まず「面白さ」を一文に固定する

最初に決めた遊びの核は、「次に安全なレーンを読み、短い猶予の中で一手だけ選ぶ」です。車を自由な座標で動かす方式も試せますが、スマートフォンでは指が画面を隠し、入力精度の差がそのまま失敗につながります。そこで車の位置を左・中央・右の3値に限定しました。

自由度を減らした代わりに、プレイヤーが見る情報は明確になります。前方に障害物が現れたとき、考えることは「どれだけハンドルを切るか」ではなく、「左か、待つか、右か」です。選択肢がすぐ理解できるので、初回プレイの数秒後には駆け引きへ入れます。

一方、内部では道路、障害物、ゲート、ニトロ、コンボを別々の規則として扱います。画面は派手でも、中心にある問いは毎回ひとつ。この見た目の豊かさと判断の単純さの分離が、短いゲームを繰り返したくなる土台です。

3レーンを座標ではなく状態として持つ

車の位置を毎フレームの x 座標だけで管理すると、「移動途中にもう一度押された」「画面幅が変わった」といった条件が重なったときに挙動を追いにくくなります。NEON VELOCITYでは、真実となる値を lane に置き、描画用の座標はそこから計算します。

type Lane = 0 | 1 | 2;
type Phase = "ready" | "playing" | "paused" | "result";
 
type RaceState = {
  phase: Phase;
  lane: Lane;
  elapsedMs: number;
  distance: number;
  combo: number;
  speedLevel: number;
};
 
// 画面幅が変わっても、論理上のレーンは変えません。
const laneToX = (lane: Lane, roadWidth: number) =>
  roadWidth * ([0.24, 0.5, 0.76][lane] ?? 0.5);

この形なら、ポーズ中に時間だけ進む事故や、結果画面で入力が効く事故も phase の確認で防げます。状態機械、英語では finite state machine と呼ばれる考え方です。難しい名前ですが、「今できることを場面ごとに限定する」だけだと考えると理解しやすくなります。

3つのレーンを移動する車と安全・危険領域の図

左・中央・右を離散状態として扱い、表示上の滑らかな移動と当たり判定を分離します。

キーボードとタップを同じ「意図」へ変換する

PCでは矢印キーやA・D、スマートフォンでは画面左右のタップを使います。入力装置ごとにゲーム処理を書くのではなく、どの入力も move(-1) または move(1) という意図へ変換しました。こうすると、テストは「右へ動く意図を送ったらレーンが1増えるか」だけを確かめればよくなります。

const move = (direction: -1 | 1) => {
  if (state.phase !== "playing") return;
 
  // 範囲外へ出ないように、0〜2へ丸めます。
  const next = Math.max(0, Math.min(2, state.lane + direction));
  state.lane = next as Lane;
  renderCarTarget();
};
 
window.addEventListener("keydown", (event) => {
  if (event.repeat) return;
  if (event.key === "ArrowLeft" || event.key.toLowerCase() === "a") move(-1);
  if (event.key === "ArrowRight" || event.key.toLowerCase() === "d") move(1);
});

タップ領域は、見えている矢印だけでなく十分な面積を持たせます。また pointerdown を使えば、マウス、ペン、タッチを同じ経路で処理できます。ただし、ページのスクロールを一律に止めるのは避けます。ゲーム面の中で操作が始まった場合だけ既定動作を抑え、記事やナビゲーションは普通にスクロールできるようにしました。

当たり判定は「正確さ」より納得感を優先する

画像の車体全体を判定矩形にすると、端の透明な部分やエフェクトまで衝突したように感じられます。そこで、プレイヤー車の当たり領域を見た目より少し細くし、障害物側にも小さな余白を設けました。数学的には完全に正確ではありませんが、「避けたように見えたのに当たった」という強い不満を減らせます。

衝突した瞬間には、結果を複数の感覚へ同時に返します。車を短く明滅させ、速度を一段下げ、低い効果音を鳴らし、コンボをゼロへ戻します。反対にゲートを通過したときは、シアンの輪、明るい音、距離の加算を同じタイミングへそろえます。このような反応は「juice」と呼ばれることがありますが、大切なのは量より因果関係です。何が起きたかを100ミリ秒ほどで理解できる順序にします。

動きを苦手とする人のために prefers-reduced-motion も尊重します。設定が有効ならカメラの揺れや長い残像を減らし、色の変化と数値更新は残します。演出を消しても、成否の情報が失われないことが条件です。

60秒をひとつの難易度曲線にする

開始直後から最高速にすると、初見の人はルールを学ぶ前に失敗します。逆に最後まで同じ間隔なら、慣れた瞬間に作業へ変わります。そこで経過率 t を0から1で表し、障害物の発生間隔と移動速度を少しずつ変えます。

// t は開始時0、終了時1です。
const difficultyAt = (t: number) => {
  const eased = t * t * (3 - 2 * t);
  return {
    obstacleIntervalMs: 920 - eased * 410,
    worldSpeed: 0.82 + eased * 0.58,
  };
};

ここで使う t * t * (3 - 2 * t) は smoothstep と呼ばれる補間です。直線的に上げるより、序盤と終盤の変化が穏やかになります。ただし、数式だけで面白さは決まりません。安全なゲートを連続させる区間、障害物を一度減らして呼吸できる区間、トンネルへ入る直前の密度が上がる区間を意図的に混ぜています。

道路が徐々に険しくなり最後のトンネルへ向かう難易度曲線の図

間隔、密度、光量を段階的に上げることで、数値グラフを見なくても「終盤へ向かう」感覚を作れます。

ゲームの難易度は、ただ右肩上がりにせず「緊張と解放」の波として考えます。難所の直後に広い安全区間があるから、危険を抜けた実感が生まれます。60秒という短さでも、導入、理解、圧力、クライマックス、余韻の順番があれば、小さな物語になります。

ゲート、ニトロ、コンボで次の一手を予約する

障害物を避けるだけでは、最善手が「安全な場所で待つ」になりがちです。そこで光るゲートを置き、危険へ近づく理由を作りました。ゲートを連続で通るとコンボが上がり、距離の加算とニトロ演出が強くなります。プレイヤーは現在の安全だけでなく、次のゲートまでの経路を読むようになります。

報酬の数値は、序盤から極端に大きくしません。基本点に小さなコンボ倍率を掛け、上限を設けます。上手な人には連続成功の意味があり、1回の失敗で取り返せない差にはならない範囲です。さらに、失敗後の最初のゲートは光を少し強くし、「ここから戻れる」と伝えます。

中毒性は罰を重くすることではなく、次の成功がすぐ見えることから生まれます。

乱数を記録できる形にして検証する

ランダムに障害物を出すゲームでは、「たまたま理不尽だった」を再現できないと改善が止まります。開発中はシード付き乱数を使い、同じシードなら同じ並びになるようにしました。左と中央と右が同時に塞がる配置を禁止し、直前の安全レーンから最低1回の入力で到達できることもチェックします。

検証では平均スコアだけでなく、次の項目を見ます。

  • 初回の入力までにかかった秒数
  • 衝突が多発した経過時間と配置のシード
  • ゲートを3つ以上つないだ人の割合
  • 左右どちらかに入力が偏っていないか
  • 60秒終了後に再挑戦を選んだ割合

ベスト距離は親サイトのセーブブリッジへ渡します。iframe内のLocal Storageへ直接閉じ込めないことで、ゲームの配信先を移しても親サイト側で保存方針を保てます。保存するのはベスト距離など最小限の値だけで、毎フレームの位置は保存しません。

小さいルールほど、説明できる設計にする

完成後に残ったのは、3レーン、左右入力、障害物、ゲート、60秒という少数の規則です。しかし、その裏では状態、入力、描画、当たり判定、難易度、保存を分離しています。単純なゲームは、雑に作ってよいゲームではありません。むしろ要素が少ないからこそ、ひとつの違和感が全体へ響きます。

NEON VELOCITYの開発でいちばん役立った基準は、「失敗した人が、次に何をすればよいか一秒以内に分かるか」でした。速さを上げる前に判断を明確にし、派手な演出を足す前に因果をそろえる。この順序を守れば、小さな操作からでも、何度も走りたくなる速度の物語を作れます。