Unreal 5.1: Mass Statetree, managing state.

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 SendEventon 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);
        }
    });
}