Files
UnrealEngine/Engine/Plugins/Runtime/MassGameplay/Source/MassMovement/Private/MassMovementProcessors.cpp
2025-05-18 13:04:45 +08:00

136 lines
5.6 KiB
C++

// 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<FMassEntityManager>& EntityManager)
{
EntityQuery.AddRequirement<FMassDesiredMovementFragment>(EMassFragmentAccess::ReadWrite);
EntityQuery.AddRequirement<FMassForceFragment>(EMassFragmentAccess::ReadWrite);
EntityQuery.AddTagRequirement<FMassOffLODTag>(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<FMassForceFragment> ForceList = Context.GetMutableFragmentView<FMassForceFragment>();
const TArrayView<FMassDesiredMovementFragment> MovementList = Context.GetMutableFragmentView<FMassDesiredMovementFragment>();
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<FMassEntityManager>& EntityManager)
{
EntityQuery.AddRequirement<FMassVelocityFragment>(EMassFragmentAccess::ReadWrite);
EntityQuery.AddRequirement<FTransformFragment>(EMassFragmentAccess::ReadWrite);
EntityQuery.AddRequirement<FMassDesiredMovementFragment>(EMassFragmentAccess::ReadOnly);
EntityQuery.AddTagRequirement<FMassOffLODTag>(EMassFragmentPresence::None);
EntityQuery.AddTagRequirement<FMassCodeDrivenMovementTag>(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<FMassVelocityFragment>(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<FTransformFragment> LocationList = Context.GetMutableFragmentView<FTransformFragment>();
const TArrayView<FMassVelocityFragment> VelocityList = Context.GetMutableFragmentView<FMassVelocityFragment>();
const TConstArrayView<FMassDesiredMovementFragment> MovementList = Context.GetFragmentView<FMassDesiredMovementFragment>();
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<FMassVelocityFragment> VelocityList = Context.GetMutableFragmentView<FMassVelocityFragment>();
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
}