Step 1 of 5 · Reading · ~3 min
Read
Game Loop
Input Handling
Gameplay code asks three questions about a key every frame: is it held, did it go down this frame, did it come up this frame. After this lesson your input layer will answer them correctly even when the operating system sends repeats, taps shorter than a frame and lost key-ups, and it will map actions to keys and clean up a thumbstick axis.
Events in, state out
The platform delivers events: key down, key up, axis motion. Game logic does not want a callback in the middle of its update; it wants to ask a question during it. So the input layer sits between them. Events are applied to a small state table, and gameplay queries the table. Each key has three facts:
- held (level): true from the press until the release. Use it for "run while the button is down".
- pressed (edge): true only in the frame the press arrived. Use it for "jump once".
- released (edge): true only in the frame the release arrived. Use it for "fire the charged shot".
At the start of every frame the edges are cleared and the held flags are kept.
What real devices force on you
- OS key repeat. Hold a key and the OS keeps sending key-down events, about 30 a second after an initial delay, each flagged as a repeat. They are not new presses. Treat them as a press and a jump fires every few frames.
- Taps shorter than a frame. The key goes down and up before gameplay runs. Held is already false, but the player did press and release it. Edges must therefore be recorded when the event arrives, never derived by comparing held against last frame.
- Lost key-ups. Alt-tab while holding a key and the window never sees the release. When focus is lost, release everything, with a released edge so a charging attack cancels cleanly.
- Stray key-ups. A release for a key that was never down produces no edge.
Actions
Gameplay asks about JUMP, not about SPACE. A binding table maps an action to one or more keys; an action is held, pressed or released if any of its keys is. Rebinding replaces the whole list. A name that is not a bound action is just a key.
Analog axes and deadzones
A thumbstick reports a signed 16-bit value from -32768 to 32767 and rarely rests at exactly 0. Anything with a magnitude at or below the deadzone is reported as 0. Passing the raw value through above the deadzone makes the output jump from 0 straight to the deadzone's size, so rescale: subtract the deadzone from the magnitude, multiply by 32767, divide by (32767 minus the deadzone) using integer division, cap the result at 32767 (the magnitude of -32768 is 32768), and restore the sign.
With a deadzone of 4000:
raw output
4000 0 (inside the deadzone)
4001 1
18383 16382
32767 32767
-32768 -32767 (capped)
-10000 -6834
Worked trace
event held pressed released
KEYDOWN X 0 yes yes no
FRAME yes no no
KEYDOWN X 1 yes no no (repeat, ignored)
KEYUP X no no yes
KEYDOWN X 0 yes yes yes (tap inside one frame: all three true)
FRAME yes no no
FOCUS_LOST no no yes
Common mistakes
- Counting a repeat event as a press.
- Computing pressed as "held now, not held last frame". A tap shorter than a frame disappears.
- Forgetting focus loss, which leaves keys stuck down.
- Clearing held at the frame boundary along with the edges.
- Letting a stray key-up create a released edge.
Your exercise
Build the input manager described in the exercise. The starter resolves actions and answers queries; you write the event handling and the axis function. The most likely mistake is letting repeat events count as presses.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…