Input Polling

While turn-based games are a good fit for event-driven programming, the nature of the game loop does not play nicely with input events. Instead, we rely on device polling - asking the control devices (the keyboard, mouse, joystick, etc.) for their current state.

However, we cannot access this information from a browser, so we do the next-best thing - keeping an object that describes the state of the input based on events. for example:

var currentInput = { up: false, down: false, left: false, right: false, a: false, b:false }

We then need to use input event handlers to update these values:

window.addEventListener('keydown', function(event) { switch(event.key) { case 'ArrowUp': currentInput.up = true; break; case 'ArrowDown': currentInput.down = true; break; case 'ArrowLeft': currentInput.left = true; break; case 'ArrowRight': currentInput.right = true; break; case ' ': // space currentInput.a = true; break; case 'Alt': currentInput.b = true; break; } }); window.addEventListener('keyup', function(event) { switch(event.key) { case 'ArrowUp': currentInput.up = false; break; case 'ArrowDown': currentInput.down = false; break; case 'ArrowLeft': currentInput.left = false; break; case 'ArrowRight': currentInput.right = false; break; case ' ': currentInput.a = false; break; case 'Alt': currentInput.b = false; break; } });

The currentInput object can then be used in the update() function to determine current input state. We can similarly use mouse and joystick events to poll the state of those devices.

Note: JavaScript events are processed in the order they are received, including the timing event that triggers the requestAnimationFrame() callback. So changes to the input structure are made between loop invocations.

Double-Buffering Input

One of the limitations of input polling is that we miss the exact moment that a button is pressed or released, and the button will likely be pressed for multiple frames. Consider pressing the space bar to fire a bullet - even tapping the key will likely span multiple frames. Firing one each frame is problematic, as we'll get a stream of bullets. Rather, we'd like to fire one bullet per key tap.

To do this, we need to know the frame in which the state of the key changes - pressed or released. This is possible if we maintain two input states - that for the current frame, and that from the previous frame:

var currentInput = { // ... input state } var priorInput = { // ... input state }

Each frame we clone the currentInput state into the priorInput structure:

function processInput() { priorInput = JSON.parse(JSON.stringify(currentInput)); }

In our update() function, we can determine if a key was just pressed:

if(!priorInput.a && currentInput.a) { // The key was just pressed this frame! // ... }

or if it was just released:

if(priorInput.a && !currentInput.a) { // The key was just pressed this frame! // ... }

Either can be used to trigger a one-off event. We can also use the currentInput by itself to know if a key is being held down.