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
Section titled “Contexts”Contexts represent different states or modes of your game. For example:
const contexts = defineContexts(['default', 'game', 'menu', 'popup']);
Common contexts include:
default
- Base game stategame
- Active gameplaymenu
- Menu navigationpopup
- Popup/modal dialogspause
- Paused state
Defining Actions
Section titled “Defining Actions”Actions are defined with their allowed contexts using defineActions
:
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
Defining Buttons
Section titled “Defining Buttons”The action system supports touch controls through buttons and joysticks. Here’s how to configure them:
const buttons = defineButtons(['A', 'B']);
Connecting Controls
Section titled “Connecting Controls”Actions can be mapped to different input methods using defineControls
:
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'], }, },});
Registering Buttons
Section titled “Registering Buttons”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); }}
Joystick Controls
Section titled “Joystick Controls”See the Joystick page for more information on the Joystick UI component and how to use it with the actions system.
How It Works
Section titled “How It Works”Context Management:
Section titled “Context Management:”The ActionsPlugin maintains the current context and validates actions against it:
sendAction<TActionData = any>(actionId: Action | string, data?: TActionData): void {// check if action is definedif (!this._actions[actionId]) {return;}// check if action is allowed for current context// send action if allowedif (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 });}}Action Dispatching:
Section titled “Action Dispatching:”Actions can be dispatched using:
// Send an action with optional dataapp.sendAction('jump', { power: 100 });// Or using the shorter aliasapp.action('jump', { power: 100 });Action Handling:
Section titled “Action Handling:”You can listen for actions:
app.actions('jump').connect((detail) => {const { id, context, data } = detail;// Handle the jump action});
Benefits
Section titled “Benefits”-
Context Control: Actions are automatically filtered based on the current context, preventing unwanted inputs during inappropriate game states.
-
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
-
Type Safety: The system is fully typed, providing autocomplete and compile-time checking:
Example
Section titled “Example”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 contextsconst actions = defineActions(contexts, { move: { context: ['game'] }, pause: { context: '*' }, resume: { context: ['pause'] }, select: { context: ['menu'] }});
// ... later on, export the action types and contextsexport type ActionTypes = keyof typeof actions;export type Contexts = (typeof contexts)[number];
Ensure your application is setup to use the actions:
import { Application } from 'dill-pixel';import { type ActionTypes, type AnalyticsEvents, type Contexts, type Data } from './dill-pixel.config';export class MyApplication extends Application<Data, Contexts, ActionTypes>P {}
Send the action from anywhere:
import { Scene } from 'dill-pixel';
export default class Game extends Scene { async initialize() { this.app.actionContext = 'game'; // connect to your actions this.addSignalConnection( // Listen for pause action (allowed in all contexts) this.app.actions('pause').connect(() => { this.pauseGame(); // Change context to 'pause'
}), // Movement only works in 'game' context this.app.actions('move').connect((detail) => { this.movePlayer(detail.data); }); ); }
pauseGame(){ this.app.actionContext = 'pause' // trigger pause menu, etc }
resumeGame(){ this.app.actionContext = 'game' // resume game, allowing movement }
}