Hi.
I’m trying to understand Mass, builing an AI utilizing Mass Statetree Zonegraph etc.
I’m trying to trigger a state change based on an ingame event. Like being shot etc, but i’m usure of the correct approach.
I’ve build a Processor
A custom trait & tag. Now my Blueprint layer can add a tag to a given entity when a given event happens. The Processor processes entities with my custom trait, tag and the statetree fragment. My intention is to trigger a statechange on the entity.
Now my first approach was to call SendEvent
on the statetree - this did work, i could change states on my entities. Now my issue was that i very quickly got a ‘Too many events’ error on my tree leading me to think this is not the correct approach? I atleast don’t understand when events are cleared from the tree…
My second approach has been to introduce a bool parameter on the tree. For now it’s called IsIdle
, which would determine which state transition to take. Now my issue is that if i call SetParameters on my statetree context it is never persisted so this approach doesn’t work.
Could anyone lead me in the correct direction?
This is my processors code:
void UStateTreeEventMassProcessor::Initialize(UObject& Owner)
{
SignalSubsystem = UWorld::GetSubsystem<UMassSignalSubsystem>(Owner.GetWorld());
}
void UStateTreeEventMassProcessor::ConfigureQueries()
{
// Should be every entity with this tag, but any of the defined fragments
EntityQuery.AddTagRequirement<FTriggerStateEventTag>(EMassFragmentPresence::All);
EntityQuery.AddRequirement<FMassStateTreeInstanceFragment>(EMassFragmentAccess::ReadWrite);
EntityQuery.AddConstSharedRequirement<FMassStateTreeSharedFragment>();
EntityQuery.AddConstSharedRequirement<FStateTreeEventSharedFragment>();
EntityQuery.AddSubsystemRequirement<UMassStateTreeSubsystem>(EMassFragmentAccess::ReadWrite);
EntityQuery.RegisterWithProcessor(*this);
}
void UStateTreeEventMassProcessor::Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context)
{
UWorld* World = EntityManager.GetWorld();
//TODO: Capture the instances needed for execution context in the []
EntityQuery.ForEachEntityChunk(EntityManager, Context,
[SignalSubsystem = SignalSubsystem.Get(), &EntityManager, World](FMassExecutionContext& Context)
{
UMassStateTreeSubsystem& MassStateTreeSubsystem = Context.GetMutableSubsystemChecked<UMassStateTreeSubsystem>(World);
const int32 NumEntities = Context.GetNumEntities();
const TArrayView<FMassStateTreeInstanceFragment> StateList = Context.GetMutableFragmentView<FMassStateTreeInstanceFragment>();
//TODO: This line crashes
const FMassStateTreeSharedFragment& SharedStateTree = Context.GetConstSharedFragment<FMassStateTreeSharedFragment>();
const FStateTreeEventSharedFragment& SharedEvent = Context.GetConstSharedFragment<FStateTreeEventSharedFragment>();
const UStateTree* StateTree = SharedStateTree.StateTree;
//Loop over every entity in the current chunk and do stuff!
for (int32 EntityIndex = 0; EntityIndex < NumEntities; ++EntityIndex)
{
const FMassStateTreeInstanceHandle& StateTreeInstance = StateList[EntityIndex].InstanceHandle;
FStateTreeInstanceData* InstanceData = MassStateTreeSubsystem.GetInstanceData(StateTreeInstance);
FMassStateTreeExecutionContext StateTreeContext(MassStateTreeSubsystem, *StateTree, *InstanceData, EntityManager, *SignalSubsystem, Context);
const FMassEntityHandle Entity = Context.GetEntity(EntityIndex);
// UE_LOG(LogTemp, Display, TEXT("Entity [%s]: "), *entity.DebugGetDescription());
auto Properties = StateTreeContext.GetStateTree()->GetDefaultParameters();
auto Idle = Properties.GetValueBool(FName("IsIdle"));
UE_LOG(LogTemp, Display, TEXT("Entity [%s]: IsIdle: %hhd"), *Entity.DebugGetDescription(), Idle.GetValue())
// FInstancedPropertyBag Properties;
Properties.SetValueBool(FName("IsIdle"), true);
StateTreeContext.SetParameters(Properties);
// StateTreeContext.SendEvent(SharedEvent.Event);
EntityManager.Defer().RemoveTag<FTriggerStateEventTag>(Entity);
}
});
}