ステートマシン
はじめに
ステート・マシンは、状態管理を単純化するために設計されている。ステートマシンは常に正確に1つの状態にあり、(トリガーによって定義された)特定の条件が満たされたときに状態間を遷移する。 ステート・グループは、複数のステート間で共有されるロジックを束ねる便利な方法だが、グループそのものはステートではない。 ステートと同じAPIを提供するが、すべてのサブステートに動作とトリガーを 分散させる。
ステートマシンは3つの主要コンポーネントから構成される:
- 州
- グループ
- トリガー
ステートマシン
ステートマシンの定義
ステートマシン定義器
コンポーネント内部にステートマシンを作成する場合、StateMachineDefinerのインスタンスが使用されます。
プロパティ
プロパティ | タイプ | 説明 |
---|---|---|
世界 | 世界 | 世界への言及。 |
イード | イード | 現在のコンポーネントのエンティティID。 |
スキーマ属性 | ワールド属性 | ワールドスコープにおける現在のコンポーネントのスキーマへの参照。 |
データ属性 | ワールド属性 | ワールドスコープにおける現在のコンポーネントのデータへの参照 。 |
次のコードは、空のステート・マシンを定義する方法の例である:
ecs.registerComponent({
...
stateMachine: ({world, eid}) => {
// ここで状態を定義する
},
})
ステートマシン定義
あるいは、コンポーネントから独立したステート・マシンを作成することもできる。
コンポーネントの外部でステート・マシンを作成する場合、StateMachineDefinition のインスタンスが使用されます。
プロパティ
プロパティ | タイプ | 説明 |
---|---|---|
initialState (必須) | string | ステートマシンの開始状態の名前 |
州(必須) | Record<string, State> | 州名とその定義を格納するマップ |
グループ | StateGroup[] | 州グループのリスト(オプション)。 |
const stateMachine = {
initialState:'a'
states:{
'a': {
onExit: () => console.log('exit a'),
trigger: {
'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'),
}].
}],
}.
州
ステートとは、ステートマシンの基本的な原子単位 である。 これらは、直接定義することもできるし、上記の流暢なStateMachineDefiner APIを使って定義することもできる。 ステートマシンは、常に正確に1つの状態にあり、現在の状態に関連するユーザー定義のトリガーに従って遷移する。
プロパティ
プロパティ | タイプ | 説明 |
---|---|---|
トリガー(必須) | Record<string, Trigger[]> | ターゲット状態によってインデックス付けされた、発信トランジション |
オンエンター | () => void | 状態移行時に呼び出される関数 |
オンティック | () => void | 状態にある間、毎フレーム呼び出される関数 |
終了 | () => void | 状態を終了するときに呼ばれる関数 |
リスナー | ListenerParams[] | イベント・リスナーのパラメータ。入力時に自動的に追加され、終了時に削除される |
州の定義
次のコードは、コンポーネント内のステート・マシン内で新しいステートを定義する方法の例です。
ecs.registerComponent({
...
stateMachine: ({world, eid}) => {
const foo = ecs.defineState('foo')
...
}
})
身分証明書
StateIdsは遷移先の指定に使われる。 StateDefinerまたは状態名そのものを文字列で指定します。
const a = ecs.definestate('a').wait(1000, 'b')
const b = ecs.defineState('b').wait(1000, a)
StateDefiner関数は「フルエント」、つまり同じインスタンスを返すので、1つのステートメントで複数の関数呼び出しを連鎖させることができる。
.initial()
ステートマシンの作成時に、この状態をステートマシンの現在の状態としてマークする。
ecs.defineState('myCustomState').initial()
.onEnter()
この状態になったときに実行するコールバックを設定する。
ecs.defineState('myCustomState').onEnter(() => {
// 何かをする
})
.onTick()
毎フレーム実行するコールバックを設定する。
ecs.defineState('myCustomState').onTick(() => {
// 何かをする
})
.onExit()
この状態を終了するときに実行するコールバックを設定します。
ecs.defineState('myCustomState').onExit(() => {
// 何かをする
})
.onEvent()
特定のイベントが呼び出されたときに遷移できるEventTriggerを、この状態から別の状態に追加するために呼び出します。
プロパティ
パラメータ | タイプ | 説明 |
---|---|---|
イベント (必須) | ストリング | リッスンするイベント名 |
nextState (必須) | 状態ID | イベント発生時に遷移する状態 |
アーギュ | オブジェクト | 移行条件の決定に使用される引数 |
アーギュ
パラメータ | タイプ | 説明 |
---|---|---|
ターゲット | イード | イベントを受け取ることが期待されるエンティティ(デフォルトはステートマシンの所有者) |
どこ | (QueuedEvent) => boolean | トランジションする前にチェックするオプションの条件。 |
ecs.defineState('myCustomState').onEvent(
ecs.input.SCREEN_TOUCH_START,
'other',
{
target: world.events.globalId,
where:(event) => event.data.position.y > 0.5
}.
)
.wait()
この状態から、設定された時間後に遷移する別の状態にTimeoutTriggerを追加するためのコール。
パラメータ | タイプ | 説明 |
---|---|---|
タイムアウト | 番号 | 遷移するまでの時間(ミリ秒単位 |
次の状態 | StateId | 次に遷移する状態 |
ecs.defineState('myCustomState').wait(1000, 'myOtherCustomState')
.onTrigger()
CustomTriggerをこのステートから別のステートへ追加するコール。 ecs.defineTrigger()を使用して、手動で起動できるTriggerHandleを作成します。
パラメータ | タイプ | 説明 |
---|---|---|
ハンドル | トリガーハンドル | 手動で作動させたときにトランジションを起こすハンドル |
次の状態 | StateId | 次に遷移する状態 |
const toOther = ecs.defineTrigger()
ecs.defineState('example').onTrigger(toOther, 'other')
...
toOther.trigger()
.listen()
このステートのリスナーセットにListenerParamsを追加するコール。 イベント・リスナーは、ステートに入ると自動的に追加され、ステートが終了すると削除される。
パラメータ | タイプ | 説明 |
---|---|---|
ターゲット | eidまたは() => eid | イベントを受信すると予想されるエンティティ |
名称 | ストリング | 注目のイベント |
リスナー | (QueuedEvent) => void | イベントがディスパッチされたときに呼び出される関数 |
const handleCollision = (event) => { ... }
ecs.defineState('example').listen(eid, ecs.physics.COLLISION_START_EVENT, handleCollision)
州グループ
ステート・グループの定義
ステートグループ定義者
コンポーネント内にステートグループを作成する場合、StateGroupDefinerのインスタンスが使用されます。
パラメータ | タイプ | 説明 |
---|---|---|
サブステート(必須) | StateId[] | このグループを構成する州のリスト。このパラメータを除外すると、すべての州をリストアップすることになる。 |
const fizz = ecs.defineState('fizz')
const buzz = ecs.defineState('buzz')
const fizzBuzz = ecs.defineStateGroup([fizz, 'buzz'])
StateGroupDefiner関数は "fluent"、つまり同じインスタンスを返すので、1つのステートメントで複数の関数呼び出しを連鎖させることができます。
.onEnter()
このグループに入るときに実行するコールバックを設定します。
ecs.defineStateGroup(['a', 'b']).onEnter(() => {
// 何かをする
})
.onTick()
毎フレーム実行するコールバックを設定する。
ecs.defineStateGroup(['a', 'b']).onTick(() => {
// 何かをする
})
.onExit()
このグループから抜けるときに実行するコールバックを設定します。
ecs.defineStateGroup(['a', 'b']).onTick(() => {
// 何かをする
})
.onEvent()
このグループ内の任意のステートから、特定のイベントが呼び出されたときに遷移できる他のステートへのEventTriggerを追加するためのコール。
プロパティ
パラメータ | タイプ | 説明 |
---|---|---|
イベント (必須) | ストリング | リッスンするイベント名 |
nextState (必須) | 状態ID | イベント発生時に遷移する状態 |
アーギュ | オブジェクト | 移行条件の決定に使用される引数 |
アーギュ
パラメータ | タイプ | 説明 |
---|---|---|
ターゲット | イード | イベントを受け取ることが期待されるエンティティ(デフォルトはステートマシンの所有者) |
どこ | (QueuedEvent) => boolean | トランジションする前にチェックするオプションの条件。 |
ecs.defineStateGroup(['a', 'b']).onEvent(
ecs.input.SCREEN_TOUCH_START,
'other',
{
target: world.events.globalId,
where:(event) => event.data.position.y > 0.5
}.
)
.wait()
このグループ内の任意のステートから、設定された時間後に遷移する他のステートへのTimeoutTriggerを追加するためのコール。
パラメータ | タイプ | 説明 |
---|---|---|
タイムアウト | 番号 | 遷移するまでの時間(ミリ秒単位 |
次の状態 | StateId | 次に遷移する状態 |
ecs.defineStateGroup(['a', 'b']).wait(1000, 'c')
.onTrigger()
CustomTriggerを、このグループ内の任意のステートから、ユーザーがいつでもすぐに遷移できる他のステートへ追加するためのコール。 ecs.defineTrigger()を使用して、手動で起動できるTriggerHandleを作成します。
パラメータ | タイプ | 説明 |
---|---|---|
ハンドル | トリガーハンドル | 手動で作動させたときにトランジションを起こすハンドル |
次の状態 | StateId | 次に遷移する状態 |
const toC = ecs.defineTrigger()
ecs.defineStateGroup(['a', 'b']).onTrigger(toC, 'c')
...
toC.trigger()
.listen()
このステートのリスナーセットにListenerParamsを追加するコール。 イベント・リスナーは、ステートに入ると自動的に追加され、ステートが終了すると削除される。
パラメータ | タイプ | 説明 |
---|---|---|
ターゲット | eidまたは() => eid | イベントを受信すると予想されるエンティティ |
名称 | ストリング | 注目のイベント |
リスナー | (QueuedEvent) => void | イベントがディスパッチされたときに呼び出される関数 |
const handleCollision = (event) => { ... }
ecs.defineState('example').listen(eid, ecs.physics.COLLISION_START_EVENT, handleCollision)
トリガー
さまざまな状況下で、移行には複数のトリガーがある。
ハンドル
状態間の任意の遷移を定義するためのオブジェクト。 ecs.defineTriggerで作成し、onTriggerまたはCustomTriggerで使用します。
パラメータ | タイプ | 説明 |
---|---|---|
トリガー() | () => void | アクティブなCustomTriggerの遷移を発生させるために、この関数を呼び出します。 |
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')
種類
イベントトリガー
EventTriggersは、指定されたイベントが呼び出されたときに、任意で遷移するために使用される。 イベントデータは、トランジションするかどうかの実行時の判断に使用できる。
パラメータ | タイプ | 説明 |
---|---|---|
タイプ (必須) | イベント | トリガーのタイプを示す定数 |
イベント (必須) | ストリング | リッスンするイベント名 |
ターゲット | イード | イベントを受信すると予想されるエンティティ |
どこ | (QueuedEvent) => boolean | 遷移する前にチェックするオプションの述語。 |
const example = {
trigger:
'other': [
{
type: 'event',
event: ecs.input.SCREEN_TOUCH_START,
target: world.events.globalId
where:(event) => event.data.position.y > 0.5
},
].
}
タイムアウトトリガー
TimeoutTriggersは、ステートまたはグループに入ってから一定時間後にトランジションを発生させるために使用されます。
パラメータ | タイプ | 説明 |
---|---|---|
タイプ (必須) | タイムアウト | トリガーのタイプを示す定数 |
タイムアウト(必須) | 番号 | 遷移する前に待つミリ秒数 |
const example = {
trigger:
'other': [
{
type: 'timeout',
timeout: 1000,
},
]
}.
カスタムトリガー
CustomTriggers(カスタムトリガー)は、いつでもトリガーできるトランジションで、即座にトランジションを起こします。 ecs.defineTrigger()を使用して、手動で起動できるTriggerHandleを作成します。
パラメータ | タイプ | 説明 |
---|---|---|
タイプ (必須) | カスタム | トリガーのタイプを示す定数 |
タイムアウト(必須) | 番号 | 遷移する前に待つミリ秒数 |
const toOther = ecs.defineTrigger()
const example = {
trigger:
'other': [
{
type: 'custom',
trigger: toOther,
},
]
}.
...
toOther.trigger()