Skip to content

Input

The engine handles keyboard and mouse input in two steps:

  1. An input system listens to the browser. It dispatches every key or mouse event on the world as KeyboardInput or MouseInput.
  2. A control system reads those events. It finds actors with a KeyboardControl or MouseControl component and dispatches your events on them, as the component’s bindings say.

So game logic does not check keys. You bind KeyW to a Move event in the editor, and a system listens for Move.

The browser sends a key event to KeyboardInputSystem, which dispatches KeyboardInput on the world. KeyboardControlSystem reads it and dispatches a Move event on the actor, as its binding says. Your system listens for Move. Browserkey and mouse eventsthe player presses WKeyboardInputSysteminput systemKeyboardInputdispatched on the worldKeyboardControlSystemcontrol systemKeyboardControl bindingKeyW → MoveMoveyour event, dispatched on the actorYour systemlistens for Move
A key press becomes your own event in two steps. The control system reads the bindings of the actor's KeyboardControl component.

There are four systems, all of them world systems:

System What it does
KeyboardInputSystem Dispatches KeyboardInput on the world
KeyboardControlSystem Dispatches the bound events on actors with KeyboardControl
MouseInputSystem Dispatches MouseInput on the world
MouseControlSystem Dispatches the bound events on actors with MouseControl
  1. In the Systems tab, add the systems for the input you need: Keyboard Input System and Keyboard Control System for the keyboard, Mouse Input System and Mouse Control System for the mouse.

  2. Add the classes to the engine in src/index.ts:

    import {
    Engine,
    KeyboardInputSystem,
    KeyboardControlSystem,
    MouseInputSystem,
    MouseControlSystem,
    KeyboardControl,
    MouseControl,
    } from 'dacha';
    const engine = new Engine({
    config,
    systems: [
    KeyboardInputSystem,
    KeyboardControlSystem,
    MouseInputSystem,
    MouseControlSystem,
    ...gameSystems,
    ],
    components: [KeyboardControl, MouseControl, ...gameComponents],
    assets: [],
    });
  3. Add a KeyboardControl or MouseControl component to each actor that reacts to input.

The mouse systems also need CameraSystem. MouseInputSystem uses it to convert the pointer position to world coordinates.

MouseInputSystem must come before MouseControlSystem.

Put your own systems that read MouseInput after MouseInputSystem too.

The keyboard systems’ order in the list does not matter.

Both input systems have the same two options:

Option What it does
useWindow Listens on window. The default is true
windowNodeId The id of the element to listen on when useWindow is off

An element receives key events only when it has focus.

A KeyboardControl component holds a list of bindings. Each binding has these fields:

Field What it does
key The key, as KeyboardEvent.code: KeyW, Space, ArrowUp
pressed true sends the event when the key goes down. false sends it when the key goes up
keepEmit Sends the event again in every frame while the key is held. Only for pressed bindings
eventType The name of the event to dispatch on the actor
attrs Values to put in the event payload

key is the physical key, not the character it types. KeyW is the same key on every keyboard layout.

In the editor, press the pencil next to Key and then press the key you want.

The Keyboard Control component in the inspector. It lists bindings such as KeyA to RotateInput, KeyW to ThrustInput and ArrowUp to ThrustInput. The KeyA binding is expanded: Key is KeyA with a pencil button, Pressed and Keep Emit are checked, Event Type is RotateInput, and Attributes has one field. Add New Bind is at the bottom.
Each row is one binding, shown as the key and the event it sends.

A pressed binding without keepEmit sends one event per press. The browser repeats keydown while a key is held, and the system ignores those repeats.

keepEmit is for actions that last while the key is down, such as movement.

A MouseControl component also holds a list of bindings:

Field What it does
event The browser event: mousedown, mouseup, mousemove, click, contextmenu, dblclick, mouseenter or mouseleave
button The mouse button for mousedown and mouseup: 0 is left, 1 is middle, 2 is right
eventType The name of the event to dispatch on the actor
attrs Values to put in the event payload

In the editor, each mouse event can have one binding.

The payload of a mouse control event has your attrs and these fields:

Field What it is
x, y The pointer position in world coordinates
screenX, screenY The pointer position in pixels, relative to the window element
nativeEvent The browser’s MouseEvent

Despite their names, screenX and screenY are not the browser’s screenX and screenY.

A control event is an actor event, so it propagates from the actor to the scene and the world:

const handleMove = (event: MoveEvent): void => {
const movement = event.target.getComponent(Movement);
movement.directionX += event.x;
movement.directionY += event.y;
};
scene.addEventListener(Move, handleMove);

Declare your event names in src/events/index.ts, and the editor offers them in the eventType field. See declaring your own events.

dacha has two types for the payload. KeyboardControlEvent<T> is an actor event with your attributes. MouseControlEvent<T> adds the pointer fields:

import type { MouseControlEvent } from 'dacha/events';
export type ShootEvent = MouseControlEvent<{ weapon: string }>;

Every actor with a matching binding receives the event. When several actors have the same binding, one key press reaches all of them.

To handle input without a control component, listen for KeyboardInput or MouseInput on the world:

import { KeyboardInput, type KeyboardInputEvent } from 'dacha/events';
const handleKey = (event: KeyboardInputEvent): void => {
if (event.key === 'Escape' && event.pressed) {
// open the pause menu
}
};
world.addEventListener(KeyboardInput, handleKey);
Event Payload
KeyboardInput key, pressed, nativeEvent
MouseInput eventType, button, x, y, screenX, screenY, nativeEvent

Raw KeyboardInput includes the browser’s repeated keydown events. x and y of MouseInput are in world coordinates, as in control events. Without a current camera, they stay in pixels.