Note: Input queueing is a basic technique of controls, which may seems like it doesn’t warrant a write-up, but it’s come up in three separate projects over the past few months, so I felt compelled to write up an article I can easily point to for an explanation.
Introduction
What constitutes “good controls” in a video game can be many nuanced bits of code handling of inputs that aggregate into one hard-to-describe system. However, one easy measure is, “I hit a button and the game did what that button is supposed to do”. Sometimes even when coding controls “correctly”, players will hit buttons and what they expected to happen doesn’t happen at all.
The Culprit? Finite State Machines
A cornerstone of game programming is the finite state machine (FSM). FSMs organize game code into a set number of states where only one state can be active at a time. FSMs must transition to a different state to perform different actions and specific actions can be taken at the beginning and/or end of these transitions. If you are unfamiliar with FSMs, there is a great deal of information elsewhere.
FSMs are excellent for organizing AI but are also often used for character controller code. While state machines are wonderful for compartmentalizing code, they are the primary reason for needing input queues.
The compartmentalizing of control inputs is definitely an advantage of finite state machines – an input cannot be accepted unless a character is in a certain state. However, players cannot always tell what state a character is in. Or they cannot tell the moment a transition from one state to another occurs. Or maybe they cannot react quickly to a state change.
Imagine this scenario for a controllable character:
- Our character has two states: running and jumping.
- The code will not accept a “jump” input if the character is already jumping, only in the running state.
Now imagine your player is jumping and wants to jump again the instant they land. However, the player hits the jump button 1 frame before landing on the ground and entering the running state. No jump occurs. Due to your correctly functioning FSM, that input will be ignored.
In a fast-paced, 60 fps game, will your player say to themselves, “Oh, maybe I hit the button 16 milliseconds too early”? Or will they say, “This game has garbage controls”? I think any game developer knows the answer to this.
Queues
Enter input queues.
Here is a simple ruleset for an input queue
- Inputs go input a single-slot queue
- An input in the queue expires after [aNumber] of milliseconds (this must be tweaked for need & feel)
- Anytime an input is taken from the queue, the queue is cleared
- When the FSM transitions to a new state, the input queue is checked and if there is a viable input, the action for that state is instantly executed.
Now let’s look at our above example with a input queue where inputs have a 33 millisecond expiration time. Our player can hit jump 2 frames too early and the jump will still occur as quickly as possible. Our player will feel like the controls are more responsive and won’t feel the need to mash buttons.
Where I have been seeing the need for input queues recently is in combat systems that have multiple attack types. For example, players will have:
- A basic attack that can chain into itself for combinations
- A super attack.
The player will hit basic attack a few times and then super but their super input gets lost because they hit the button during the final few frames of animation of the basic attack. An input queue makes the attacks flow seamlessly into one another even when a player’s timing it a bit off.
Conclusion
Finite state machines are one of the best tools in the game programmer’s toolbox but a strict implementation without input queues can lead to controls feeling like they are losing inputs. More complex input queues can be created, like using multiple input slots with FIFO (first in, first out) execution. You can also make separate queues for different inputs types, like moving and attacking.
Some players will never even notice because they just mash buttons like crazy but more deliberate players will notice this issue instantly and, quite rightly, become frustrated with your game.