0
0

StateTreeの構成について

Last updated at Posted at 2024-08-14

最初に

StateTreeについて触る機会があったので、自分の中の整理を含めて記事にしてみました。
間違い等あればご指摘頂けますと幸いです。
また、今回の内容は基本的にプログラマー向けになります。

環境

Windows 11
UE 5.4.3

参考

履歴

日時 内容
2024/08/15 Schemaに関する情報に誤解を招く表現があったため、修正しました

準備

エディターのメニューから「編集」>「プラグインの設定」から下記2つを有効化します
image.png

StateTreeとは

StateTreeとは、様々な場面でロジックをBehaviorTreeやStateMachineの様に管理できる
階層型ステートマシン」です。

BehaviorTreeの代わりに思考を管理する用途に使用することもできれば、
BehaviorTree側で呼び出しのTaskも用意されているため併用も可能です。

また、UE5.4時点でExperimentalではありますが
AvalancheMassEntityGameplayInteractionsプラグインの中でも使用されており、
今後の活躍が期待できます。

StateTreeの構成

まず、StateTreeの機能を構成している主な要素について説明をしようと思います。

  1. State Tree Component
  2. State Tree Schema
  3. State
  4. Transition
  5. Node(Task, Evaluator, Condition)

1. State Tree Component

StateTreeを利用したいActorにアタッチするComponentです。
AI使用を目的として用意された派生であるUStateTreeAIComponentの他、
C++、BPで継承して独自実装も可能です。

StateTreeComponentで行う内容は以下

  • StateTreeの開始、一時停止、終了
  • 使用するStateTreeとParameterの設定
  • 使用するSchemaの定義

StateTreeの開始、一時停止、終了

UStateTreeComponentUBrainComponentを継承しており、
各種オーバーライドした関数がStateTreeに対する実行処理になります

関数名 処理内容
StartLogic StateTreeの開始
RestartLogic 実行中、また終了しているStateTreeの再稼働
StopLogic StateTreeの停止
PauseLogic StateTreeの一時停止
ResumeLogic StateTreeの再開
IsRunning StateTreeが実行中か
IsPaused StateTreeが一時停止中か

使用するStateTreeとParameterの設定

このComponentで使用するStateTreeの指定を行います。
image (1).png

Parameterというのは、StateTreeのアセット側で定義して、
外部から引数として指定できる値でそれなりに自由なパラメーターの指定が可能です。
image (2).png
値を追加すると、StateTreeを設定している場所で追加した値の設定が可能になります。
image (3).png

使用するSchemaの定義

Schemaの定義は、StateTreeComponentが継承している
IStateTreeSchemaProviderGetSchemaを使って、
このComponentで使用したいSchemaのクラスを指定します。

// 例:AIStateTreeComponent
TSubclassOf<UStateTreeSchema> UStateTreeAIComponent::GetSchema() const
{
	return UStateTreeAIComponentSchema::StaticClass();
}

この指定を行うことで、StateTreeComponentでStateTreeを設定する時に
同じSchemaを使用しているStateTreeしかリストアップされなくなります。

2. Schema

後述するNode(Task、Condition、Evaluator)の制限や、
StateTreeエディタにあるContext項目など各所で利用するデータの構造や形式を定義します。
image (4).png
C++限定ですが自分でUStateTreeSchemaを継承して作成や拡張することも可能です。

Schemaの基本的な役割は以下

  • Contextの指定
  • Nodeに対する制限
  • Nodeとして使用できる型の制限
  • ExternalDataの型指定、収集

Contextの指定

Schemaで定義される、StateTree内で使用可能な外部のデータです。
基本的にはStateTreeを利用しているActorやAIControllerなどが対象になっています。
image (6).png
image (5).png
ここで定義された内容はStateTree内でNodeのPropertyBindとして使用可能です。
image (7).png

Nodeに対する制限

Evaluator、ConditionのStateTree上での使用を制限できます

/** @return True if enter conditions are allowed. */
virtual bool AllowEnterConditions() const { return true; }

/** @return True if evaluators are allowed. */
virtual bool AllowEvaluators() const { return true; }

また、1つのStateに1つのTaskだけにすることも可能です。

/** @return True if multiple tasks are allowed. */
virtual bool AllowMultipleTasks() const { return true; }

Nodeとして使用できる型の制限

