マガジン

小さなレースゲームを作る:曲がる感覚を整える最初の試作

遊びをつくる

執筆にClaudeを使用しています。

段ボールで作った簡素なカーブを、小さな赤いレーシングカーが曲がる試作風景
記事のテーマをもとに制作したAI生成イラスト。

「車を走らせる」コードはすぐ書ける。難しいのは「曲がる感覚」が生まれる瞬間だ。タイヤが地面を蹴るような重さ、速度が上がるほど舵が鋭くなる手応え——それはフラグ一本で切り替わるものではなく、複数の小さなパラメータが重なって初めて伝わる。この記事では、大きな車両シミュレーターを目標にするのではなく、「操舵と速度の関係だけ」に絞った最初の試作を組む手順を整理する。使うコードは説明用の簡潔なサンプルであり、動作検証済の完成品ではない。試しながら調整することを前提に読んでほしい。

この記事の目次
  1. なぜ最初に「曲がる感覚」から始めるのか
  2. 入力→向き変化→移動の三段構成
  3. 調整しやすいパラメータの置き方
  4. 試作でよく詰まる三つの問題と対処
  5. 素材を使うときの権利確認の進め方
  6. 試作を整理して次のステップへ

なぜ最初に「曲がる感覚」から始めるのか

レースゲームの試作でよくある失敗は、コースや背景グラフィック、ラップタイム計測を先に作り込んで、肝心の操作感の調整が後回しになることだ。操作が気持ちよくなければ、どれだけコースを磨いてもテストプレイが続かない。

最初の試作で確認すべきことは一つに絞れる。「速さと舵の角度が変わったとき、車の動きは違和感なく変化するか」。それだけを判断できる最小構成を先に作り、感触が出てからコースや演出を足していく。この順序が、試作を早く終わらせるだけでなく、最後まで「なぜこの数値にしたのか」を追跡しやすくする。

ここで扱う「感覚」は主観的なものだが、コードの構造は客観的に決められる。入力を受け取り、向きを変え、向きにそって移動する——この三段構成を明確に分けて書くことが、後の調整作業を大幅に楽にする。

入力→向き変化→移動の三段構成

シンプルなトップビューの2Dレースゲームを例に、三段構成を説明する。

第一段:入力の受け取り

キーボードであれゲームパッドであれ、入力はまず「左右の操舵量(-1から+1の実数)」と「アクセル量(0から+1)」に変換する。キーボードは0か1の二値になりがちだが、毎フレームの加算・減算で疑似的な連続値を作ることができる。

javascript
// 説明用サンプル。動作保証はない。
let steer = 0;          // -1(左)〜 +1(右)
let throttle = 0;       // 0〜1
const STEER_SPEED = 0.05;   // 1フレームの舵の変化量
const STEER_RETURN = 0.03;  // 入力がないときに中央へ戻る速さ

function updateInput(keys) {
  if (keys['ArrowLeft'])  steer = Math.max(steer - STEER_SPEED, -1);
  else if (keys['ArrowRight']) steer = Math.min(steer + STEER_SPEED, 1);
  else steer *= (1 - STEER_RETURN); // キーを離したら徐々に戻る

  throttle = keys['ArrowUp'] ? 1 : 0;
}

STEER_SPEEDを大きくすると「キビキビした」舵、小さくすると「もっさりした」舵になる。STEER_RETURNが小さすぎると、キーを離しても車がなかなか直進しない。この二値が「舵の癖」の大部分を決める。

第二段:向き変化

向きの変化量は「操舵量 × 速度係数」で決まる。速度が遅いときは大きく曲がり、速いときは曲がりにくくする——これだけで実在の車らしさが一段増す。

javascript
// 説明用サンプル。動作保証はない。
const MAX_TURN_RATE = 0.04; // ラジアン/フレーム(最大旋回量)
const SPEED_TURN_FACTOR = 0.8; // 速度による旋回抑制の強さ

function updateAngle(car) {
  const speedRatio = car.speed / car.maxSpeed; // 0〜1
  const turnRate = MAX_TURN_RATE * (1 - speedRatio * SPEED_TURN_FACTOR);
  car.angle += steer * turnRate * car.speed; // 停車中は回らない
}

停車中は旋回しない(car.speedを掛ける)という設計は、ゲームによっては外したほうが自然なこともある。カートレースのように切り返しを多用するゲームでは、低速でも小回りを許容する調整が必要になる。

第三段:移動

向きが決まれば、移動は単純なベクトル計算だ。

javascript
// 説明用サンプル。動作保証はない。
const ACCEL = 0.2;
const FRICTION = 0.05;

function updatePosition(car) {
  if (throttle > 0) {
    car.speed = Math.min(car.speed + ACCEL, car.maxSpeed);
  } else {
    car.speed = Math.max(car.speed - FRICTION, 0);
  }
  car.x += Math.cos(car.angle) * car.speed;
  car.y += Math.sin(car.angle) * car.speed;
}

調整しやすいパラメータの置き方

試作の段階では「感触を変えたいときにどこを触ればいいか」を明確にすることが最も重要だ。コードのあちこちにマジックナンバーが散らばっていると、一か所変えるたびに意図しない副作用が生まれる。

