// Copyright Epic Games, Inc. All Rights Reserved. #include "Movement/MassMovementProcessors.h" #include "MassCommonUtils.h" #include "MassCommandBuffer.h" #include "MassCommonFragments.h" #include "MassExecutionContext.h" #include "MassMovementFragments.h" #include "Math/UnrealMathUtility.h" #include "MassSimulationLOD.h" //----------------------------------------------------------------------// // UMassApplyForceProcessor //----------------------------------------------------------------------// UMassApplyForceProcessor::UMassApplyForceProcessor() : EntityQuery(*this) { ExecutionFlags = (int32)EProcessorExecutionFlags::AllNetModes; ExecutionOrder.ExecuteInGroup = UE::Mass::ProcessorGroupNames::ApplyForces; ExecutionOrder.ExecuteAfter.Add(UE::Mass::ProcessorGroupNames::Avoidance); } void UMassApplyForceProcessor::ConfigureQueries(const TSharedRef& EntityManager) { EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); EntityQuery.AddTagRequirement(EMassFragmentPresence::None); } void UMassApplyForceProcessor::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()); EntityQuery.ForEachEntityChunk(Context, [this, DeltaTime](FMassExecutionContext& Context) { const TArrayView ForceList = Context.GetMutableFragmentView(); const TArrayView MovementList = Context.GetMutableFragmentView(); for (FMassExecutionContext::FEntityIterator EntityIt = Context.CreateEntityIterator(); EntityIt; ++EntityIt) { FMassForceFragment& Force = ForceList[EntityIt]; FMassDesiredMovementFragment& DesiredMovement = MovementList[EntityIt]; // Update desired velocity from steering forces. DesiredMovement.DesiredVelocity += Force.Value * DeltaTime; // Reset to zero after force is applied. Processors accumulate forces into the force fragment Force.Value = FVector::ZeroVector; } }); } //----------------------------------------------------------------------// // UMassApplyMovementProcessor //----------------------------------------------------------------------// UMassApplyMovementProcessor::UMassApplyMovementProcessor() : EntityQuery(*this) #if WITH_MASSGAMEPLAY_DEBUG , DebugEntityQuery(*this) #endif { ExecutionFlags = (int32)EProcessorExecutionFlags::AllNetModes; ExecutionOrder.ExecuteInGroup = UE::Mass::ProcessorGroupNames::Movement; ExecutionOrder.ExecuteAfter.Add(UE::Mass::ProcessorGroupNames::ApplyForces); } void UMassApplyMovementProcessor::ConfigureQueries(const TSharedRef& EntityManager) { EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); EntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); EntityQuery.AddRequirement(EMassFragmentAccess::ReadOnly); EntityQuery.AddTagRequirement(EMassFragmentPresence::None); EntityQuery.AddTagRequirement(EMassFragmentPresence::All); #if WITH_MASSGAMEPLAY_DEBUG // We have a separate processor because we want the debug entity query to run always, regardless of the FMassCodeDrivenMovementTag DebugEntityQuery.AddRequirement(EMassFragmentAccess::ReadWrite); #endif } 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()); EntityQuery.ForEachEntityChunk(Context, [this, DeltaTime](FMassExecutionContext& Context) { const TArrayView LocationList = Context.GetMutableFragmentView(); const TArrayView VelocityList = Context.GetMutableFragmentView(); const TConstArrayView MovementList = Context.GetFragmentView(); for (FMassExecutionContext::FEntityIterator EntityIt = Context.CreateEntityIterator(); EntityIt; ++EntityIt) { FMassVelocityFragment& Velocity = VelocityList[EntityIt]; // For code driven, we just apply desired velocity as velocity // This is basically the equivalent of the character movement component // Smoothing / acceleration could be done here if we wanted Velocity.Value = MovementList[EntityIt].DesiredVelocity; FTransform& CurrentTransform = LocationList[EntityIt].GetMutableTransform(); #if WITH_MASSGAMEPLAY_DEBUG if (UE::MassMovement::bFreezeMovement) { Velocity.Value = FVector::ZeroVector; } #endif // WITH_MASSGAMEPLAY_DEBUG FVector CurrentLocation = CurrentTransform.GetLocation(); CurrentLocation += Velocity.Value * DeltaTime; CurrentTransform.SetTranslation(CurrentLocation); } }); #if WITH_MASSGAMEPLAY_DEBUG DebugEntityQuery.ForEachEntityChunk(Context, [this, DeltaTime](FMassExecutionContext& Context) { const TArrayView VelocityList = Context.GetMutableFragmentView(); for (FMassExecutionContext::FEntityIterator EntityIt = Context.CreateEntityIterator(); EntityIt; ++EntityIt) { FMassVelocityFragment& Velocity = VelocityList[EntityIt]; // Keep as "expected value" for next frame. Velocity.DebugPreviousValue = Velocity.Value; } }); #endif // WITH_MASSGAMEPLAY_DEBUG }