特定の型のTask、Evaluator、Conditionのエディタ上での使用自体を制限できます
UStateTreeAISchemaでは追加でFStateTreeAITaskBaseを使用できるように指定しています。

/** @return True if specified struct is supported */
virtual bool IsStructAllowed(const UScriptStruct* InScriptStruct) const { return false; }

/** @return True if specified class is supported */
virtual bool IsClassAllowed(const UClass* InScriptStruct) const { return false; }
bool UStateTreeAIComponentSchema::IsStructAllowed(const UScriptStruct* InScriptStruct) const
{
	return Super::IsStructAllowed(InScriptStruct) || InScriptStruct->IsChildOf(FStateTreeAITaskBase::StaticStruct());
}

ExternalDataの型指定、収集

External Dataとは、各Nodeで特定の外部データを参照したい時、
都度取得するための関数を呼ばなくて済むように予め情報をキャッシュして
Handle経由で簡単にアクセスできるようにする機能です。

Schemaではそこで使用できる型の指定と、実際のデータの収集を行います。

class STATETREEMODULE_API UStateTreeSchema : public UObject
{
	// 使用できる型の判定.
	virtual bool IsExternalItemAllowed(const UStruct& InStruct) const { return false; }
}

※具体的な使用方法はNodeのExternal Dataに関する項目をご確認ください。
例として、StateTreeComponentSchemaでは

  • Actor
  • Component
  • World Subsystem

の使用が許可されています。

bool UStateTreeComponentSchema::IsExternalItemAllowed(const UStruct& InStruct) const
{
	return InStruct.IsChildOf(AActor::StaticClass())
			|| InStruct.IsChildOf(UActorComponent::StaticClass())
			|| InStruct.IsChildOf(UWorldSubsystem::StaticClass());
}

そして、CollectExternalData関数で外部データの情報をキャッシュしています。

bool UStateTreeComponentSchema::CollectExternalData(const FStateTreeExecutionContext& Context, const UStateTree* StateTree, TArrayView<const FStateTreeExternalDataDesc> ExternalDataDescs, TArrayView<FStateTreeDataView> OutDataViews)
{
	// ~略~
	AAIController* AIOwner = Cast<AAIController>(Owner);
	for (int32 Index = 0; Index < ExternalDataDescs.Num(); Index++)
	{
		const FStateTreeExternalDataDesc& ItemDesc = ExternalDataDescs[Index];
		if (ItemDesc.Struct != nullptr)
		{
			if (ItemDesc.Struct->IsChildOf(UWorldSubsystem::StaticClass()))
			{
				UWorldSubsystem* Subsystem = World->GetSubsystemBase(Cast<UClass>(const_cast<UStruct*>(ItemDesc.Struct.Get())));
				OutDataViews[Index] = FStateTreeDataView(Subsystem);
			}
			else if (ItemDesc.Struct->IsChildOf(UActorComponent::StaticClass()))
			{
				UActorComponent* Component = Owner->FindComponentByClass(Cast<UClass>(const_cast<UStruct*>(ItemDesc.Struct.Get())));
				OutDataViews[Index] = FStateTreeDataView(Component);
			}
			else if (ItemDesc.Struct->IsChildOf(APawn::StaticClass()))
			{
				APawn* OwnerPawn = (AIOwner != nullptr) ? AIOwner->GetPawn() : Cast<APawn>(Owner);
				OutDataViews[Index] = FStateTreeDataView(OwnerPawn);
			}
			else if (ItemDesc.Struct->IsChildOf(AAIController::StaticClass()))
			{
				AAIController* OwnerController = AIOwner;
				OutDataViews[Index] = FStateTreeDataView(OwnerController);
			}
			else if (ItemDesc.Struct->IsChildOf(AActor::StaticClass()))
			{
				AActor* OwnerActor = (AIOwner != nullptr) ? AIOwner->GetPawn() : Owner;
				OutDataViews[Index] = FStateTreeDataView(OwnerActor);
			}
		}
	}

	return true;
}

ここで注意しないといけないのが、上記処理をみるとUStateTreeComponentSchema
取得できるActorやComponentは全てOwnerを経由したものであるということです。
つまり、Ownerに関連していない他キャラなどの参照は行えません。
そういったデータを参照するときはEvaluaterを使用するのが良いでしょう。

3. State