パラメータは必ずファイルの先頭にまとめて定数として置く。調整ログとして「変更前の値/変更後の値/変えた理由」をコメントで残しておくと、何度か試作を繰り返した後でも「なぜその数値になったか」を追えるようになる。

  • STEER_SPEED / STEER_RETURN:舵の重さと、離したときの戻りの速さ。最初に手をつけるべき項目。
  • MAX_TURN_RATE:車の最大旋回量。コースの曲率に合わせて決める。
  • SPEED_TURN_FACTOR:速度による旋回抑制の強さ。0にするとすべての速度で同じ曲がり方になる。
  • ACCEL / FRICTION:加速の鋭さと、アクセルを離したときの減速。FRICTIONを0にするとブレーキ操作が必要になる設計になる。

一度にすべてのパラメータを変えると何が効いたかわからなくなる。一つ変えて試し、記録してから次を変える、という習慣が試作の品質を高める。

試作でよく詰まる三つの問題と対処

問題①:速度が高いほど「すべる」感じがなくならない

車の向き(angle)と実際の進行方向(ベロシティベクトル)が常に一致している設計では、高速でも即座に向きが変わり、「タイヤが食いつく感覚」が出ない。簡単な対処は、実際の移動ベクトルを「現在の向きに少しずつ近づける」ラープ処理を挟むことだ。

javascript
// 説明用サンプル。動作保証はない。
// velocityAngleを目標のangleへ徐々に近づける
const GRIP = 0.12; // 0に近いほどドリフトしやすい
car.velocityAngle += (car.angle - car.velocityAngle) * GRIP;
car.x += Math.cos(car.velocityAngle) * car.speed;
car.y += Math.sin(car.velocityAngle) * car.speed;

GRIPを1にすると元の「向き=進行方向」に戻る。0.05〜0.2の範囲で試すと、コースとゲームの雰囲気に合うラインが見つかりやすい。

問題②:フレームレートで挙動が変わる

毎フレームに固定値を足したり掛けたりする設計は、60fpsと30fpsで全く違う挙動になる。デルタタイム(前フレームからの経過秒数)を使ってパラメータに掛けることで、フレームレートに依存しない動きを作れる。ブラウザゲームではrequestAnimationFrameのコールバック引数として渡されるタイムスタンプの差分を使うのが標準的な方法だ。

問題③:コースアウト後の処理がない

最初の試作ではコースを無視して走れる「無限平面」でも操作感は確認できる。ただし壁や境界の反発を入れたくなったときは、「速度を反転する」より「速度を大きく減衰させる」方が自然に見えることが多い。コース設計と操作感の調整は、分けて作業できるよう最初から意識しておくといい。

素材を使うときの権利確認の進め方

試作段階では自作の矩形や三角形だけで操作感の検証ができるため、素材の権利問題は発生しない。公開するゲームに他者の素材(画像・音楽・フォント)を使う場合は、配布・公開用途での利用条件を素材ごとに確認する必要がある。

確認すべき項目は「商用利用の可否」「クレジット表記の要否」「改変可否」「ゲームへの組み込み可否」の四点だ。ライセンス表記がない素材は権利者に直接問い合わせるか、使用を控える。

  • CC0(パブリックドメイン相当):帰属表示なしで利用可能なことが多いが、配布元の条件ページを毎回確認する。
  • CC BY(表示):クレジット表記が必要。表記方法は配布元の指示に従う。
  • ゲーム向け素材配布サービス:サービスごとに独自の利用規約があり、「個人利用のみ」「フリーゲームのみ」など条件が異なる。ゲームをウェブで公開する場合は「ウェブ公開」の可否を明示的に確認する。

素材の利用条件は使用を開始する前に確認し、記録しておく。後から確認しようとすると、素材の配布ページが消えていたり、条件が変わっていたりすることがある。確認日とライセンスの内容を自分のメモに残すことで、公開時のクレジット表記作業が楽になる。

試作を整理して次のステップへ

操作感の調整が一段落したら、試作をそのまま公開することも選択肢の一つだ。「完成していないから公開しない」より「今の時点で試してもらってフィードバックを得る」方が、次の作業の優先順位がはっきりする。

アソバースへの投稿はHTMLファイルの直接アップロードとZIPファイルのアップロード、および公開済みURLの登録に対応している。単一HTMLにまとめた試作は、最小限の投稿形式として機能する。ゲーム内のログインやセーブ機能が必要な場合は、プラットフォームのSDKや独自のサーバーサイド処理を作者が別途実装する必要がある。

試作から次の段階へ進むときに判断すべきことは明確だ。「コースを追加する前に、今の操作感で遊び続けられるか」。この問いに「はい」と言えるなら、次のレイヤーを乗せるタイミングになっている。「いいえ」であれば、パラメータの調整か構造の見直しをもう一周する価値がある。

大きな車両物理ライブラリや3Dレンダリングは、この「曲がる感覚が整った」段階の後で初めて意味を持つ。最初の試作で目指すのはそこではなく、「このゲームで走る感覚はどういうものか」を自分が言葉にできる状態だ。それが決まれば、コース設計も素材選びも迷いが減る。

この記事のコードサンプルはコンセプトの説明を目的としており、特定の環境での動作を保証するものではない。実際の実装では使用するゲームエンジンやフレームワークのAPIに合わせて書き直す必要がある。本文はClaudeが執筆し、人間による取材・プレイテスト・コード検証は行っていない。