Skip to content
Lesson 3 of 14

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

  1. 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.
  2. 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.
  3. 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.
  4. 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.

Up nextTransforms & MathGame Loop

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…