Skip to content

Input ​

This chapter introduces keyboard input and shows how to drive movement and rotation from arrow keys. We add a shared Input helper in lib, then use it in the engine and the demo.

The input helper (lib, ch04) ​

The input system keeps track of three states:

  • Held keys (isDown) — currently pressed.
  • Just pressed (wasPressed) — pressed this frame only.
  • Just released (wasReleased) — released this frame only.

Here is the input class as it exists at the ch04 tag:

ts
export class Input {
	private keys: Set<string> = new Set();
	private justPressed: Set<string> = new Set();
	private justReleased: Set<string> = new Set();

	private onKeyDown = (e: KeyboardEvent) => {
		if(!e.repeat) {
			this.keys.add(e.key);
			this.justPressed.add(e.key);
		}
	};

	private onKeyUp = (e: KeyboardEvent) => {
		this.keys.delete(e.key);
		this.justReleased.add(e.key);
	};

	constructor() {
		window.addEventListener("keydown", this.onKeyDown);
		window.addEventListener("keyup", this.onKeyUp);
	}

	dispose(): void {
		window.removeEventListener("keydown", this.onKeyDown);
		window.removeEventListener("keyup", this.onKeyUp);
	}

	update(): void {
		this.justPressed.clear();
		this.justReleased.clear();
	}

	isDown(key: string): boolean {
		return this.keys.has(key);
	}

	wasPressed(key: string): boolean {
		return this.justPressed.has(key);
	}

	wasReleased(key: string): boolean {
		return this.justReleased.has(key);
	}
}

How it works ​

  • keydown adds the key to the held set and records a one‑frame justPressed state.
  • keyup removes the key and records a one‑frame justReleased state.
  • update() clears the one‑frame sets and must be called once per frame.

Key values use KeyboardEvent.key, so arrows are ArrowLeft, ArrowRight, and ArrowUp.

Engine integration (ch04) ​

At this tag, the engine owns an Input instance and updates it every frame:

  • this.input is available in subclasses.
  • this.input.update() runs at the end of each frame to clear one‑frame states.
  • Pressing Escape toggles pause by stopping or resuming the loop.

This makes it easy for any chapter to check keys during update().

Using input in the demo ​

In update(), we read arrow keys to steer and thrust:

ts
if (this.input.isDown('ArrowLeft')) {
	this.playerAngle -= 180 * deltaTime;
}
if (this.input.isDown('ArrowRight')) {
	this.playerAngle += 180 * deltaTime;
}

if (this.input.isDown('ArrowUp')) {
	this.playerThrust = 200;
} else {
	this.playerThrust = 0;
}

The rest of the update applies thrust to velocity, friction, and wraps the position so the ship re‑enters from the opposite edge.

Library changes ​

  • Added lib/Input for keyboard state tracking.
  • Engine now owns an Input instance and calls input.update() every frame.
  • Escape toggles pause inside the engine loop.