状態(階層)を定義します。遷移する条件に応じてアクティブなStateを切り替えることで、
状況に応じたTaskなどの処理を行うことができます。
image.png
Stateのパラメーターを見ていきましょう
image (9).png

名前 説明
Name Stateの名前
Type Stateのタイプ
Selection Behavior Stateがアクティブになる時の挙動
Parameters このState以下でバインド可能な一時変数
Enter Conditions このStateをアクティブの判定に使用されるConditionのリスト
Tasks このStateがアクティブな場合に処理されるTaskのリスト
Transitions このStateがアクティブな時に他Stateへ移動する場合の遷移先リスト

Type

このStateがどういった挙動を行うかの設定です。

  • State
    Taskや子供Stateを含む通常のState

  • Group
    子Stateだけを含むStateです。これ自体に処理を行う能力はありません。
    ※そのためTaskは設定できません、途中でタイプを切替してもCompile時に破棄されます

    image (10).png

  • Linked
    StateTree内の別Stateのフローを流用したい場合に使用します。
    Linked Subtreeには流用したいState(TypeはSubtree設定のものOnly)を指定します
    ※Group同様、このState自体にTaskを設定することはできません。

    image (11).png

  • Subtree
    Linked設定のStateから指定することができるStateです。
    このStateは意図的に遷移しない限り、通常のフローで遷移することはありません

    image (12).png

  • Linked Asset
    別のSubTreeアセットを使用することが可能です。
    ※使用できるのは、別のStateTreeのみです

    image (13).png

※Linked と SubTreeについての補足 使用例を説明すると
  • 戦闘中はエネミーに向かって移動し、到着したらランダムなアクション
  • 非戦闘中はプレイヤーに向かって移動し、到着したらランダムなアクション

といったすごいアレな内容のフローを用意したい時があるとします。
これを何も考えずに作ります。
image (14).png

今は単純な移動とランダムなアクション、と言っているので問題ないですが
これが複雑な行動になっていくと、それぞれのStateに都度タスクや
子Stateを追加していくのは正直なところ手間です。

そこでLinkedとSubTreeを活用します、
先ほどの画像の処理をSubTreeを使って変更するとこのようになります。

image (15).png

変更した内容としては

  • それぞれのStateで行っていた処理を別のフローで用意
  • 呼び出し元のStateをLinked設定に変更して移動先のStateをSubTreeに変更

これで今後拡張があっても、共通処理になっているので
SubTreeの方を変更するだけで済みます。

ちなみに、SubTreeのStateでは呼び出し元(Linked)のStateから
渡すパラメーターを引数の様に定義できます。

これを利用すれば、同じフローで条件を変更して処理をさせるといったことも可能です
image (16).png

image (17).png

Selection Behavior

  • None
    Transitionの遷移先候補に指定できません、また、通常の遷移の対象にもなりません。

  • TryEnterState
    子Stateがいても、まずこのStateが選択されます。
    子Stateに遷移したい場合は別途Transitionを指定する必要があります。

  • TrySelectChildrenInOrder
    最初に遷移可能な子Stateを選択しようとします。
    子Stateが存在しない場合は、このStateが選択されます

  • TryFollowTransitions
    Stateに遷移する時、代わりにTransitionをトリガーして有効なStateに遷移します

4. Transition

Stateなどから、他のStateに遷移したい場合の条件などを設定するための機能です、
こちらは以下のパターンの使用方法があります。

  • State 経由のTransition
  • Instance Data 経由のTransition

State経由のTransition

image (18).png
各パラメーターの説明を記載します。

Trigger

遷移の判定タイミングです。

On State Completed Stateが終了した時に遷移判定が行われます
On State Succeeded Stateが成功終了した時に遷移判定が行われます
On State Failed Stateが失敗終了した時に遷移判定が行われます
On Tick 毎フレーム遷移判定が行われます
On Event 特定のEvent(Gameplay Tag)が発行された時に遷移判定が行われます

OnEventのトリガーとなるイベントは各Nodeから呼び出し可能です。
image (19).png
C++はFStateTreeInstanceData(FStateTreeInstanceStorage)EventQueueの関数を呼び出します。

struct FStateTreeInstanceStorage
{
	/** Events */
	UPROPERTY()
	FStateTreeEventQueue EventQueue;
};

struct STATETREEMODULE_API FStateTreeEventQueue
{
	void SendEvent(const UObject* Owner, const FGameplayTag& Tag, const FConstStructView Payload = FConstStructView(), const FName Origin = FName());
};

