Has anyone found a way to assign / update values to a StateTree parameter?
Yeah it get’s complicated. You have to use your evaluator in combination with your actor to pass information back and forth.
So you can’t update the parameter specifically, you use those as default values, pass them into your evaluator and then adjust there.
Hopefully that makes sense.
Does that work, sure, does it make sense? That is another story. The current workflow I have found makes parameters useless. If I am going to have default variables or anything I need to update or set from outside the actor using state tree I have to set up a bunch of variables on the actor. Was hoping this would be more like Blackboard and Behaviour Tree than needing to make an external data housing component or the such.
Can you elaborate? I tried what you described but it’s not working.
The parameter in state tree:
The variable in the evaluator:
Changing the variable’s value in the evaluator did not update the corresponding state tree parameter.
As far as I know, parameters are “set once, read only” data. If you want runtime parameters, you have to
- Make a task, global task or evaluator where the variable will be calculated.
- Make your variable in this object.
- Make it instance editable (click on the eye), or in C++ make it VisibleAnywhere or EditAnywhere (I believe either should work)
- Move it to the Output category. It must be spelled correctly, it won’t work otherwise.
- Add the task or evaluator object to the tree. If you did everything correctly, you should see a gray bubble saying OUT and you won’t be able to bind to this variable (since you’ll be writing to it)
- Set this variable only from this object.
Other tasks and evaluators can then bind themselves to read from this variable.
This is a poorly documented feature, but I got it to work doing these steps.
// for 5.4
template <typename T>
EPropertyBagResult SetStateTreeReferenceParameter(FStateTreeReference& StateTreeReference, const FName ValueName, const T& Value)
{
auto& InstancedPropertyBag = StateTreeReference.GetMutableParameters();
const auto PropertyBagResult = InstancedPropertyBag.SetValueStruct(ValueName, Value);
ensure(PropertyBagResult == EPropertyBagResult::Success);
// set overriden flag
if (auto* Desc = InstancedPropertyBag.FindPropertyDescByName(ValueName))
{
StateTreeReference.SetPropertyOverridden(Desc->ID, true);
}
return PropertyBagResult;
}
This is working but emitting warnings, and I didn’t test it in production.
Is making the variables on the actor itself an option or does it HAVE to be on the state tree?
Because on the context actor its easy peasy to read and write variables.
I need almost exactly this, just with the twist that I need to “write” into the evaluator’s variable, not just “read”.
Is this even possible?
Edit:
For anyone out there interested in the answer of my question:
It’s not possible manipulating the value of a variable of an Evaluator via a StateTree-Task. People I have talked with told me - if it is realy needed - to create global variables e.g in the Player’s ActorBP and to create a reference to that Actor in the “Context”-Section of the StateTree so that the tasks or any other area of StateTree can read/write those values via the vairable in that Actor.
Thank you!
This is the method that I have gone with. It works great because in all of the bindings (ie. enter conditions), you can reference variables from the context actor, all while being able to modify them anywhere necessary. HIGHLY recommend this approach.
Are you on 5.4? Have you tried using FStateTreePropertyRef for your tasks that you want to update the parameters? There are limitations on what can be used with FStateTreePropertyRef and the allowed types can be found in StateTreePropertyRef.h. It would require using C++ as the property refs are not Blueprintable.
-James
I just started using FStateTreePropertyRef today with UE 5.5 for writing values to variables from tasks to be used by later tasks and it works quite well. I used FStateTreeRunEnvQueryTask as reference and changed my tasks to be derived from FStateTreeTaskCommonBase instead of UStateTreeNodeBlueprintBase, which is viable for me since I’m not using blueprints for several of my tasks. When you create your InstanceData struct with the FStateTreePropertyRefs, you simply promote those to a parameter on the Task that will be writing to your variable, then reference the parameter in later tasks to get the modified value.
There are some things that do not work as expected. For the InstanceDataType structs, you cannot specify the Category of the FStateTreePropertyRef to be Output, otherwise you cannot promote/bind that to a parameter on the state tree. So for example, a runtime writable float would need to be declared like this without the Output category in order to be able to write to it:
UPROPERTY(EditAnywhere, Category = Out, meta = (RefType = "float"))
FStateTreePropertyRef AfterAttackDelay;
You don’t get the nice UI showing that the parameter is an output parameter this way unfortunately.
I can see how this can be confusing with using the property refs. The ref is inherently both an input and an output (possibly, but not a requirement as the ref to an array is faster than a copy).
Is the issue largely that the property ref does not show in the consuming task’s binding list as TaskName->AfterAttackDelay
like you see for an output parameter? The data from the ref is still able to be gathered as it is stored as a parameter on the tree, whereas the data from a task would be destroyed once the task ended if nothing was bound to the Output variable.
This video explains it nicely:
Also, Property Refs that @James.Keeling recommended are now exposed to Blueprints and are explained in the video at 16:55.
I’m trying to better understand some of the code surrounding State Tree parameters and have run into a roadblock.
Example code:
auto& Params = StateTreeRef.GetMutableParameters();
auto a = Params.GetValueBool(TEXT("bIsDaytime")).GetValue();
Params.SetValueBool(TEXT("bIsDaytime"), false);
auto b = Params.GetValueBool(TEXT("bIsDaytime")).GetValue();
auto& Params2 = StateTreeRef.GetMutableParameters();
auto c = Params2.GetValueBool(TEXT("bIsDaytime")).GetValue();
bIsDaytime is a State Tree parameter originally set to true.
a returns true, as expected.
b returns false, as expected after SetValueBool.
However, c returns true. It seems like GetMutableParameters only returns a copy of the parameters, so any Set function calls made from Params isn’t reflected in the State Tree. Calling GetMutableParameters a second time doesn’t reflect any changes made, which means the changes are only instanced to Params.
Why is this? Doesn’t this defeat the point of GetMutable if I can’t make any changes to the actual State Tree parameters?
Ideally I would be able to make changes to the State Tree parameters, which would be reflected across all Tasks and references to that State Tree. An easy workaround would be to create a bIsDaytime bool in the Actor/AIController, but I would rather have the data visually reflected within the State Tree as it should be.
Looking further into it, it seems that the actual runtime parameter values are stored in InstanceDataStorage.
Setting the parameter values is easy enough using SetGlobalParameters(FInstancedPropertyBag x), but GetGlobalParameters() returns FInstancedPropertyBag.Value and not the actual FInstancedPropertyBag? For whatever reason?
If GetGlobalParameters() returned a FInstancedPropertyBag, it would have been easy enough to call it, set the necessary individual values in the FInstancedPropertyBag copy, then call SetGlobalParameters() to overwrite the global parameters.
Since FInstancedPropertyBag contains all the functions for getting/setting the values in the PropertyBag, using GetGlobalParameters() is useless for easily changing the parameter values without changing the plugin code.
void UPlayerStateTreeComponent::SetInitialParameters()
{
const UStateTree* Tree = GetStateTree();
if (!Tree)
{
UE_LOG(LogTemp, Warning, TEXT("StateTree not valid."));
return;
}
InstanceData.GetMutableStorage().SetGlobalParameters(Tree->GetDefaultParameters());
FInstancedPropertyBag BagCopy = Tree->GetDefaultParameters(); // 複製一份可改的
BagCopy.SetValueFloat(TEXT("Health"), 100.f);
BagCopy.SetValueBool(TEXT("bIsReady"), true);
InstanceData.GetMutableStorage().SetGlobalParameters(BagCopy);
}
I success change statetree global parameter in unreal5.4
UPlayerStateTreeComponent is Inheritance by UStateTreeComponent
Health and bIsReady is parameter name
I tried that before. It seems to work for initializing the values in C++. The issue is when you want to change the values at runtime instead of only initializing them.
As far as I know, GetDefaultParameters() only gets what you set as the default in the State Tree within the Editor. Meaning if “Health” was initialized as 0.f in the Editor, GetDefaultParameters() will always return “Health” as 0.f. It can’t be used as a value getter for runtime logic.
If it’s only good for initialization, then you might as well just initialize your desired value in the State Tree within the Editor in the first place.
In my previous post, using GetGlobalParameters() returns an FInstancedPropertyBag.Value (which is an FConstStructView) instead of an FInstancedPropertyBag. Maybe there’s a way to construct an FInstancedPropertyBag from only its value, but I can’t be bothered to jump through so many hoops. For now I’m using my AIController to store “global” State Tree variables.
I suppose them being called State Tree “Parameters” and not “Variables” makes it obvious they should be read-only. But it would be nice to have them function as editable variables instead of only being used to pass in asset data.
void UPlayerStateTreeComponent::SetHptoST(float hp,float maxhp)
{
if (_ST)
{
FInstancedPropertyBag BagCopy = _ST->GetDefaultParameters();
BagCopy.SetValueFloat(TEXT("hp"), hp);
BagCopy.SetValueFloat(TEXT("maxhp"), maxhp);
InstanceData.GetMutableStorage().SetGlobalParameters(BagCopy);
}
else
{
UE_LOG(LogTemp, Warning, TEXT("StateTree not valid."));
return;
}
}
It can When It running I will use this funtion to change my HP
It really work
This code will change DefaultParameter->InstanceData.GetMutableStorage().SetGlobalParameters(BagCopy);
So when you use GetDefaultParameters() ,It will get the NewDefaultParameter
- _ST is UStateTree* _ST;