「着地する少し前にジャンプキーを押したのに反応しない…これって仕様?」「先行入力って格闘ゲームでよく聞くけど、どういう仕組みなの?」「IsTriggerだけじゃ足りないの?」
こんな疑問はありませんか?
今回は、アクションゲームの操作性を大きく左右する入力バッファリング(先行入力)について、実際にコンパイル・実行した結果を交えながら解説していきます。「押したのに反応しない」を実際に再現してから、その解決策を見ていきます。
入力バッファリングとは?
入力バッファリングとは、
・押されたキー入力を一定フレームの間だけ「記憶」しておき、後から条件が揃ったタイミングで改めて使えるようにする仕組み
です。トリガー入力だけを使うと「押した瞬間」しか反応できませんが、実際のプレイヤーは「地面に着く直前」のようにタイミングがシビアな場面で、少し早めにボタンを押してしまうことがよくあります。これを毎回「反応しない」で切り捨てると、操作がシビアで気持ちよくないゲームになってしまいます。
【実験】バッファリング無しだとどうなるか
プレイヤーが空中にいるframe 10でジャンプキーを押し、frame 13で実際に着地する、という状況を再現してみます。まずはバッファリング無しの版です。
bool jumped = false;
if (pressedThisFrame && isGrounded) { // 押した瞬間、かつ地面にいる場合だけジャンプ
jumped = true;
}実行結果です。
=== NG: バッファリング無し(着地した瞬間しかジャンプを受け付けない) ===
frame 8: grounded=0 pressed=0 -> -
frame 9: grounded=0 pressed=0 -> -
frame 10: grounded=0 pressed=1 -> -
frame 11: grounded=0 pressed=0 -> -
frame 12: grounded=0 pressed=0 -> -
frame 13: grounded=1 pressed=0 -> -
frame 14: grounded=1 pressed=0 -> -
frame 15: grounded=1 pressed=0 -> -frame 10でボタンは確かに押されているのに、その時点ではまだ空中(grounded=0)なので弾かれ、着地するframe 13にはもうボタンが押されていないため、結局最後までジャンプが1回も発生しません。プレイヤー視点では「ちゃんと押したのに反応しなかった」という、理不尽な操作ミス扱いになってしまいます。
入力バッファリングを実装してみる
押された入力を「何フレーム前まで」なら有効とみなすか、というバッファ期間を持つクラスを実装します。
class InputBuffer {
public:
static const int BUFFER_FRAMES = 6; // このフレーム数だけ「押した」を覚えておく
void Record(int currentFrame) {
s_PressedFrame = currentFrame;
}
// まだ有効期限内の入力が残っているか(currentFrame時点で判定)
bool IsBuffered(int currentFrame) const {
if (s_PressedFrame < 0) return false;
return (currentFrame - s_PressedFrame) <= BUFFER_FRAMES;
}
// 使ったら消す(1回だけ消費させるため)
void Consume() {
s_PressedFrame = -1;
}
private:
int s_PressedFrame = -1;
};ボタンを押した瞬間はRecordで「いつ押されたか」を記録するだけにとどめ、実際にジャンプさせるかどうかの判定は着地判定側に任せます。
if (pressedThisFrame) {
jumpBuffer.Record(frame); // 押した瞬間はとりあえず記録するだけ
}
bool jumped = false;
if (isGrounded && jumpBuffer.IsBuffered(frame)) {
jumped = true;
jumpBuffer.Consume();
}同じ状況(frame 10でボタン、frame 13で着地)で実行した結果です。
=== OK: 入力バッファリングあり(先行入力を着地まで覚えておく) ===
frame 8: grounded=0 pressed=0 buffered=0 -> -
frame 9: grounded=0 pressed=0 buffered=0 -> -
frame 10: grounded=0 pressed=1 buffered=1 -> -
frame 11: grounded=0 pressed=0 buffered=1 -> -
frame 12: grounded=0 pressed=0 buffered=1 -> -
frame 13: grounded=1 pressed=0 buffered=0 -> ジャンプ!
frame 14: grounded=1 pressed=0 buffered=0 -> -
frame 15: grounded=1 pressed=0 buffered=0 -> -frame 10で押されたボタンの記憶がframe 11・12でもbuffered=1として残り続け、frame 13で着地した瞬間にちゃんとジャンプが発動しました。プレイヤーからすれば、少し早めに押しただけなのに、狙い通りにジャンプできたように感じられます。
入力バッファリングのよくある失敗例と対処法
①バッファ期間を決めずにトリガー判定だけで済ませる
上で実演した通りです。特にジャンプ・回避のようにタイミングがシビアな操作には、数フレーム分のバッファリングを検討しましょう。
②使った入力をConsumeし忘れて何度も発動する
ジャンプが成立した後もConsume()を呼び忘れると、バッファ期間が切れるまで毎フレームジャンプ判定が成立し続け、1回の入力で連続ジャンプしてしまいます。使ったら必ず消費しましょう。
③バッファ期間を長くしすぎる
便利だからといってバッファ期間を極端に長くすると、「だいぶ前に押したボタン」が予想外のタイミングで発動し、逆に操作の意図が伝わりにくくなります。数フレーム(60FPSなら3〜8フレーム程度)を目安に、実際にプレイして調整するのが安全です。
注意点
- 押した瞬間は記録するだけ、実際の発動判定は使う側で行う
- 発動したら必ずConsumeして消費する
- バッファ期間は長すぎず短すぎず、実プレイで調整する
まとめ
- 入力バッファリングは押された入力を数フレーム覚えておく仕組み
- バッファリング無しだと「押したのに反応しない」が実際に起きることを確認した
- バッファリングありだと、条件が揃った瞬間に先行入力が発動する
- 発動後は必ずConsumeで消費する
「反応が悪い」と感じるゲームの多くは、実はプレイヤーの反射神経ではなく、こうしたバッファの有無が原因だったりします。数フレームの余裕を持たせるだけで体感が大きく変わることを、実際に数値で確認できました。
ここまで読んでくださりありがとうございました。

