Zum Hauptinhalt springen

Zustandsmaschinen

Einführung

Zustandsautomaten wurden entwickelt, um die Zustandsverwaltung zu vereinfachen.

Ein Zustandsautomat setzt sich aus drei Hauptkomponenten zusammen:

  • Staaten
  • Staatliche Gruppen
  • Auslöser

Ein Zustandsautomat befindet sich immer in genau einem Zustand und wechselt zwischen den Zuständen, wenn bestimmte Bedingungen (definiert durch die Auslöser) erfüllt sind. Zustandsgruppen sind eine bequeme Möglichkeit, die gemeinsame Logik mehrerer Zustände zu bündeln, aber die Gruppen sind selbst keine Zustände.

Beispiel

ecs.registerComponent({
name: 'Jump On Touch',
stateMachine: ({world, entity, defineState}) => {
const idle = defineState('idle').initial().onEnter(() => {
console.log('Entering idle state')
}).onEvent(ecs.input.SCREEN_TOUCH_START, 'jumping')

const jumping = defineState('jumping').onEnter(() => {
console.log('Springender Zustand')
ecs.physics.applyImpulse(world, entity.eid, 0, 5, 0)
}).onTick(() => {
console.log('Im springenden Zustand')
}).wait(2000, 'Leerlauf')
},
})

Definieren eines Zustandsautomaten

Wenn Sie einen Zustandsautomaten in einer Komponente erstellen, wird Ihre Funktion wie folgt aufgerufen:

Eigenschaften

EigentumTypBeschreibung
worldWorldHinweis auf die Welt.
eideidDie Entitäts-ID der aktuellen Komponente
UnternehmenEntitätDie Entitätsinstanz der aktuellen Komponente
defineStateFunktionEine Funktion zur Definition von Zuständen in der Zustandsmaschine
defineStateGroupFunktionEine Funktion zur Definition von Gruppen im Zustandsautomaten
schemaAttributeWorldAttributeVerweis auf das Schema der aktuellen Komponente im World Scope.
dataAttributeWorldAttributeVerweis auf die Daten der aktuellen Komponente im World Scope.

Der folgende Code ist ein Beispiel dafür, wie man einen leeren Zustandsautomaten definiert:

ecs.registerComponent({
...
stateMachine: ({world, entity, defineState}) => {
// Hier werden Zustände definiert
},
})

Staat

Ein Zustand ist die grundlegende atomare Einheit eines Zustandsautomaten. Nachdem Sie die möglichen Zustände Ihres Zustandsautomaten definiert haben, können Sie zwischen den Zuständen wechseln, indem Sie Auslöser definieren.

Definieren eines Staates

Der folgende Code ist ein Beispiel für die Definition eines neuen Zustands innerhalb eines Zustandsautomaten innerhalb einer Komponente.

ecs.registerComponent({
...
stateMachine: ({world, entity, defineState}) => {
const foo = defineState('foo')
...
}
})
Tipp

Zustandsfunktionen sind "fließend", d. h. sie geben dieselbe Instanz des Zustands zurück, so dass Sie mehrere Funktionsaufrufe in einer einzigen Anweisung verketten können.

.initial()

Kennzeichnen Sie diesen Zustand als den ersten aktiven Zustand, wenn der Zustandsautomat erstellt wird.

defineState('myCustomState').initial()

.onEnter()

Legen Sie einen Callback fest, der beim Eintritt in diesen Zustand ausgeführt wird.

defineState('myCustomState').onEnter(() => {
// Etwas tun
})

.onTick()

Legen Sie einen Callback fest, der bei jedem Frame ausgeführt wird, solange dieser Zustand aktiv ist.

defineState('myCustomState').onTick(() => {
// Etwas tun
})

.onExit()

Legen Sie einen Callback fest, der beim Verlassen dieses Zustands ausgeführt wird.

defineState('myCustomState').onExit(() => {
// Etwas tun
})

.onEvent()

Übergang in einen neuen Zustand, wenn ein bestimmtes Ereignis eintrifft.

ParameterTypBeschreibung
Ereignis (erforderlich)StringDer Name des Ereignisses, nach dem gesucht werden soll
nextState (erforderlich)String oder StaatDer Zustand, in den bei Eintreten des Ereignisses übergegangen werden soll
Optionen (fakultativ)ObjektZusätzliche Optionen

Optionen

ParameterTypBeschreibung
ZieleidDie Entität, die das Ereignis empfangen soll (Standardwert ist die aktuelle Entität)
wobei(Ereignis) => booleschEine optionale Bedingung, die vor dem Übergang zu prüfen ist; ist sie falsch, findet der Übergang nicht statt.
defineState('myCustomState').onEvent(
ecs.input.SCREEN_TOUCH_START,
'other',
{
target: world.events.globalId,
where: (event) => event.data.position.y > 0.5
}
)

.wait()

Übergang in einen neuen Zustand nach einer bestimmten Zeitspanne.

ParameterTypBeschreibung
TimeoutnumberDie Dauer in Millisekunden vor dem Übergang
nextStateString oder StaatDer nächste Zustand, in den übergegangen wird
defineState('myCustomState').wait(1000, 'myOtherCustomState')

.onTrigger()

Übergang in einen neuen Zustand, wenn ein TriggerHandle (definiert mit ecs.defineTrigger()) ausgelöst wird.

