Skip to content

System Scheduling

v2.4.0+

In addition to manually controlling execution order with updateOrder, the framework provides a declarative system scheduling mechanism that lets you define execution order through dependencies.

import { EntitySystem, ECSSystem, Stage, Before, After, InSet } from '@esengine/ecs-framework';
// Use decorators to declare system scheduling
@ECSSystem('Movement')
@Stage('update') // Execute in update stage
@After('InputSystem') // Execute after InputSystem
@Before('RenderSystem') // Execute before RenderSystem
class MovementSystem extends EntitySystem {
constructor() {
super(Matcher.all(Position, Velocity));
}
protected process(entities: readonly Entity[]): void {
// Movement logic
}
}
// Use system sets for grouping
@ECSSystem('Physics')
@Stage('update')
@InSet('CoreSystems') // Belongs to CoreSystems set
class PhysicsSystem extends EntitySystem {
// ...
}
@ECSSystem('Collision')
@Stage('update')
@After('set:CoreSystems') // Execute after all systems in CoreSystems set
class CollisionSystem extends EntitySystem {
// ...
}

The framework defines the following system execution stages, executed in order:

StageDescriptionTypical Usage
startupStartup stageOne-time initialization
preUpdatePre-update stageInput handling, state preparation
updateMain update stage (default)Core game logic
postUpdatePost-update stagePhysics, collision detection
cleanupCleanup stageResource cleanup, state reset
@ECSSystem('Input')
@Stage('preUpdate') // Handle input in pre-update stage
class InputSystem extends EntitySystem {
protected process(entities: readonly Entity[]): void {
// Read input, update input components
}
}
@ECSSystem('Movement')
@Stage('update') // Handle movement in main update stage
class MovementSystem extends EntitySystem {
protected process(entities: readonly Entity[]): void {
// Move entities based on input
}
}
@ECSSystem('Physics')
@Stage('postUpdate') // Handle physics in post-update stage
class PhysicsSystem extends EntitySystem {
protected process(entities: readonly Entity[]): void {
// Physics simulation and collision detection
}
}
@ECSSystem('Cleanup')
@Stage('cleanup') // Reset state in cleanup stage
class CleanupSystem extends EntitySystem {
protected process(entities: readonly Entity[]): void {
// Clean up temporary data
}
}

If you prefer not to use decorators, you can configure scheduling at runtime using the Fluent API:

@ECSSystem('Movement')
class MovementSystem extends EntitySystem {
constructor() {
super(Matcher.all(Position, Velocity));
// Configure scheduling using Fluent API
this.stage('update')
.after('InputSystem')
.before('RenderSystem')
.inSet('CoreSystems');
}
}

System sets allow you to group related systems, then define dependencies based on the entire set:

// Define core systems set
@ECSSystem('Movement')
@InSet('CoreSystems')
class MovementSystem extends EntitySystem { }
@ECSSystem('Physics')
@InSet('CoreSystems')
class PhysicsSystem extends EntitySystem { }
@ECSSystem('AI')
@InSet('CoreSystems')
class AISystem extends EntitySystem { }
// Execute after core systems set
@ECSSystem('Render')
@After('set:CoreSystems')
class RenderSystem extends EntitySystem { }
// Execute before core systems set
@ECSSystem('Input')
@Before('set:CoreSystems')
class InputSystem extends EntitySystem { }

The framework automatically detects cyclic dependencies and throws clear errors:

// This will cause a cycle dependency error
@ECSSystem('SystemA')
@Before('SystemB')
class SystemA extends EntitySystem { }
@ECSSystem('SystemB')
@Before('SystemA') // Error: A -> B -> A forms a cycle
class SystemB extends EntitySystem { }
// Error message: Cyclic dependency detected: SystemA -> SystemB -> SystemA
DecoratorDescriptionExample
@Stage(name)Specify execution stage@Stage('update')
@Before(system)Execute before specified system@Before('RenderSystem')
@After(system)Execute after specified system@After('InputSystem')
@InSet(name)Join system set@InSet('CoreSystems')
@Before('set:name')Execute before set@Before('set:UI')
@After('set:name')Execute after set@After('set:Physics')
FeatureupdateOrderDeclarative Scheduling
ConfigurationManually set valuesDeclare dependencies
ReadabilityNeed to remember value meaningsDirectly express intent
Cycle detectionNoneAutomatic
Refactor-friendlyManually adjust valuesAuto-handles ordering
Use caseSimple projectsComplex dependencies