State Machine
MavonEngine uses a pushdown automaton pattern for entity state management. States are stacked: a new state can be pushed on top (suspending the previous one) and later popped to resume it. This allows complex behaviors like interruptions and resumable actions.
Reference: The pattern is taken directly from Game Programming Patterns — State chapter.
EntityState
All states extend the abstract EntityState class.
import { EntityState, Actor } from '@mavonengine/core'
class IdleState extends EntityState {
enter(): void {
// Called when this state becomes the active state
}
suspend(): void {
// Called when a new state is pushed on top of this one
}
leave(): void {
// Called when this state is permanently removed from the stack
}
update(delta: number): EntityState | void {
// Return a new state to transition, or void to stay in this state
if (this.entity./* some condition */) {
return new WalkState(this.entity)
}
}
}
Lifecycle methods
| Method | When it's called |
|---|---|
enter() | State becomes the top of the stack |
suspend() | Another state is pushed on top |
leave() | State is popped off the stack permanently |
update(delta) | Every tick, only called on the top state |
Transitioning
Return a new EntityState from update() to transition. The current state's leave() is called and the new state's enter() is called.
update(delta: number): EntityState | void {
if (this.shouldAttack()) {
return new AttackState(this.entity)
}
}
Accessing the entity
Every state has a reference to its owning Actor via this.entity.
class WalkState extends EntityState {
update(delta: number): EntityState | void {
// Move the entity
this.entity.position.x += delta * speed
}
}
Usage with Actor
States are managed automatically by Actor.update(). Initialize the first state in your actor's constructor:
import { Actor, EntityState } from '@mavonengine/core'
class MyActor extends Actor {
constructor() {
super()
this.state = [new IdleState(this)]
}
}
The stack is available at actor.state (an array where the last element is the active state).
Example: idle → walk → attack
class IdleState extends EntityState {
update(delta: number) {
if (input.keysPressed.has('KeyW')) return new WalkState(this.entity)
}
}
class WalkState extends EntityState {
update(delta: number) {
this.entity.position.z -= delta * 5
if (input.keysPressed.has('Space')) return new AttackState(this.entity)
if (!input.keysPressed.has('KeyW')) return new IdleState(this.entity)
}
}
class AttackState extends EntityState {
private timer = 0
update(delta: number) {
this.timer += delta
if (this.timer > 0.5) return new IdleState(this.entity)
}
}