FIG-127

ゲームループとTick処理 — 遅いPCでも、壁をすり抜けないために

ゲーム 2026.10.13 公開 読了 約10分

ゲームの画面は、パラパラ漫画です。1秒間に何十回も「入力を読む → 世界を1コマ進める → 絵を描く」を繰り返していて、この繰り返しをゲームループ、1回分を1フレームと呼びます。60fpsなら1秒に60回、1フレームに使える時間はわずか16.7ミリ秒です。

やっかいなのは、1秒に何回まわせるかがPCによって違うこと。速いPCと遅いPCで同じゲームを動かすと、キャラの速さが変わってしまったり、壁をすり抜けてしまったりします。下の図1で、フレームレートの違う2台のPCに同じキャラを同じコードで走らせてみてください。キャラは壁で止まるはずです。

GAME LOOP — 毎フレーム、入力 → 更新 → 描画
 
PC A —
PC B —
下の帯 = 1フレーム1マス。幅が広いほど、そのフレームに時間がかかった
MODE ①
1フレームごとに、決まった距離だけ進める
図1 — ①はフレームレートで速さが変わる。②は速さはそろうが、カクついた1フレームで大きく飛んで壁をすり抜ける。③は経過時間を1/60秒ずつ刻んで進めるので、どちらも起きない

① フレームごとに固定量 — 遅いPCでは、キャラも遅い

いちばん素朴な書き方は、x += 3 のように1フレームで進む距離を決めてしまうことです。これだと、1秒に60回まわるPCは60回、20回しかまわらないPCは20回しか進みません。図1で走らせると、PC Bのキャラは3倍遅い。昔のゲームには、速いPCで遊ぶと敵まで速くなってしまうものがありましたが、原因はこれです。

② 経過時間(dt)をかける — 速さはそろうが、飛ぶ

そこで、前のフレームから実際に何秒たったか(dt)を測り、x += 速さ × dt とします。フレームが少ないPCは1回でたくさん進むので、1秒あたりの進み方がそろいます。

ところが、ディスクの読み込みや別アプリのせいで1フレームだけ0.5秒止まったらどうなるか。次のフレームの dt は0.5秒になり、キャラは一気に大きく瞬間移動します。当たり判定は「いまの位置で壁と重なっているか」しか見ていないので、壁の手前から向こう側へ飛んでしまうと、一度も重ならずにすり抜けます。図1の「カクつかせる」で起きるのがこれです。

③ 固定Tick — 時間を貯めて、1/60秒ずつ払う

解決策は、世界を進める単位をいつも同じ1/60秒(1Tick)に固定することです。経過時間は貯金箱(accumulator)に入れ、1Tick分たまるごとに1回ずつ世界を進めて当たり判定をします。0.5秒止まったあとのフレームでは、30Tick分をまとめて30回に分けて進めるので、壁の手前で必ず止まれます。描画は今まで通り1フレームに1回なので、描画の回数と世界を進める回数を切り離した、ということです。

物理演算や対戦ゲームのように、どのPCでも同じ結果になってほしい処理はたいていこの方式です。ただし、止まりすぎると取り戻しのTickが増えすぎて余計に重くなる(spiral of death)ので、1フレームで進めるTick数には上限を設けます。また、Tickの途中のタイミングで描くとカクついて見えるため、前後のTickの位置を補間して描く工夫もよく使われます。

用語ミニ辞書
ゲームループ
入力・更新・描画を、ゲームが終わるまで繰り返すループ。
フレーム / fps
ループ1回分(=画面1枚)。fpsは1秒あたりのフレーム数。
dt(デルタタイム)
前のフレームから今のフレームまでに経過した時間。
Tick / 固定タイムステップ
世界を進める一定の時間単位。描画のフレームとは別に、決まった刻みで更新する方式。
すり抜け(トンネリング)
1回の更新での移動が大きすぎて、薄い壁と一度も重ならずに通り抜けてしまう現象。

まとめ

ゲームループは入力 → 更新 → 描画の繰り返しで、その速さはPCや状況によって変わります。1フレームに固定量進めると速さがPC次第になり、経過時間をかけるとカクついた瞬間に大きく飛ぶ。経過時間を貯めて決まった刻み(Tick)で世界を進めると、フレームレートに関係なく同じ動きになります。「絵を描く回数」と「世界を進める回数」を分けて考えることが、ゲームループの核心です。