How to set a target location for MassAI with StateTree?

In the “First 60 mins with Mass” article a State Tree is setup such that each entity can wander around a NavMesh using the “Find Reachable Point” and “Follow Path” tasks.

My question is how would you go about giving each target a location to head towards, rather than select a random point?

The “Follow Path” task expects a FMassTargetLocation input, which is bound to something else it would seem, but I can’t seem to find a way to do that?

My first thought was “use an Event to pass the location across?” - which you can set FMassTargetLocation as the payload type, the outputs are the content and thus it can’t be bound to FMassTargetLocation.

Next, I figured I’d make a wrapper struct in the game plugin, use that to pass the payload over, and hopefully be able to bind that as the input - that just resulted in compile errors, as even after I added “MassAIBehavior” to the plugin build dependencies it was still giving my unresolved symbol errors for FMassTargetLocation ( __declspec(dllimport) class UScriptStruct * __cdecl Z_Construct_UScriptStruct_FMassTargetLocation ).

Finally, I figured “ok, maybe not optimal but what about just bouncing via a BP Task?” - but it seems you can’t create a variable of FMassTargetLocation type, so providing an output to bind too might well be impossible?

At this point I’m thinking it might just be easier to write my own version of the movement code and go from there, but wanted to check here in case I missed something obvious/non-obvious about this setup?

Hi.

I encountered that exact problem last year. What I did, was creating a simple task to convert FVector to FMassTargetLocation. I just place it before the move task and use the output to feed the move task.

Like this.

Here is the task itself. May it save you some time.

.h

USTRUCT()
struct FTaskVectorToMassLocationInstanceData
{
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, Category = "Parameter")
    FVector Location = FVector::ZeroVector;

    UPROPERTY(EditAnywhere, Category = "Output")
    FMassTargetLocation MassLocation;
};

USTRUCT(DisplayName = "VectorToMassLocation")
struct FTaskVectorToMassLocation : public FMassStateTreeTaskBase
{
    GENERATED_BODY()
    
    using FInstanceDataType = FTaskVectorToMassLocationInstanceData;

protected:
    virtual const UStruct* GetInstanceDataType() const override { return FInstanceDataType::StaticStruct(); }
    virtual EStateTreeRunStatus EnterState(FStateTreeExecutionContext& Context, const FStateTreeTransitionResult& Transition) const override;
};

.cpp

EStateTreeRunStatus FTaskVectorToMassLocation::EnterState(FStateTreeExecutionContext& Context, const FStateTreeTransitionResult& Transition) const
{
    FInstanceDataType& InstanceData = Context.GetInstanceData<FInstanceDataType>(*this);
    InstanceData.MassLocation.EndOfPathPosition = InstanceData.Location;
    return EStateTreeRunStatus::Succeeded;
}