UStateTreeComponentを使用している場合は下記関数でも呼び出し可能です。

class UStateTreeComponent
{
	/** Sends event to the running StateTree. */
	UFUNCTION(BlueprintCallable, Category = "Gameplay|StateTree")
	void SendStateTreeEvent(const FStateTreeEvent& Event);

	/** Sends event to the running StateTree. */
	void SendStateTreeEvent(const FGameplayTag Tag, const FConstStructView Payload = FConstStructView(), const FName Origin = FName());
};

Transition To

遷移先の指定を行います。特定のStateを指定する他、遷移の仕方の指定をすることもできます
image (20).png

None 何もしません
Next State 同階層の次(下)にあるStateに移動します
Next Selectable State 同階層の次(下)にある、遷移可能なStateに移動します
Tree Succeeded StateTreeを成功扱いで終了させます。
Tree Failed StateTreeを失敗扱いで終了させます。
StateTree内のState名 指定したStateに遷移します

Conditions

遷移条件です。複数指定可、詳細は後述するNodeのConditionをご参照ください。

Priority

※On Tick, On Eventのみ
遷移の優先度です。同じ優先度が複数ある場合は登録順に実行されます。

Delay Transition

※On Tick, On Eventのみ
trueを指定すると、遷移実行がDelay Duration( + Delay Random Variance)の秒数だけ遅延します。
遅延中に他のTransitionが条件を満たした場合は、そちらが優先されます。

Delay Duration

※On Tick, On Eventのみ
遷移の実行を遅らせる時間(秒)

Delay Random Variance

※On Tick, On Eventのみ
Delay Durationに加算する、遅延時間のランダムな範囲の最大値(秒)です。

Instance Data経由のTransition

TransitionはState外、各Nodeからのリクエスト可能です。
image (21).png

C++ではFStateTreeInstanceData(FStateTreeInstanceStorage)経由でリクエストします。

struct FStateTreeInstanceStorage
{
	void AddTransitionRequest(const UObject* Owner, const FStateTreeTransitionRequest& Request);
};

5. Node

StateTree内で使用する要素で、以下の派生があります。

  1. Task
    基本はActiveなState内で行う「処理」を定義するものです
    ※Global Taskという、StateTree実行中にStateに関係なく動作するTaskもあります
  2. Evaluator
    SchemaのContextやExternal Data、StateTreeのParameterで参照できない、
    外部のデータをStateTreeで参照できるようにするものです。
  3. Condition
    StateのTransition全般で「条件」を定義するものです。

これら全てをまとめて「Node」と呼称します。
実際、各種使用される定義の派生は全てNodeBaseを継承しています。

C++ベースの継承

BPベースの継承

まずそれぞれの話に移る前に、Nodeで共通部分の説明をします。

  • Instance Data
  • Property Bind
  • External Data(外部データの参照の設定)

Instance Data

各Nodeは、

  • FStateTreeNodeBaseを継承した実処理を記載する定義
  • NodeのRuntime中のデータを保存するInstanceData用の定義

で構成されます ※BPベースの場合は考慮の必要はありません

デフォルトで用意されている、DelayTaskを例に見てみます。

USTRUCT()
struct STATETREEMODULE_API FStateTreeDelayTaskInstanceData
{
	GENERATED_BODY()
	
	/** Delay before the task ends. */
	UPROPERTY(EditAnywhere, Category = Parameter, meta = (EditCondition = "!bRunForever", ClampMin="0.0"))
	float Duration = 1.f;
	
	/** Adds random range to the Duration. */
	UPROPERTY(EditAnywhere, Category = Parameter, meta = (EditCondition = "!bRunForever", ClampMin="0.0"))
	float RandomDeviation = 0.f;
	
	/** If true the task will run forever until a transition stops it. */
	UPROPERTY(EditAnywhere, Category = Parameter)
	bool bRunForever = false;

	/** Internal countdown in seconds. */
	float RemainingTime = 0.f;
};

USTRUCT(meta = (DisplayName = "Delay Task"))
struct STATETREEMODULE_API FStateTreeDelayTask : public FStateTreeTaskCommonBase
{
	GENERATED_BODY()

	using FInstanceDataType = FStateTreeDelayTaskInstanceData;
	
	FStateTreeDelayTask() = default;

