input buffering in action games — how precise is precise enough and what's your actual window?

64 views 4 replies

Been spending way too long tuning input buffering for a melee action game and I'm starting to question every decision I've made.

The basic idea is simple enough: if the player presses an attack button slightly before the current action allows it (like near the end of an animation), you buffer that input and execute it as soon as the window opens. Without it, anything that isn't frame-perfect feels unresponsive. Fine. But the tuning is where I keep going in circles.

My current setup is roughly:

// on input received:
bufferedAction = inputAction;
bufferTimer = BUFFER_WINDOW; // currently 150ms

// in update:
if (bufferTimer > 0 && canAcceptInput()) {
    executeAction(bufferedAction);
    bufferedAction = null;
    bufferTimer = 0;
}
bufferTimer -= deltaTime;

150ms feels okay but I've seen recommendations ranging from 100ms to 200ms depending on genre. Fighting game players swear by shorter windows because long buffers cause ghost inputs, where you accidentally queue an action you never consciously intended. But on a controller with any latency, short windows mean players feel like their inputs are getting dropped.

A few things I haven't solved cleanly:

  • Priority conflicts — what happens when a dodge and an attack are both buffered? I'm currently just taking the most recent, which feels wrong in practice.
  • Clearing the buffer correctly — should entering a hit reaction clear it? A locked cinematic state? I'm not consistent and it shows.
  • Per-action vs. global buffer — some systems use separate windows per action type. Is that actually worth the added complexity or is it overkill for a non-fighting game?

Curious what people have actually shipped. Is 150ms a reasonable baseline for third-person melee, or am I in the wrong ballpark entirely? And if you've solved the priority conflict thing cleanly, I'd love to hear it.

Worth noting: buffer windows can feel completely different across input devices even when the raw frame count is identical. A 6-frame buffer on keyboard feels snappy; that same window on a controller can start to feel like the game is reading ahead, because analog stick return-to-neutral isn't as clean as a key-up event. I ended up doing separate tuning passes for keyboard/mouse vs. gamepad, which added QA surface area but resolved a bunch of subtle feel complaints that were hard to articulate until you realized they were device-specific all along.

One thing I haven't seen mentioned here: the buffer should consume on the earliest valid opportunity, not the latest. Early in my project I was holding buffered inputs until the last possible frame in the window, thinking it would maximize responsiveness, and it actually made chained inputs feel sluggish. The player would queue the second hit early, the buffer would sit on it until the final cancellable frame, and that gap felt longer than it was even though the window size was identical.

Switching to "execute as soon as the character enters a cancellable state", even if that's on frame 2 of a 6-frame buffer window, made chaining feel noticeably snappier. The window size controls how early you can pre-input, not how long the game delays your action.

The buffer window probably shouldn't be uniform across input types. In my melee game I found that attack inputs could tolerate 6–8 frames without feeling sticky, but dodge inputs needed to be tighter (3–4 frames max). A buffered dodge that fires slightly late feels like the character ignored you, whereas a slightly-early attack usually just reads as intentional aggression.

Also worth separating "consumed on action" from "consumed on opportunity." An attack input can sit in the buffer and fire the moment the character exits hitstun. But a jump input probably shouldn't fire 8 frames later after you've already walked off a platform edge. That specific edge case is legitimately infuriating for players and hard to debug because it only happens at speed.

Replying to ShadowMist: Worth noting: buffer windows can feel completely different across input devices ...

The analog stick dead zone compounds this too. A 6-frame buffer can feel sticky on a controller even when it isn't, because the input isn't binary. There's no tactile confirmation the game heard you, so you second-guess yourself and press again, which then fires twice. On keyboard there's zero ambiguity.

I ended up adding a debug overlay that flashed whenever the buffer consumed an input. Looked ridiculous but it was the only way to actually tune buffer windows across device types without just guessing. Pulled it before ship obviously, but seriously consider it during development if you're struggling to feel the difference.

Moonjump
Forum Search Shader Sandbox
Sign In Register