Skip to content

Actions

Actions are a powerful system for handling input and events in your game while maintaining context-aware control over when these events can occur. The system consists of three main components: contexts, actions, and controls.

Contexts represent different states or modes of your game. For example:

src/dill-pixel.config.ts
const contexts = defineContexts(['default', 'game', 'menu', 'popup']);

Common contexts include:

  • default - Base game state
  • game - Active gameplay
  • menu - Menu navigation
  • popup - Popup/modal dialogs
  • pause - Paused state

Actions are defined with their allowed contexts using defineActions:

src/dill-pixel.config.ts
const actions = defineActions(contexts, {
// Action with specific contexts
move_left: { context: ['game'] },
// Action allowed in all contexts
pause: { context: '*' },
// Action with multiple contexts
close: { context: ['menu', 'popup'] },
});

Each action specifies:

  • An action identifier (e.g., move_left)
  • Context(s) where the action is allowed
  • '*' means the action is allowed in all contexts

The action system supports touch controls through buttons and joysticks. Here’s how to configure them:

src/dill-pixel.config.ts
const buttons = defineButtons(['A', 'B']);

Actions can be mapped to different input methods using defineControls:

src/dill-pixel.config.ts
const controls = defineControls(actions, buttons, {
keyboard: {
down: {
move_left: ['ArrowLeft', 'A'],
jump: ['ArrowUp', 'Space'],
},
up: {
pause: 'P',
close: 'Escape',
},
},
touch: {
down: {
jump: 'A',
warp: 'B',
},
joystick: {
move_left: ['left', 'bottom_left'],
},
},
});

In your game code, create and register button instances with the touch controls:

import { MyApplication } from '@/MyApplication';
import { Scene, Button } from 'dill-pixel';
export default class GameScene extends Scene<MyApplication> {
initialize() {
// Create buttons
const jumpButton = this.add.button({
id: 'A', // Must match the ID used in defineButtons
textures: {
default: 'button-a',
hover: 'button-a-hover',
active: 'button-a-pressed',
},
});
const warpButton = this.add.button({
id: 'B',
textures: {
default: 'button-b',
hover: 'button-b-hover',
active: 'button-b-pressed',
},
});
// Register buttons with touch controls
this.app.input.touch.addButton(jumpButton);
this.app.input.touch.addButton(warpButton);
}
}

See the Joystick page for more information on the Joystick UI component and how to use it with the actions system.

  1. The ActionsPlugin maintains the current context and validates actions against it:

    sendAction<TActionData = any>(actionId: Action | string, data?: TActionData): void {
    // check if action is defined
    if (!this._actions[actionId]) {
    return;
    }
    // check if action is allowed for current context
    // send action if allowed
    if (
    this._actions[actionId]?.context === '*' ||
    this._actions[actionId]?.context === this.context ||
    this._actions[actionId]?.context?.includes(this.context)
    ) {
    return this.getAction<TActionData>(actionId).emit({ id: actionId, context: this.context, data });
    }
    }
  2. Actions can be dispatched using:

    // Send an action with optional data
    app.sendAction('jump', { power: 100 });
    // Or using the shorter alias
    app.action('jump', { power: 100 });
  3. You can listen for actions:

    app.actions('jump').connect((detail) => {
    const { id, context, data } = detail;
    // Handle the jump action
    });
  1. Context Control: Actions are automatically filtered based on the current context, preventing unwanted inputs during inappropriate game states.

  2. Input Abstraction: The action system separates input handling from game logic, making it easier to:

    • Support multiple input methods (keyboard, touch, gamepad)
    • Change control schemes without modifying game logic
    • Add new input methods
  3. Type Safety: The system is fully typed, providing autocomplete and compile-time checking:

The following example demonstrates how the action system ensures that player movement is automatically disabled when the game is paused, while still allowing pause-specific actions like “resume” to function.

Register the action in your dill-pixel.config.ts file:

// Define actions with contexts
const actions = defineActions(contexts, {
move: { context: ['game'] },
pause: { context: '*' },
resume: { context: ['pause'] },
select: { context: ['menu'] }
});
// ... later on, export the action types and contexts
export type ActionTypes = keyof typeof actions;
export type Contexts = (typeof contexts)[number];