Movement with Mass Framework
How to process movement with Mass Framework. Mass Framework is an ECS for managing crowds and traffic, available since Unreal Engine 5.0. Engine code excerpts below were taken from UE 5.3 and their signatures updated to the Unreal Engine 5.8 API - check the source of your engine version for implementation details.

Processing Movement with MassGameplay built-in resources
Movement Trait
Built-in
MassGameplayTrait located AtUE5/Engine/Plugins/Runtime/MassGameplay/Source/MassMovementImage
.hUCLASS(meta = (DisplayName = "Movement")) class MASSMOVEMENT_API UMassMovementTrait : public UMassEntityTraitBase { GENERATED_BODY() protected: virtual void BuildTemplate(FMassEntityTemplateBuildContext& BuildContext, const UWorld& World) const override; UPROPERTY(Category="Movement", EditAnywhere) FMassMovementParameters Movement; };.cppvoid UMassMovementTrait::BuildTemplate(FMassEntityTemplateBuildContext& BuildContext, const UWorld& World) const { FMassEntityManager& EntityManager = UE::Mass::Utils::GetEntityManagerChecked(World); BuildContext.RequireFragment<FAgentRadiusFragment>(); BuildContext.RequireFragment<FTransformFragment>(); BuildContext.AddFragment<FMassVelocityFragment>(); BuildContext.AddFragment<FMassForceFragment>(); const FConstSharedStruct MovementFragment = EntityManager.GetOrCreateConstSharedFragment(Movement); BuildContext.AddConstSharedFragment(MovementFragment); }Check Built-in Movement processor to get knwoledge of processing the movement from the trait
Fragments for movement
As you can see, the built-in
Movement TraitofMassGameplayplugin works with the 4 following fragments:FMassVelocityFragmentLocated at
UE5/Engine/Plugins/Runtime/MassGameplay/Source/MassMovementUSTRUCT() struct MASSMOVEMENT_API FMassVelocityFragment : public FMassFragment { GENERATED_BODY() FVector Value = FVector::ZeroVector; };FMassForceFragmentLocated at
UE5/Engine/Plugins/Runtime/MassGameplay/Source/MassMovementUSTRUCT() struct MASSMOVEMENT_API FMassForceFragment : public FMassFragment { GENERATED_BODY() FVector Value = FVector::ZeroVector; };FAgentRadiusFragmentLocated at
UE5/Engine/Plugins/Runtime/MassGameplay/Source/MassCommonUSTRUCT() struct MASSCOMMON_API FAgentRadiusFragment : public FMassFragment { GENERATED_BODY() UPROPERTY(EditAnywhere, Category = "") float Radius = 40.f; };FTransformFragmentLocated at
UE5/Engine/Plugins/Runtime/MassGameplay/Source/MassCommonUSTRUCT() struct MASSCOMMON_API FTransformFragment : public FMassFragment { GENERATED_BODY() const FTransform& GetTransform() const { return Transform; } void SetTransform(const FTransform& InTransform) { Transform = InTransform; } FTransform& GetMutableTransform() { return Transform; } protected: UPROPERTY(Transient) FTransform Transform; };Movement Processor
Built-in
MassGameplayProcessor located atUE5/Engine/Plugins/Runtime/MassGameplay/Source/MassMovementNow, as you know the fragments and their values, you can check how they are used for automatical processing of movement. The point is, in a case of using the built-in
Movement Trait(MassGameplay), all you need for processing movements is to set (update) the traits values. You can do that e.g. from a custom Mass processor..hUCLASS() class MASSMOVEMENT_API UMassApplyMovementProcessor : public UMassProcessor { GENERATED_BODY() public: UMassApplyMovementProcessor(); protected: virtual void ConfigureQueries(const TSharedRef<FMassEntityManager>& EntityManager) override; virtual void Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context) override; private: FMassEntityQuery EntityQuery; };.cppUMassApplyMovementProcessor::UMassApplyMovementProcessor() : EntityQuery(*this) { ExecutionFlags = (int32)EProcessorExecutionFlags::All; ExecutionOrder.ExecuteInGroup = UE::Mass::ProcessorGroupNames::Movement; ExecutionOrder.ExecuteAfter.Add(UE::Mass::ProcessorGroupNames::Avoidance); } void UMassApplyMovementProcessor::ConfigureQueries(const TSharedRef<FMassEntityManager>& EntityManager) { EntityQuery.AddRequirement<FMassVelocityFragment>(EMassFragmentAccess::ReadWrite); EntityQuery.AddRequirement<FTransformFragment>(EMassFragmentAccess::ReadWrite); EntityQuery.AddRequirement<FMassForceFragment>(EMassFragmentAccess::ReadWrite); EntityQuery.AddTagRequirement<FMassOffLODTag>(EMassFragmentPresence::None); EntityQuery.AddConstSharedRequirement<FMassMovementParameters>(EMassFragmentPresence::All); } void UMassApplyMovementProcessor::Execute(FMassEntityManager& EntityManager, FMassExecutionContext& Context) { // Clamp max delta time to avoid force explosion on large time steps (i.e. during initialization). const float DeltaTime = FMath::Min(0.1f, Context.GetDeltaTimeSeconds()); QUICK_SCOPE_CYCLE_COUNTER(HighRes); EntityQuery.ForEachEntityChunk(Context, [DeltaTime](FMassExecutionContext& Context) { const int32 NumEntities = Context.GetNumEntities(); const FMassMovementParameters& MovementParams = Context.GetConstSharedFragment<FMassMovementParameters>(); const TArrayView<FTransformFragment> LocationList = Context.GetMutableFragmentView<FTransformFragment>(); const TArrayView<FMassForceFragment> ForceList = Context.GetMutableFragmentView<FMassForceFragment>(); const TArrayView<FMassVelocityFragment> VelocityList = Context.GetMutableFragmentView<FMassVelocityFragment>(); for (int32 EntityIndex = 0; EntityIndex < NumEntities; ++EntityIndex) { FMassForceFragment& Force = ForceList[EntityIndex]; FMassVelocityFragment& Velocity = VelocityList[EntityIndex]; FTransform& CurrentTransform = LocationList[EntityIndex].GetMutableTransform(); // Update velocity from steering forces. (Force default value is ZeroVector) Velocity.Value += Force.Value * DeltaTime; FVector CurrentLocation = CurrentTransform.GetLocation(); CurrentLocation += Velocity.Value * DeltaTime; CurrentTransform.SetTranslation(CurrentLocation); } }); }
Anything unclear?
Let us know!