Systems
Systems contain game logic. They declare what data they need via parameters, and ESEngine injects the data automatically. Systems operate on all entities in the scene that match the query.
Defining Systems
import { defineSystem, Res, Time, Query, Mut, LocalTransform, Velocity } from 'esengine';
const movementSystem = defineSystem( [Res(Time), Query(Mut(LocalTransform), Velocity)], (time, query) => { for (const [entity, transform, velocity] of query) { transform.position.x += velocity.linear.x * time.delta; transform.position.y += velocity.linear.y * time.delta; } });This system automatically processes every entity in the scene that has both LocalTransform and Velocity components.
defineSystem takes two arguments:
- Parameters — an array of
Query,Res,ResMut, orCommandsdescriptors - Function — receives the resolved parameters in the same order
Registering Systems
Register systems with top-level functions:
import { addSystem, addStartupSystem, addSystemToSchedule, Schedule } from 'esengine';
addStartupSystem(setupSystem); // Schedule.StartupaddSystem(movementSystem); // Schedule.UpdateaddSystemToSchedule(Schedule.FixedUpdate, physicsSystem);Schedule Types
| Schedule | When it runs |
|---|---|
Startup | Once at the beginning |
First | Every frame, before PreUpdate |
PreUpdate | Every frame, before Update |
Update | Every frame (main game logic) |
PostUpdate | Every frame, after Update |
Last | Every frame, after PostUpdate |
FixedPreUpdate | At fixed intervals, before FixedUpdate |
FixedUpdate | At fixed intervals (physics) |
FixedPostUpdate | At fixed intervals, after FixedUpdate |
System Parameters
Commands
Create, modify, and destroy entities at runtime:
import { Commands, Sprite, LocalTransform } from 'esengine';
defineSystem([Commands()], (cmds) => { // Spawn a new entity with components (chainable) const bullet = cmds.spawn() .insert(LocalTransform, { position: { x: 0, y: 0, z: 0 } }) .insert(Sprite, { size: { x: 8, y: 8 } }) .id();
// Modify an existing entity cmds.entity(bullet) .insert(Velocity, { linear: { x: 100, y: 0 } }) .remove(Sprite);
// Despawn an entity cmds.despawn(bullet);
// Insert a resource cmds.insertResource(Score, { value: 0 });});Commands API
| Method | Returns | Description |
|---|---|---|
cmds.spawn() | EntityCommands | Create a new entity, returns a builder |
cmds.entity(entity) | EntityCommands | Get a builder for an existing entity |
cmds.despawn(entity) | Commands | Queue entity for destruction |
cmds.insertResource(res, value) | Commands | Insert or overwrite a resource |
EntityCommands API
spawn() and entity() return an EntityCommands builder. All methods are chainable:
| Method | Returns | Description |
|---|---|---|
.insert(component, data?) | this | Add or update a component |
.remove(component) | this | Remove a component |
.id() | Entity | Get the entity ID |
Query
Iterate entities with specific components:
import { Query, Mut, LocalTransform, Sprite } from 'esengine';
defineSystem([Query(Mut(LocalTransform), Sprite)], (query) => { for (const [entity, transform, sprite] of query) { transform.position.x += 1; }});See Queries for the full API.
Res (read-only resource)
import { Res, Time } from 'esengine';
defineSystem([Res(Time)], (time) => { console.log(`Delta: ${time.delta}s`);});ResMut (mutable resource)
import { ResMut } from 'esengine';
defineSystem([ResMut(GameState)], (state) => { state.value.score += 10;});See Resources for the full API.
Combining Parameters
defineSystem( [Commands(), Res(Time), Res(Input), Query(Mut(LocalTransform), Velocity)], (cmds, time, input, query) => { // All parameters available });Example: Player Movement
Define a Speed component and attach it to the player entity in the editor. Then write a system:
import { defineComponent } from 'esengine';export const Speed = defineComponent('Speed', { value: 200 });import { defineSystem, addSystem, Res, Time, Input, Query, Mut, LocalTransform } from 'esengine';import { Speed } from '../components/Speed';
addSystem(defineSystem( [Res(Time), Res(Input), Query(Mut(LocalTransform), Speed)], (time, input, query) => { for (const [entity, transform, speed] of query) { if (input.isKeyDown('KeyD')) { transform.position.x += speed.value * time.delta; } if (input.isKeyDown('KeyA')) { transform.position.x -= speed.value * time.delta; } } }));