Best Practices for State Tree: Looping States and Instance Data

Hello!

I am working on a State Tree implementation of a system that involves processing an array of instanced objects that have a function that is executed to produce a result of Success, Fail or Not Applicable. This array is processed until a result of Success or Fail is reached. This array is processed multiple times during gameplay. I want to upgrade the system so that it can handle branches on Success or Fail while having UI/UX that makes it clear what is happening when developer set it up. I was intrigued by using State Tree since the hierarchical state tree implementation allows for the branching behavior I want and comes with a nice editor for setting up the assets.

There is one blocker issue I am encountering with the State Tree implementation. The current implementation relies on the instanced objects being able to store state. The benefit of instanced objects is they are easily accessible and can be manipulated when certain gameplay events occur to store data on the instance. I know that State Tree uses instance data that is stored external from the State Tree asset. However, I noticed that the instance data is reset on starting the tree. I thought I could work around this reset issue by just starting the tree once and having the Root state wait for an Event transition, but I was losing the instance data anyway when looping back to the Root state when FStateTreeExecutionContext::UpdateInstanceData is called and the InstanceData array is shrunk to contain only the values for the remaining active frame. So if I have states A->B->C, and C loops back to A, the instance data for B and C are lost.

My desired State Tree implementation is to have each state contain one Task that has the existing logic from the instanced objects in my current solution. If the state is successful, execution jumps to the next child state. If failed, execution jumps to the next sibling state. When complete, execution jumps back to the Root and waits for the next trigger Event. When a state reenters, the instance data from the initial execution persists.

Is there any existing or planned features of State Tree to help me get the solution I want? I feel like the existing implementation is very close. It’s just that the instance data is automatically cleared out when I need it to remain available until the tree is stopped. I’m most interested in a solution that makes it easy to setup logic in a blueprint asset. This means that the BP variables are what persist instead of needing to use an external asset like a component or blackboard asset.

Thanks!

-Rick

We have introduced the idea of persistent runtime execution data for nodes (tasks/conditions/etc.) in StateTree. I believe it was submitted in September and should be a part of 5.7 when it releases. I do not anticipate this being exposed to BP anytime soon, but step one is making it possible in general.

You create the runtime data similarly to node instance data and can set the type with something like this:

using FExecutionRuntimeDataType = FStateTreeNodeExecutionRuntimeData; // <- this would be the struct you created for runtime data
using FInstanceDataType = FStateTreeNodeInstanceData; // <- usual node instance data struct
 
virtual const UStruct* GetExecutionRuntimeDataType() const override { return FExecutionRuntimeDataType::StaticStruct(); }
virtual const UStruct* GetInstanceDataType() const override { return FInstanceDataType::StaticStruct(); }

You can then get the execution runtime data for the node from the ExecutionContext while the tree is running. It should allow for a task or condition to maintain some data between runs of the node. One of our initial ideas for this was something like cooldowns or gates which need to be hit X amount of times before allowing execution.

-James

1 Like