ステートマシン
はじめに
ステート・マシンは、状態管理を単純化するために設計されている。ステートマシンは常に正確に1つの状態にあり、(トリガーによって定義された)特定の条件が満たされたときに状態間を遷移する。 ステート・グループは、複数のステート間で共有されるロジックを束ねる便利な方法だが、グループそのものはステートではない。 ステートと同じAPIを提供するが、すべてのサブステートに動作とトリガーを分散させる。
ステートマシンは3つの主要コンポーネントから構成される:
- 州
- グループ
- トリガー
ステートマシン
ステートマシンの定義
ステートマシン定義器
コンポーネント内部にステートマシンを作成する場合、StateMachineDefinerのインスタンスが使用されます。
プロパティ一覧
Property | Type | 商品説明 |
---|---|---|
world | World | 世界への言及。 |
eid | eid | 現在のコンポーネントのエンティティID。 |
schemaAttribute | WorldAttribute | ワールドスコープにおける現在のコンポーネントのスキーマへの参照。 |
dataAttribute | WorldAttribute | ワールドスコープにおける現在のコンポーネントのデータへの参照。 |
次のコー ドは、空のステート・マシンを定義する方法の例である:
ecs.registerComponent({
...
stateMachine: ({world, eid}) => {
// ここで状態を定義する
},
})
ステートマシン定義
あるいは、コンポーネントから独立したステート・マシンを作成することもできる。
コンポーネントの外部でステート・マシンを作成する場合、StateMachineDefinition のインスタンスが使用されます。
プロパティ一覧
Property | Type | 商品説明 |
---|---|---|
initialState (必須) | string | ステートマシンの開始状態の名前 |
states (必須) | Record<string, State> | 州名とその定義を格納するマップ |
groups | 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つの状態にあり、現在の状態に関連するユーザー定義のトリガーに従って遷移する。
プロパティ一覧
Property | Type | 商品説明 |
---|---|---|
triggers (必須) | Record<string, Trigger[]> | ターゲット状態によってインデックス付けされた、発信トランジション |
onEnter | () => void | 状態移行時に呼び出される関数 |
onTick | () => void | 状態にある間、毎フレーム呼び出される関数 |
onExit | () => void | 状態を終了するときに呼ばれる関数 |
listeners | ListenerParams[] | イベント・リスナーのパラメータ。入力時に自動的に追加され、終了時に削除される |
州の定義
次のコードは、コンポーネント内のステート・マシン内で新しいステートを定義する方法の例です。
ecs.registerComponent({
...
stateMachine: ({world, eid}) => {
const foo = ecs.defineState('foo')
...
}
})
ID
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を、この状態から別の状態に追加するために呼び出します。
プロパティ一覧
パラメーター | Type | 商品説明 |
---|---|---|
event (必須) | string | リッスンするイベント名 |
nextState (必須) | stateId | イベント発生時に遷移する状態 |
args | object | 移行条件の決定に使用される引数 |
アーギュ
パラメーター | Type | 商品説明 |
---|---|---|
target | eid | イベントを受け取ることが期待されるエンティティ(デフォルトはステートマシンの所有者) |
where | (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を追加するためのコール。
パラメーター | Type | 商品説明 |
---|---|---|
timeout | number | 遷移するまでの時間(ミリ秒単位 |
nextState | StateId | 次に遷移する状態 |
ecs.defineState('myCustomState').wait(1000, 'myOtherCustomState')
.onTrigger()
CustomTriggerをこのステートから別のステートへ追加するコール。 ecs.defineTrigger()を使用して、手動で起動できるTriggerHandleを作成します。
パラメーター | Type | 商品説明 |
---|---|---|
handle | TriggerHandle | 手動で作動させたときにトランジションを起こすハンドル |
nextState | StateId | 次に遷移する状態 |
const toOther = ecs.defineTrigger()
ecs.defineState('example').onTrigger(toOther, 'other')
...
toOther.trigger()
.listen()
このステートのリスナーセットにListenerParamsを追加するコール。 イベント・リスナーは、ステートに入ると自動的に追加され、ステートが終了すると削除される。
パラメーター | Type | 商品説明 |
---|---|---|
target | eid または () => eid | イベントを受信すると予想されるエンティティ |
name | string | 注目のイ ベント |
listener | (QueuedEvent) => void | イベントがディスパッチされたときに呼び出される関数 |
const handleCollision = (event) => { ... }
ecs.defineState('example').listen(eid, ecs.physics.COLLISION_START_EVENT, handleCollision)
州グループ
ステート・グループの定義
ステートグループ定義者
コンポーネント内にステートグループを作成する場合、StateGroupDefinerのインスタンスが使用されます。
パラメーター | Type | 商品説明 |
---|---|---|
substates (必須) | 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を追加するためのコール。
プロパティ一覧
パラメーター | Type | 商品説明 |
---|---|---|
event (必須) | string | リッスンするイベント名 |
nextState (必須) | stateId | イベント発生時に遷移する状態 |
args | object | 移行条件の決定に使用される引数 |
アーギュ
パラメーター | Type | 商品説明 |
---|---|---|
target | eid | イベントを受け取ることが期待されるエンティティ(デフォルトはステートマシンの所有者) |
where | (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を追加するためのコール。
パラメーター | Type | 商品説明 |
---|---|---|
timeout | number | 遷移するまでの時間(ミリ秒単位 |
nextState | StateId | 次に遷移する状態 |
ecs.defineStateGroup(['a', 'b']).wait(1000, 'c')
.onTrigger()
CustomTriggerを、このグループ内の任意のステートから、ユーザーがいつでもすぐに遷移できる他 のステートへ追加するためのコール。 ecs.defineTrigger()を使用して、手動で起動できるTriggerHandleを作成します。
パラメーター | Type | 商品説明 |
---|---|---|
handle | TriggerHandle | 手動で作動させたときにトランジションを起こすハンドル |
nextState | StateId | 次に遷移する状態 |
const toC = ecs.defineTrigger()
ecs.defineStateGroup(['a', 'b']).onTrigger(toC, 'c')
...
toC.trigger()
.listen()
このステートのリスナーセットにListenerParamsを追加するコール。 イベント・リスナーは、ステートに入ると自動的に追加され、ステートが終了すると削除される。
パラメーター | Type | 商品説明 |
---|---|---|
target | eid または () => eid | イベントを受信すると予想されるエンティティ |
name | string | 注目のイベント |
listener | (QueuedEvent) => void | イベントがディスパッチされたときに呼び出される関数 |
const handleCollision = (event) => { ... }
ecs.defineState('example').listen(eid, ecs.physics.COLLISION_START_EVENT, handleCollision)
トリガー
さまざまな状況下で、移行には複数のトリガーがある。
ハンドル
状態間の任意の遷移を定義するためのオブジェクト。 ecs.defineTriggerで作成し、onTriggerまたはCustomTriggerで使用します。
パラメーター | Type | 商品説明 |
---|---|---|
trigger() | () => 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は、指定されたイベントが呼び出されたときに、任意で遷移するために使用される。 イベントデータは、トランジションするかどうかの実行時の判断に使用できる。
パラメーター | Type | 商品説明 |
---|---|---|
type (必須) | 'event' | トリガーのタイプを示す定数 |
event (必須) | string | リッスンするイベント名 |
target | eid | イベントを受信すると予想されるエンティティ |
where | (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は、ステートまたはグループに入ってから一定時間後にトランジションを発生させるために使用されます。
パラメーター | Type | 商品説明 |
---|---|---|
type (必須) | 'timeout' | トリガーのタイプを示す定数 |
timeout (必須) | number | 遷移する前に待つミリ秒数 |
const example = {
trigger:
'other': [
{
type: 'timeout',
timeout: 1000,
},
]
}
カスタムトリガー
CustomTriggers(カスタムトリガー)は、いつでもトリガーできるトランジションで、即座にトランジションを起こします。 ecs.defineTrigger()を使用して、手動で起動できるTriggerHandleを作成します。
パラメーター | Type | 商品説明 |
---|---|---|
type (必須) | 'custom' | トリガーのタイプを示す定数 |
timeout (必須) | number | 遷移する前に待つミリ秒数 |
const toOther = ecs.defineTrigger()
const example = {
trigger:
'other': [
{
type: 'custom',
trigger: toOther,
},
]
}
...
toOther.trigger()