Machines à états
Introduction
Les machines à états sont conçues pour simplifier la gestion des états.Une machine à états est toujours dans un seul état à la fois et passe d'un état à l'autre lorsque certaines conditions (définies par les déclencheurs) sont remplies. Les groupes d'États sont un moyen pratique de regrouper la logique partagée entre plusieurs États, mais les groupes ne sont pas des États à proprement parler. Ils fournissent en grande partie la même API qu'un état, mais répartissent le comportement et les déclencheurs sur l'ensemble de leurs sous-états.
Une machine à états est composée de trois éléments principaux :
- États
- Groupes
- Déclencheurs
Machine à états
Définition d'une machine à états
Définisseur de machines d'État
Lors de la création d'une machine d'État dans un composant, une instance de StateMachineDefiner est utilisée.
Propriétés
Propriété | Type | Description |
---|---|---|
monde | Le monde | Référence au monde. |
eid | eid | L'ID de l'entité du composant actuel |
schemaAttribute | Attribut du monde | Référence au schéma du composant actuel dans World Scope. |
dataAttribute | Attribut du monde | Référence aux données de la composante actuelle dans World Scope. |
Le code suivant est un exemple de définition d'une machine à états vide :
ecs.registerComponent({
...
stateMachine : ({world, eid}) => {
// Définir les états ici
},
})
StateMachineDefinition
Vous pouvez également créer une machine d'état indépendante d'un composant.
Lors de la création d'une machine d'État en dehors d'un composant, une instance de StateMachineDefinition est utilisée.
Propriétés
Propriété | Type | Description |
---|---|---|
initialState (Obligatoire) | chaîne de caractères | Nom de l'état de départ de la machine à états |
États (obligatoire) | Record<string, State> | Une carte qui stocke les noms des États et leur définition |
groupes | StateGroup[] | Une liste facultative de groupes d'États. |
const stateMachine = {
initialState : 'a'
states : {
'a' : {
onExit : () => console.log('exit a'),
triggers : {
'b' : [{ type: 'timeout', timeout: 1000 }],
},
},
'b' : { onEnter : () => console.log('enter b') },
},
groups : [{
substates : ['a', 'b'],
listeners : [{
target : world.events.globalId,
name : ecs.input.SCREEN_TOUCH_START,
listener : (event) => console.log('touch'),
}]
}],
}
État
Un état est l'unité atomique fondamentale d'une machine à états. Ils peuvent être définis directement ou à l'aide de l'API fluente StateMachineDefiner décrite ci-dessus. Une machine à états est toujours dans un seul état à la fois et passe d'un état à l'autre en fonction de déclencheurs définis par l'utilisateur et associés à l'état actuel.
Propriétés
Propriété | Type | Description |
---|---|---|
déclencheurs (obligatoire) | Record<string, Trigger[]> | Transitions sortantes, indexées par leur état cible |
onEnter | () => void | Fonction appelée lors de l'entrée dans l'état |
onTick | () => void | Fonction appelée chaque fois que l'on se trouve dans l'état |
onExit | () => void | Fonction appelée lors de la sortie de l'état |
auditeurs | ListenerParams[] | Paramètres de l'écouteur d'événements, automatiquement ajoutés à l'entrée et supprimés à la sortie |
Définir un État
Le code suivant est un exemple de définition d'un nouvel état à l'intérieur d'une machine d'état dans un composant.
ecs.registerComponent({
...
stateMachine : ({world, eid}) => {
const foo = ecs.defineState('foo')
...
}
})
identifiant
Les StateIds sont utilisés pour spécifier les destinations des transitions. Il peut s'agir d'un StateDefiner ou du nom de l'État lui-même sous forme de chaîne de caractères.
const a = ecs.definestate('a').wait(1000, 'b')
const b = ecs.defineState('b').wait(1000, a)
Les fonctions de StateDefiner sont "fluentes", c'est-à-dire qu'elles renvoient la même instance, ce qui vous permet d'enchaîner plusieurs appels de fonctions dans une seule déclaration.
.initial()
Marquer cet état comme étant l'état actuel de l'automate à états lors de sa création.
ecs.defineState('myCustomState').initial()
.onEnter()
Définir une procédure de rappel à exécuter lors de l'entrée dans cet état.
ecs.defineState('myCustomState').onEnter(() => {
// Faire quelque chose
})
.onTick()
Définir un rappel à exécuter à chaque image.
ecs.defineState('myCustomState').onTick(() => {
// Faire quelque chose
})
.onExit()
Définit un rappel à exécuter lorsque l'on quitte cet état.
ecs.defineState('myCustomState').onExit(() => {
// Faire quelque chose
})
.onEvent()
Appel pour ajouter un EventTrigger de cet état à un autre qui peut faire la transition lorsqu'un événement spécifique est invoqué.
Propriétés
Paramètres | Type | Description |
---|---|---|
événement (obligatoire) | chaîne de caractères | Le nom de l'événement à écouter |
nextState (Obligatoire) | stateId | L'état vers lequel il faut passer lorsque l'événement se produit |
args | objet | Arguments utilisés pour déterminer les conditions de transition |
Args
Paramètres | Type | Description |
---|---|---|
cible | eid | L'entité censée recevoir l'événement (par défaut, le propriétaire de la machine d'état) |
où | (QueuedEvent) => booléen | Une condition facultative à vérifier avant la transition ; si elle est fausse, la transition n'a pas lieu. |
ecs.defineState('myCustomState').onEvent(
ecs.input.SCREEN_TOUCH_START,
'other',
{
target : world.events.globalId,
where : (event) => event.data.position.y > 0.5
}
)
.wait()
Appel pour ajouter un TimeoutTrigger de cet état à un autre qui transite après un certain temps.
Paramètres | Type | Description |
---|---|---|
délai d'attente | nombre | Durée en millisecondes avant la transition |
nextState | StateId | L'état suivant à atteindre |
ecs.defineState('myCustomState').wait(1000, 'myOtherCustomState')
.onTrigger()
Appel pour ajouter un CustomTrigger de cet état à un autre qui peut être transposé à tout moment par l'utilisateur. Utilisez ecs.defineTrigger() pour créer un TriggerHandle qui peut être invoqué manuellement.
Paramètres | Type | Description |
---|---|---|
poignée | TriggerHandle | La poignée qui provoque une transition lorsqu'elle est activée manuellement |
nextState | StateId | L'état suivant à atteindre |
const toOther = ecs.defineTrigger()
ecs.defineState('example').onTrigger(toOther, 'other')
...
toOther.trigger()
.listen()
Appel pour ajouter des ListenerParams à l'ensemble des listeners de cet état. Un récepteur d'événements sera automatiquement ajouté lors de l'entrée dans l'état et supprimé lors de la sortie.
Paramètres | Type | Description |
---|---|---|
cible | eid ou () => eid | L'entité qui est censée recevoir un événement |
nom | chaîne de caractères | L'événement à suivre |
auditeur | (QueuedEvent) => void | La fonction à appeler lorsque l'événement est déclenché |
const handleCollision = (event) => { ... }
ecs.defineState('example').listen(eid, ecs.physics.COLLISION_START_EVENT, handleCollision)
Groupes d'États
Définition d'un groupe d'États
Définisseur de groupes d'États
Lors de la création d'un groupe d'États à l'intérieur d'un composant, une instance de StateGroupDefiner est utilisée.
Paramètres | Type | Description |
---|---|---|
substates (Obligatoire) | StateId[] | La liste des États qui composent ce groupe ; l'exclusion de ce paramètre équivaut à lister tous les États. |
const fizz = ecs.defineState('fizz')
const buzz = ecs.defineState('buzz')
const fizzBuzz = ecs.defineStateGroup([fizz, 'buzz'])
Les fonctions de StateGroupDefiner sont "fluentes", c'est-à-dire qu'elles renvoient la même instance, ce qui vous permet d'enchaîner plusieurs appels de fonctions dans une seule déclaration.
.onEnter()
Définir un rappel à exécuter lors de l'entrée dans ce groupe.
ecs.defineStateGroup(['a', 'b']).onEnter(() => {
// Faire quelque chose
})
.onTick()
Définir un rappel à exécuter à chaque image.
ecs.defineStateGroup(['a', 'b']).onTick(() => {
// Faire quelque chose
})
.onExit()
Définir un rappel à exécuter lors de la sortie de ce groupe.
ecs.defineStateGroup(['a', 'b']).onTick(() => {
// Faire quelque chose
})
.onEvent()
Appel pour ajouter un EventTrigger de n'importe quel état de ce groupe vers un autre état qui peut effectuer une transition lorsqu'un événement spécifique est invoqué.
Propriétés
Paramètres | Type | Description |
---|---|---|
événement (obligatoire) | chaîne de caractères | Le nom de l'événement à écouter |
nextState (Obligatoire) | étatId | L'état vers lequel il faut passer lorsque l'événement se produit |
args | objet | Arguments utilisés pour déterminer les conditions de transition |
Args
Paramètres | Type | Description |
---|---|---|
cible | eid | L'entité censée recevoir l'événement (par défaut, le propriétaire de la machine d'état) |
où | (QueuedEvent) => booléen | Une condition facultative à vérifier avant la transition ; si elle est fausse, la transition n'a pas lieu. |
ecs.defineStateGroup(['a', 'b']).onEvent(
ecs.input.SCREEN_TOUCH_START,
'other',
{
target : world.events.globalId,
where : (event) => event.data.position.y > 0.5
}
)
.wait()
Appel pour ajouter un TimeoutTrigger depuis n'importe quel état de ce groupe vers un autre état qui transite après un certain temps.
Paramètres | Type | Description |
---|---|---|
délai d'attente | nombre | Durée en millisecondes avant la transition |
nextState | StateId | L'état suivant à atteindre |
ecs.defineStateGroup(['a', 'b']).wait(1000, 'c')
.onTrigger()
Appel pour ajouter un CustomTrigger de n'importe quel état de ce groupe vers un autre état qui peut être transposé à tout moment par l'utilisateur. Utilisez ecs.defineTrigger() pour créer un TriggerHandle qui peut être invoqué manuellement.
Paramètres | Type | Description |
---|---|---|
poignée | TriggerHandle | La poignée qui provoque une transition lorsqu'elle est activée manuellement |
nextState | StateId | L'état suivant à atteindre |
const toC = ecs.defineTrigger()
ecs.defineStateGroup(['a', 'b']).onTrigger(toC, 'c')
...
toC.trigger()
.listen()
Appel pour ajouter des ListenerParams à l'ensemble des listeners de cet état. Un récepteur d'événements sera automatiquement ajouté lors de l'entrée dans l'état et supprimé lors de la sortie.
Paramètres | Type | Description |
---|---|---|
cible | eid ou () => eid | L'entité qui est censée recevoir un événement |
nom | chaîne de caractères | L'événement à suivre |
auditeur | (QueuedEvent) => void | La fonction à appeler lorsque l'événement est déclenché |
const handleCollision = (event) => { ... }
ecs.defineState('example').listen(eid, ecs.physics.COLLISION_START_EVENT, handleCollision)
Déclencheurs
Il existe plusieurs types de déclencheurs de la transition dans différentes circonstances
Poignée
Objet utilisé pour définir une transition arbitraire entre deux états. Il doit être créé via ecs.defineTrigger et est utilisé par onTrigger ou CustomTrigger.
Paramètres | Type | Description |
---|---|---|
déclencher() | () => void | Appeler cette fonction pour déclencher toutes les transitions CustomTrigger actives. |
const go = ecs.defineTrigger()
const stopped = ecs.defineState('stoppe').onTick(() => {
if (world.input.getAction('start-going')) {
go.trigger()
}
}).onTrigger(go, 'going')
const going = ecs.defineState('going')
Les types
Déclencheur d'événements
Les déclencheurs d'événements sont utilisés pour assurer une transition facultative lorsqu'un événement spécifique est invoqué. Les données relatives à l'événement peuvent être utilisées pour prendre la décision d'effectuer ou non une transition au cours de l'exécution.
Paramètres | Type | Description |
---|---|---|
type (obligatoire) | événement | Une constante pour indiquer le type de déclencheur |
événement (obligatoire) | chaîne de caractères | Le nom de l'événement à écouter |
cible | eid | L'entité qui est censée recevoir un événement |
où | (QueuedEvent) => booléen | Un prédicat optionnel à vérifier avant la transition ; retourner false empêchera la transition de se produire |
const example = {
triggers :
'other' : [
{
type : 'event',
event : ecs.input.SCREEN_TOUCH_START,
target : world.events.globalId
where : (event) => event.data.position.y > 0.5
},
]
}
Déclencheur de délai d'attente
Les TimeoutTriggers sont utilisés pour provoquer une transition après un délai fixe à partir de l'entrée dans un état ou un groupe.
Paramètres | Type | Description |
---|---|---|
type (obligatoire) | délai d'attente | Une constante pour indiquer le type de déclencheur |
timeout (Obligatoire) | nombre | Nombre de millisecondes à attendre avant d'effectuer la transition |
const example = {
triggers :
'other' : [
{
type: 'timeout',
timeout: 1000,
},
]
}
Déclencheur personnalisé
Les CustomTriggers sont des transitions qui peuvent être déclenchées à tout moment, provoquant une transition immédiate. Utilisez ecs.defineTrigger() pour créer un TriggerHandle qui peut être invoqué manuellement.
Paramètres | Type | Description |
---|---|---|
type (obligatoire) | 'custom' (personnalisé) | Une constante pour indiquer le type de déclencheur |
timeout (Obligatoire) | nombre | Nombre de millisecondes à attendre avant d'effectuer la transition |
const toOther = ecs.defineTrigger()
const example = {
triggers :
'other' : [
{
type: 'custom',
trigger: toOther,
},
]
}
...
toOther.trigger()