Zustandsmaschine
MavonEngine verwendet ein Pushdown-Automat-Muster für die Entity-Zustandsverwaltung. Zustände werden gestapelt: Ein neuer Zustand kann oben hinzugefügt werden (der vorherige wird ausgesetzt) und später wieder entfernt, um ihn fortzusetzen. Dies ermöglicht komplexe Verhaltensweisen wie Unterbrechungen und fortsetzbare Aktionen.
Referenz: Das Muster ist direkt aus Game Programming Patterns — State-Kapitel entnommen.
EntityState
Alle Zustände erweitern die abstrakte EntityState-Klasse.
import { EntityState, Actor } from '@mavonengine/core'
class IdleState extends EntityState {
enter(): void {
// Wird aufgerufen, wenn dieser Zustand zum aktiven Zustand wird
}
suspend(): void {
// Wird aufgerufen, wenn ein neuer Zustand darüber gestapelt wird
}
leave(): void {
// Wird aufgerufen, wenn dieser Zustand dauerhaft vom Stack entfernt wird
}
update(delta: number): EntityState | void {
// Neuen Zustand zurückgeben zum Wechseln, oder void um in diesem Zustand zu bleiben
if (this.entity./* eine Bedingung */) {
return new WalkState(this.entity)
}
}
}
Lebenszyklus-Methoden
| Methode | Wann aufgerufen |
|---|---|
enter() | Zustand wird der Stack-Spitze |
suspend() | Ein anderer Zustand wird darüber gestapelt |
leave() | Zustand wird dauerhaft vom Stack entfernt |
update(delta) | Jeden Tick, nur am obersten Zustand aufgerufen |
Wechseln
Gib einen neuen EntityState von update() zurück, um zu wechseln. Das leave() des aktuellen Zustands wird aufgerufen und das enter() des neuen Zustands.
update(delta: number): EntityState | void {
if (this.shouldAttack()) {
return new AttackState(this.entity)
}
}
Zugriff auf die Entity
Jeder Zustand hat eine Referenz auf den besitzenden Actor über this.entity.
class WalkState extends EntityState {
update(delta: number): EntityState | void {
// Entity bewegen
this.entity.position.x += delta * speed
}
}
Verwendung mit Actor
Zustände werden automatisch von Actor.update() verwaltet. Initialisiere den ersten Zustand im Konstruktor deines Actors:
import { Actor, EntityState } from '@mavonengine/core'
class MyActor extends Actor {
constructor() {
super()
this.state = [new IdleState(this)]
}
}
Der Stack ist bei actor.state verfügbar (ein Array, wobei das letzte Element der aktive Zustand ist).
Beispiel: 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)
}
}