ParameterTypBeschreibung
handleTriggerHandleDer Griff, der bei manueller Betätigung einen Übergang auslöst
nextStateString oder StaatDer nächste Zustand, in den übergegangen wird
const toOther = ecs.defineTrigger()
defineState('example').onTrigger(toOther, 'other')
...
toOther.trigger()

.listen()

Registrieren Sie einen Ereignis-Listener, der automatisch hinzugefügt wird, wenn der Zustand betreten wird, und beim Verlassen entfernt wird.

ParameterTypBeschreibung
Zieleid oder () => eidDie Entität, von der erwartet wird, dass sie ein Ereignis erhält
NameStringDas Ereignis, auf das Sie achten sollten
Hörer(Ereignis) => voidDie Funktion, die aufgerufen wird, wenn das Ereignis ausgelöst wird
const handleCollision = (event) => { 
console.log('Kollidiert mit', event.data.other)
}
defineState('example').listen(eid, ecs.physics.COLLISION_START_EVENT, handleCollision)

Staatliche Gruppen

Eine Zustandsgruppe ist eine Möglichkeit, Verhalten und Auslöser zu definieren, die für eine Liste von Zuständen gelten. Staatliche Gruppen sind keine Staaten und können nicht direkt in diese überführt werden. Wenn ein Zustand in der Gruppe aktiv ist, sind auch das Verhalten und die Auslöser der Gruppe aktiv.

Definieren einer Statusgruppe

ParameterTypBeschreibung
substates (fakultativ)Array von String oder StateDie Liste der Staaten, aus denen sich diese Gruppe zusammensetzt; das Ausschließen dieses Parameters entspricht der Auflistung aller Staaten
const fizz = defineState('fizz')
const buzz = defineState('buzz')

const fizzBuzz = defineStateGroup([fizz, 'buzz'])
Tipp

Zustandsgruppenfunktionen sind "fließend", d. h. sie geben dieselbe Instanz der Zustandsgruppe zurück, so dass Sie mehrere Funktionsaufrufe in einer einzigen Anweisung verketten können.

.onEnter()

Legen Sie einen Callback fest, der beim Betreten dieser Gruppe ausgeführt wird.

defineStateGroup(['a', 'b']).onEnter(() => {
// Etwas tun
})

.onTick()

Legen Sie einen Callback fest, der bei jedem Frame ausgeführt wird, solange diese Gruppe aktiv ist.

defineStateGroup(['a', 'b']).onTick(() => {
// Etwas tun
})

.onExit()

Legen Sie einen Callback fest, der beim Verlassen dieser Gruppe ausgeführt wird.

defineStateGroup(['a', 'b']).onTick(() => {
// Etwas tun
})

.onEvent()

Übergang in einen neuen Zustand, wenn ein bestimmtes Ereignis eintrifft.

ParameterTypBeschreibung
Ereignis (erforderlich)StringDer Name des Ereignisses, nach dem gesucht werden soll
nextState (erforderlich)String oder StaatDer Zustand, in den bei Eintreten des Ereignisses übergegangen werden soll
Optionen (fakultativ)ObjektZusätzliche Optionen

Optionen

ParameterTypBeschreibung
targeteidDie Entität, die das Ereignis empfangen soll (Standardwert ist die aktuelle Entität)
wobei(Ereignis) => booleschEine optionale Bedingung, die vor dem Übergang zu prüfen ist; ist sie falsch, findet der Übergang nicht statt.
defineStateGroup(['a', 'b']).onEvent(
ecs.input.SCREEN_TOUCH_START,
'other',
{
target: world.events.globalId,
where: (event) => event.data.position.y > 0.5
}
)

.wait()

Übergang in einen neuen Zustand nach einer bestimmten Zeitspanne.

ParameterTypBeschreibung
TimeoutNummerDie Dauer in Millisekunden vor dem Übergang
nextStateString oder StaatDer nächste Zustand, in den übergegangen wird
defineStateGroup(['a', 'b']).wait(1000, 'c')

.onTrigger()

Übergang in einen neuen Zustand, wenn ein TriggerHandle (definiert mit ecs.defineTrigger()) ausgelöst wird.

ParameterTypBeschreibung
GriffTriggerHandleDer Griff, der bei manueller Betätigung einen Übergang auslöst
nextStateString oder StaatDer nächste Zustand, in den übergegangen wird
const toC = ecs.defineTrigger()
defineStateGroup(['a', 'b']).onTrigger(toC, 'c')
...
toC.trigger()

.listen()

Registrieren Sie einen Ereignis-Listener, der automatisch hinzugefügt wird, wenn die Zustandsgruppe betreten wird, und der beim Verlassen entfernt wird.

ParameterTypBeschreibung
targeteid oder () => eidDie Entität, von der erwartet wird, dass sie ein Ereignis erhält
nameStringDas Ereignis, auf das Sie achten sollten
listener(Ereignis) => voidDie Funktion, die aufgerufen wird, wenn das Ereignis ausgelöst wird
const handleCollision = (event) => { 
console.log('collided with', event.data.other)
}
defineStateGroup(['a', 'b']).listen(eid, ecs.physics.COLLISION_START_EVENT, handleCollision)

Benutzerdefinierte Auslöser

Sie können einen benutzerdefinierten Auslöser definieren, der jederzeit aufgerufen werden kann, um einen Übergang zu bewirken.

const go = ecs.defineTrigger()
const stopped = defineState('stopped').onTick(() => {
if (world.input.getAction('start-going'))) {
go.trigger()
}
}).onTrigger(go, 'going')
const going = defineState('going')