	virtual const UStruct* GetInstanceDataType() const override { return FInstanceDataType::StaticStruct(); }

	virtual EStateTreeRunStatus EnterState(FStateTreeExecutionContext& Context, const FStateTreeTransitionResult& Transition) const override;
	virtual EStateTreeRunStatus Tick(FStateTreeExecutionContext& Context, const float DeltaTime) const override;
};

コードを見るとわかりますが、

  • FStateTreeDelayTaskFStateTreeNodeBaseを継承して、時間カウントの実処理の定義
  • FStateTreeDelayTaskInstanceDataが処理で使用するパラメーターを保持

となっています。

何故このような構成になっているのかは、
ActiveではないStateのメモリを無駄に確保しないようにするためと思われます。

InstanceDataの指定はFStateTreeNodeBaseのGetInstanceDataType関数で行います。
UStructを渡していますが、派生であるUClassの指定も可能です

/** @return Struct that represents the runtime data of the node. */
virtual const UStruct* GetInstanceDataType() const { return nullptr; };

ちなみに全てのパラメーターをInstanceDataに定義する必要はなく、
設定パラメーターなどRuntime中に変化しないものは、実処理側の宣言で問題ありません

Property Bind

Evaluatorなどを作って頂くとわかりやすいですが、
StateTree内のEvaluatorやTaskなどのパラメーターを他のNodeで参照できる機能です。
image (22).png

この機能はNodeの種類によってバインドできる対象が異なります。

Nodeの種類 バインド可能なデータ
Condition(StateのEnter条件) Globalなデータの他、親StateのTaskのパラメーター
Condition(Transitionの条件) Globalなデータの他、親Stateと現在ActiveなStateのTaskのパラメーター
Task Globalなデータの他、親StateのTask、現在ActiveなStateの自分より前に実行されるTaskのパラメーター

親StateのTaskで処理した結果を子StateのEnter条件に使いたい、が可能ということです。
また、Propertyのカテゴリで以下の様にPropetyBindの制限をかけることもできます。

  1. Categoryに”Input”を指定

    // 例:DrawDebugTextTaskのReferenceActor
    USTRUCT()
    struct STATETREEMODULE_API FStateTreeDebugTextTaskInstanceData
    {
    	GENERATED_BODY()
    
    	/** Optional actor where to draw the text at. */
    	UPROPERTY(EditAnywhere, Category = "Input", meta=(Optional))
    	TObjectPtr<AActor> ReferenceActor = nullptr;
    };
    

    StateTree上の表示を見ると、バインドでのみ指定ができることが分かります。
    DrawDebugTextTaskの場合はOptionalが設定されていますが、
    通常Inputカテゴリに指定されているパラメーターはバインドされていない時は
    エラーになりますのでご注意ください。
    image (24).png

  2. Categoryに”Output”を指定
    例:Evaluator(BPで定義しているのに特に理由はありません)
    image (25).png
    image (26).png

    Outputを指定すると、StateTree上ではBind出力専用になっており、 値を変更することはできません。
    image (27).png

  3. 指定なし(or 上記以外)
    例:DelayTask

    USTRUCT()
    struct STATETREEMODULE_API FStateTreeDelayTaskInstanceData
    {
    	GENERATED_BODY()
    	
    	/** Delay before the task ends. */
    	UPROPERTY(EditAnywhere, Category = Parameter, meta = (EditCondition = "!bRunForever", ClampMin="0.0"))
    	float Duration = 1.f;
    
    	~~	
    };
    

    StateTree内で設定もできますし、他のデータでバインドも可能です
    image (28).png

External Data(外部データの参照の設定)

Schemaの説明でも記載しましたが、
StateTree外の情報を簡単に参照できるようにするための機能です。
※BPベースでは使用できません。

使用方法はFStateTreeNodeBaseのLink関数をオーバーライドして、
TStateTreeExternalDataHandleを使ってHandleを定義するだけです。

struct FTestSTTask_Test : public FStateTreeTaskBase
{
		virtual bool Link(FStateTreeLinker& Linker) override;

		// ここで使用できる型はSchemaで指定した型のみ!
		TStateTreeExternalDataHandle<UStateTreeComponent> CompHandle;
};
virtual bool FTestSTTask_Test::Link(FStateTreeLinker& Linker)
{
	Linker.LinkExternalData(CompHandle);
	return true;
}

