入力処理・バッファリングと遅延
操作が「重い」「入らない」を根絶したい人へ。入力のサンプリング周期と先行入力バッファ、コヨーテタイム、そして押してから画面に映るまでの遅延の内訳を押さえれば、手触りは設計で作れます。
- 入力はフレーム境界で離散標本化するため、ポーリング周期より短い操作を取りこぼす。ゲーム更新レートとOS・機器のポーリングレート(USB HIDは125〜1000Hz)を分けて設計する。
- 先行入力(インプットバッファ)は、行動が受理可能になる前に来た入力を数フレーム保持して受理時に消費する仕組み。コヨーテタイムは接地判定が外れた直後の猶予フレームで、いずれも「人間の入力タイミングのばらつき」を吸収して操作を成立しやすくする。
- 入力遅延は標本化・シミュレーション・描画・バッファ待ち・表示の合計だ。多重バッファ、VSync、GPU先行フレーム、画面応答が重なり、60Hzでも数十〜100ms超になり得る。
入力は連続ではなく「フレーム境界で離散サンプリングされる」
横にスクロール
プレイヤーのボタン操作は物理的には連続時間で起きますが、ゲームがそれを認識できるのは更新ループが入力を読みに行った瞬間だけです。多くのゲームは固定または可変の更新周期を回し、各フレームの先頭で入力状態をまとめて取得します。つまり入力は、フレーム境界というサンプリング点で離散化されます。60Hz更新なら約16.67msごと、120Hzなら約8.33msごとにしか「今どのボタンが押されているか」を観測しません。
ここで区別すべきは、ゲームロジックの更新レートと、OS/入力デバイスのポーリングレートが別物だという点です。USB接続のキーボードやゲームパッドはHID(Human Interface Device)として動作し、ホストが一定間隔でデバイスへ状態を問い合わせます。この間隔(ポーリングレート)はフルスピードUSBの既定で125Hz(8ms)、高速なゲーミングデバイスでは500Hz(2ms)や1000Hz(1ms)です。デバイスが1000Hzで新しい状態を用意していても、ゲームが60Hzでしか読まなければ、実効的な入力分解能は60Hz側で頭打ちになります。逆にゲームが240Hzで回っても、デバイスが125Hzなら入力の更新はそこで律速されます。遅延と取りこぼしを詰めるには、この2つのレートを分けて考えることが出発点です(OS側のイベント配送やスケジューリングの詳細は /os/ を参照)。
入力がレベル(押されている/いない状態)ではなくエッジ(押した瞬間・離した瞬間)で意味を持つとき、サンプリング周期より短い操作は取りこぼされます。60Hz更新で、押してから8ms後に離すような超短時間のタップは、2つのサンプル点の隙間に収まると「押していない」と観測されうる。これを防ぐには、デバイス/OS層でエッジをラッチ(押下イベントを次の読み取りまで保持)するか、更新周期の間に発生した入力イベントをキューにためて漏れなく処理する設計にします。ポーリングだけに頼らず、イベント駆動でエッジを拾う経路を併用するのが定石です。
先行入力バッファ:早すぎた入力を「捨てずに待たせる」
熟練プレイヤーほど、次の行動が受理可能になる直前に入力を送り込みます。着地の1〜2フレーム前にジャンプを押す、攻撃モーションが終わる寸前に次の攻撃を入れる、といった具合です。素朴な実装では「その瞬間に行動が受理可能でなければ入力を無視」してしまうため、わずかに早い入力はすべて空振りになり、操作は極端にシビアで「入らない」ものになります。
先行入力(インプットバッファリング、input buffering)は、受理できないタイミングで来た入力を即座に捨てず、一定フレーム数だけ保持しておく仕組みです。行動が受理可能になった瞬間にバッファを確認し、有効期限内の入力が残っていればそれを消費して行動を発火します。「ジャンプ入力を最大6フレーム覚えておき、その間に接地したら即ジャンプ」という設計が典型です。バッファ長は手触りに直結し、短すぎるとシビアに、長すぎると「押していないのに勝手に出た」と感じる暴発になります。多くのアクションゲームで数フレーム(およそ3〜10フレーム)が目安です。
先行入力バッファの擬似コード(毎フレーム実行):
# 1. 入力サンプリング:このフレームでジャンプが押されたら記録
if jump_pressed_this_frame:
jump_buffer_timer = JUMP_BUFFER_FRAMES # 例: 6フレーム
# 2. 受理条件を満たし、かつバッファが生きていれば発火
if is_grounded and jump_buffer_timer > 0:
do_jump()
jump_buffer_timer = 0 # 消費したらクリア
# 3. 毎フレーム、バッファの寿命を減らす
if jump_buffer_timer > 0:
jump_buffer_timer -= 1
# ※タイマーはフレーム数で持つ。可変フレームレートなら
# 経過時間(ミリ秒)で持ち、更新レートに依らず一定にする
バッファ長をフレーム数で持つと、更新レートが変わったとき猶予時間そのものが変化します。60Hzでの6フレーム(100ms)は、120Hzでは6フレーム(50ms)になり、体感が変わる。可変フレームレートや複数のリフレッシュレートを想定するなら、バッファはミリ秒などの実時間で持ち、残り時間 -= デルタタイム で減衰させるのが安全です。固定タイムステップ(後述)で更新を回すゲームでは、フレーム数基準でも猶予時間が一定に保たれるため、どちらのモデルを採るかは更新方式とセットで決めます。
コヨーテタイムとコンボ:入力タイミングのばらつきを吸収する
コヨーテタイム(coyote time)は、プラットフォームの端から足を踏み外して接地判定が外れた直後の数フレームだけ、まだ地上にいるものとしてジャンプを受け付ける猶予です。名前は、崖から踏み出しても一瞬空中に留まるアニメの表現に由来します。人間は「端ちょうどで飛ぶ」を正確には合わせられず、わずかに遅れて押しがちです。コヨーテタイムはその遅れを吸収し、「端で飛べなかった」という理不尽な失敗を減らします。実装は、接地が外れてからのフレーム数を数え、それが猶予以内ならジャンプを許可するだけです。先行入力が「早すぎる入力」を、コヨーテタイムが「遅すぎる入力」を救う——両者は対になっています。
コンボや必殺技のコマンド入力も、本質は同じ「タイミングのばらつき吸収」です。格闘ゲームの必殺技は方向とボタンの列(例 {下, 右下, 右, パンチ})を、決められた入力受付猶予(インプットウィンドウ)の中で順に成立させる必要があります。ここで先行入力バッファは、前の技のモーション中に入れた次コマンドを保持し、硬直明けに即発動させる役割を担います。受付猶予が狭いほど操作は難しく、広いほど寛容ですが暴発しやすい。この猶予とバッファ長の調整が、対戦ゲームの手触りと難易度設計の核心になります。
| 仕組み | 救う対象 | 典型的な猶予 | やり過ぎたときの副作用 |
|---|---|---|---|
| 先行入力バッファ | 受理可能になる前の「早すぎる入力」 | 3〜10フレーム相当 | 押していないのに暴発したと感じる |
| コヨーテタイム | 接地が外れた後の「遅すぎるジャンプ」 | 3〜6フレーム相当 | 空中でもジャンプでき浮遊感が出る |
| コマンド入力猶予 | コマンド列の各入力のタイミングずれ | 数フレーム〜十数フレーム | 簡単な入力で技が暴発する |
| ジャンプ抜け(可変ジャンプ) | ボタンを離す反応の遅れ | 押下継続を数フレーム監視 | ジャンプの高さ制御が鈍る |
エンドツーエンド入力遅延の内訳と削減
「押してから画面に反映されるまで」の遅延は単一の値ではなく、複数段のパイプライン遅延の総和です。大まかには次のように積み上がります。
エンドツーエンド入力遅延の内訳(60Hz例、値は概念的な目安):
(1) デバイス→OSのポーリング 0〜8ms ポーリング周期に依存(125Hzで最大8ms)
(2) 入力サンプリング待ち 0〜16ms 次のフレーム境界まで最悪1フレーム待つ
(3) シミュレーション(更新) 約16ms 入力を反映した状態を1フレームで計算
(4) レンダリング(GPU描画) 約16ms その状態のフレームを描く
(5) バックバッファのキュー 0〜Nフレーム 多重バッファ/先行フレームでたまる
(6) VSync待ち+走査・表示応答 0〜16ms+ 表示タイミングとパネル応答
→ 合計は容易に 数フレーム=数十〜100ms超 に達しうる。
各段は「最悪1フレーム待つ」構造なので、段数×フレーム時間で効いてくる。
遅延を削る要点は、この各段を短くする、または段数を減らすことです。第一に、更新・描画レートを上げると1フレームの時間そのものが縮み、(2)〜(6)の各待ちが比例して小さくなります(60Hzの16.67msが120Hzでは8.33ms)。第二に、GPUが先行して描きためるフレーム(先行レンダリングキュー)を減らす。多くのAPIやドライバはスループット向上のため複数フレームを先読みしてキューに積みますが、これは(5)を丸ごと増やします。低遅延モードや「先行フレーム=1」に制限すると、スループットと引き換えに遅延が減ります。第三に、VSyncの扱いです。VSyncオフはティアリング(表示の裂け)と引き換えに待ち(6)を消し、可変リフレッシュレート(VRR)はティアリングを避けつつ待ちを抑えます。トリプルバッファは(6)の待ちを減らしつつも(5)のキューを増やしうるため、遅延とのトレードオフを個別に確認する必要があります(描画パイプラインとフレームペーシングの詳細は /graphics/ を参照)。
シミュレーションを決定論的に保つため、多くのゲームは物理・ロジックを固定タイムステップ(例 毎秒60回)で回し、描画はそれとは別レートで状態を補間して表示します。この構成は再現性と滑らかさに優れますが、補間は「1ステップ前と現ステップの間」を描くため、原理的に最大1固定ステップ分の追加表示遅延を生みます。外挿(次を予測して描く)にすれば遅延は減らせますが、予測が外れると巻き戻り(スナップ)が見えます。低遅延を最優先する対戦系では固定ステップを高頻度化して補間の遅延を圧縮し、滑らかさ優先のタイトルでは補間を選ぶ、といった判断になります。入力遅延・決定論・映像の滑らかさは三つ巴のトレードオフだと捉えてください。
- 2つのレートを分ける: ゲームの更新レートと、デバイス/OSのポーリングレート(HID既定125Hz、高速で最大1000Hz)は独立。実効入力分解能は両者の遅い方で律速される。
- エッジの取りこぼし対策: ポーリング周期より短い操作は失われうる。エッジをラッチする、入力イベントをキューにためて漏れなく処理する経路を併用する。
- 先行入力とコヨーテタイムは対: 先行入力は「早すぎる入力」を数フレーム保持して救い、コヨーテタイムは接地が外れた「直後の遅すぎるジャンプ」を救う。猶予はフレーム数か実時間かを更新方式に合わせて選ぶ。
- 遅延はパイプラインの総和: サンプリング+更新+描画+バックバッファキュー+表示。各段が最悪1フレーム待つため段数×フレーム時間で効く。削減は「レートを上げる」「先行フレームを減らす」「VSync/VRRを最適化する」。
- 固定タイムステップの補間は決定論と滑らかさを得る代わりに最大1ステップの表示遅延を伴う。低遅延優先ならステップ高頻度化、滑らかさ優先なら補間、と使い分ける。
まとめ
入力処理の手触りは、偶然ではなく設計で作られます。まず入力はフレーム境界で離散サンプリングされるため、ゲームの更新レートとデバイス/OSのポーリングレートを分けて理解し、遅い方が実効分解能を決めることを押さえます。ポーリング周期より短い操作の取りこぼしは、エッジのラッチやイベントキューで防ぎます。次に、人間の入力タイミングのばらつきを吸収する仕組みとして、受理前の早すぎる入力を数フレーム保持する先行入力バッファ、接地が外れた直後の遅すぎるジャンプを救うコヨーテタイム、そしてコマンド入力の受付猶予があり、いずれも猶予長の調整が難易度と手触りを左右します。最後に、押してから映るまでのエンドツーエンド遅延は、サンプリング・シミュレーション・レンダリング・バックバッファのキュー・表示という各段の総和であり、各段が最悪1フレーム待つ構造ゆえに段数とフレーム時間の積で効きます。更新レートの向上、先行レンダリングフレームの削減、VSync/VRRの最適化、そして固定タイムステップと補間のトレードオフ管理——これらを組み合わせて初めて、「軽くて、狙い通りに入る」操作感が実現します。
ゲーム開発の記事ガイド
入力処理・バッファリングと遅延を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
ゲーム開発
比較で見る軸
難易度: advanced / カテゴリ: ゲーム開発 / タグ数: 6
導入後に効く点
先行入力(インプットバッファ)は、行動が受理可能になる前に来た入力を数フレーム保持して受理時に消費する仕組み。コヨーテタイムは接地判定が外れた直後の猶予フレームで、いずれも「人間の入力タイミングのばらつき」を吸収して操作を成立しやすくする。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- ゲーム開発
- タグ数
- 6
判断チェックリスト
- 自社の用途が「ゲーム開発 / 入力処理」に近いか確認する。
- 強みである「入力はフレーム境界で離散標本化するため、ポーリング周期より短い操作を取りこぼす。ゲーム更新レートとOS・機器のポーリングレート(USB HIDは125〜1000Hz)を分けて設計する。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。