あとはFStateTreeExecutionContextGetExternalData()で参照するだけです。
ポインタも可能。

EStateTreeRunStatus FTestSTTask_Test::EnterState(FStateTreeExecutionContext& Context,
	const FStateTreeTransitionResult& Transition) const
{	
	const UStateTreeComponent& Component = Context.GetExternalData(CompHandle);
	return FStateTreeTaskBase::EnterState(Context, Transition);
}

5-1. Task

TaskはアクティブなState内で動作する通常のTaskと、
Stateの状態に関係なく動作するGlobalTaskの2種類があります。
こちらは呼び出し方が違うだけで、Task自体は共通で使用可能です。
image (29).png

Taskの処理のタイミング

GrobalTaskは基本的にStateTree実行中、常に実行されます。
通常のTaskでは以下のタイミング別に処理を呼び出すことができます
image (30).png

image (31).png

イベント名 タイミング
EnterState Stateの開始時に呼ばれます
Tick StateがActiveな時、毎Tick呼ばれます
ExitState Stateの終了時に呼ばれます
StateCompleted Stateが完了した時に呼ばれますが、
条件付きのTransitionによる遷移など特定の状況下では呼ばれないので
終了時の解放、クリア処理などを呼ぶのは避けた方が良いです。

また、タスクの処理タイミングは設定によっても変化するため注意が必要です。
image (31).png

  • bShouldStateChangeOnReselect
    trueの場合、タスクは所属しているStateが前にアクティブだった場合でも、EnterState/ExitState を呼び出します。

  • bShouldCallTick
    Tickを呼ぶかどうかのフラグ
    ※falseだとバインドされているプロパティの情報が更新されないので注意してください

  • bShouldCallTickOnlyOnEvents
    trueに設定すると、Tick()はイベントがあるときのみ呼び出されます。
    bShouldCallTickStateがtrueの場合は効果なし。
    ※Tickしない場合はバインドプロパティの情報が更新されないので注意してください

TaskはStateの終了判定に関係している

TaskはStateの完了判定に関わっており、StateのTransitionにある

  • OnStateCompleted
  • OnStateSucceeded
  • OnStateFailed

上記タイミングはState内にあるTaskの終了に応じて発行されます
(複数ある場合はどれか1つが終了するとトリガーされます)
※GlobalTaskの場合はStateTree自体の終了判定になります

挙動としてはDelayTaskがわかりやすいです、
Durationで指定された時間が経過するとOnStateSucceededで終了します。
image (32).png
ただし、全てのTaskがこの限りではありません。
下記画像で使用しているDebugTextTaskは終了しないTaskのため、
この場合はState1に留まり続けてしまうため注意が必要です。
image (33).png

5-2. Evaluator

ContextやStateTreeのParameter以外の、使用したい外部データを取得するのに使用します。
データの更新は以下のタイミングで行えるようになっています。
image (34).png

関数名 タイミング
Tick StateTree実行中毎Tick呼ばれる
TreeStart StateTree開始時
TreeStop StateTree終了時

上記好きなタイミングで取得処理を記載し、EvaluatorのInstanceDataの変数に反映して
変数のUPropertyでOutputを指定すればStateTree内で参照が可能になります

5-3. Condition

Stateの遷移に関連する場所で使用する判定を定義します。
使用箇所は以下

  • StateのEnter条件
    image (35).png

  • Transitionの遷移条件
    image (36).png

Conditionは判定の結果を返す関数のみです。
image (37).png

struct STATETREEMODULE_API FStateTreeConditionBase : public FStateTreeNodeBase
{

	virtual bool TestCondition(FStateTreeExecutionContext& Context) const { return false; }
};

おまけ

StateTreeにはデバッグ機能が存在しており、StateTreeの遷移を録画することが可能です。

表示されていない場合はツールバー>Window>Debuggerで表示してください
image (38).png

  1. シュミレーションの再生、停止
  2. StateTreeのトレース録画の開始、停止
  3. 録画したトレースの削除
  4. トレースした対象Actorと実行番号
  5. 録画時のタイムラインとState表示
  6. 選択している4と5のシークバーのタイミングにおけるStateの状態

その他、Stateにはブレークポイントも配置できます。
Untitled (1).png

最後に

次回があれば、今回はみでたFStateTreeExecutionContextと合わせて
StateTreeの進行について記事にできればと思います。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0