Files
PHY/Plugins/GMS/Source/GenericMovementSystem/Private/GMS_MoverMovementSystemComponent.cpp
2026-03-03 01:23:02 +08:00

754 lines
26 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "GMS_MoverMovementSystemComponent.h"
#include "PoseSearch/PoseSearchTrajectoryPredictor.h"
#include "Runtime/Launch/Resources/Version.h"
#include "GameFramework/Pawn.h"
#include "Components/SkeletalMeshComponent.h"
#include "MoverComponent.h"
#include "MoverPoseSearchTrajectoryPredictor.h"
#include "BoneControllers/AnimNode_OffsetRootBone.h"
#include "Components/CapsuleComponent.h"
#include "DefaultMovementSet/CharacterMoverComponent.h"
#include "DefaultMovementSet/NavMoverComponent.h"
#include "DefaultMovementSet/Settings/CommonLegacyMovementSettings.h"
#include "Locomotions/GMS_MainAnimInstance.h"
#include "MoveLibrary/MovementMixer.h"
#include "Mover/GMS_MoverStructLibrary.h"
#include "Mover/Modifers/GMS_MovementStateModifer.h"
#include "Net/UnrealNetwork.h"
#include "Net/Core/PushModel/PushModel.h"
#include "Settings/GMS_SettingObjectLibrary.h"
#include "Utility/GMS_Constants.h"
#include "Utility/GMS_Log.h"
#include "Utility/GMS_Math.h"
#include "Utility/GMS_Utility.h"
#include "Utility/GMS_Vector.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_MoverMovementSystemComponent)
UGMS_MoverMovementSystemComponent::UGMS_MoverMovementSystemComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
SetIsReplicatedByDefault(true);
PrimaryComponentTick.bCanEverTick = true;
bWantsInitializeComponent = true;
bReplicateUsingRegisteredSubObjectList = true;
MovementModeToTagMapping = {
{TEXT("None"), GMS_MovementModeTags::None},
{TEXT("Walking"), GMS_MovementModeTags::Grounded},
{TEXT("NavWalking"), GMS_MovementModeTags::Grounded},
{TEXT("Falling"), GMS_MovementModeTags::InAir},
{TEXT("Swimming"), GMS_MovementModeTags::Swimming},
{TEXT("Flying"), GMS_MovementModeTags::Flying},
};
}
void UGMS_MoverMovementSystemComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
FDoRepLifetimeParams Parameters;
Parameters.bIsPushBased = true;
Parameters.Condition = COND_SkipOwner;
DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, MovementState, Parameters)
DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, RotationMode, Parameters)
}
void UGMS_MoverMovementSystemComponent::InitializeComponent()
{
Super::InitializeComponent();
MovementState = DesiredMovementState;
RotationMode = DesiredRotationMode;
MoverComponent = OwnerPawn->FindComponentByClass<UMoverComponent>();
if (MoverComponent)
{
TrajectoryPredictor = NewObject<UMoverTrajectoryPredictor>(this, UMoverTrajectoryPredictor::StaticClass());
TrajectoryPredictor->Setup(MoverComponent);
MoverComponent->InputProducer = this;
if (!MoverComponent->MovementMixer)
{
// Prevent crash by early create this object on initialzie component.
MoverComponent->MovementMixer = NewObject<UMovementMixer>(this, TEXT("Default Movement Mixer"));
}
// Make sure this component are ticking after the mover component so this component can access the most up-to-date mover state.
AddTickPrerequisiteComponent(MoverComponent);
}
NavMoverComponent = OwnerPawn->FindComponentByClass<UNavMoverComponent>();
MeshComponent = OwnerPawn->FindComponentByClass<USkeletalMeshComponent>();
if (MeshComponent)
{
AnimationInstance = MeshComponent->GetAnimInstance();
// Make sure the mesh and animation blueprint are ticking after the character so they can access the most up-to-date character state.
MeshComponent->AddTickPrerequisiteComponent(this);
}
}
// Called when the game starts
void UGMS_MoverMovementSystemComponent::BeginPlay()
{
Super::BeginPlay();
//This callback fires only on predicting client and server, not simulated pawn.
MoverComponent->OnMovementModeChanged.AddDynamic(this, &ThisClass::OnMoverMovementModeChanged);
RefreshMovementSetSetting();
MoverComponent->OnPreSimulationTick.AddDynamic(this, &ThisClass::OnMoverPreSimulationTick);
}
void UGMS_MoverMovementSystemComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
if (IsValid(MoverComponent))
{
MoverComponent->OnMovementModeChanged.RemoveDynamic(this, &ThisClass::OnMoverMovementModeChanged);
}
Super::EndPlay(EndPlayReason);
}
// Called every frame
void UGMS_MoverMovementSystemComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
DECLARE_SCOPE_CYCLE_COUNTER(TEXT("UGMS_MoverMovementSystemComponent::TickComponent"), STAT_GMS_MovementSystem_Tick, STATGROUP_GMS)
TRACE_CPUPROFILER_EVENT_SCOPE_STR(__FUNCTION__)
if (!GetMovementDefinition().IsValid() || !IsValid(AnimationInstance) || !IsValid(ControlSetting))
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
return;
}
RefreshMovementBase();
RefreshInput(DeltaTime);
RefreshLocomotionEarly();
RefreshView(DeltaTime);
RefreshLocomotion(DeltaTime);
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
RefreshLocomotionLate(DeltaTime);
}
void UGMS_MoverMovementSystemComponent::OnMoverPreSimulationTick(const FMoverTimeStep& TimeStep, const FMoverInputCmdContext& InputCmd)
{
const FCharacterDefaultInputs* CharacterInputs = InputCmd.InputCollection.FindDataByType<FCharacterDefaultInputs>();
const FGMS_MoverMovementControlInputs* ControlInputs = InputCmd.InputCollection.FindDataByType<FGMS_MoverMovementControlInputs>();
if (ControlInputs)
{
// update movement state, settings. before actually do it.
ApplyMovementState(ControlInputs->DesiredMovementState);
ApplyRotationMode(ControlInputs->DesiredRotationMode);
}
}
void UGMS_MoverMovementSystemComponent::OnMoverMovementModeChanged(const FName& PreviousMovementModeName, const FName& NewMovementModeName)
{
// Use the mover movement mode to set the locomotion mode to the right value.
if (NewMovementModeName != NAME_None)
{
if (MovementModeToTagMapping.Contains(NewMovementModeName) && MovementModeToTagMapping[NewMovementModeName].IsValid())
{
if (LocomotionMode != MovementModeToTagMapping[NewMovementModeName])
{
SetLocomotionMode(MovementModeToTagMapping[NewMovementModeName]);
}
}
else
{
GMS_CLOG(Error, "No locomotion mode mapping for MovementMode:%s", *NewMovementModeName.ToString());
}
}
}
void UGMS_MoverMovementSystemComponent::ApplyMovementSetting()
{
if (IsValid(MoverComponent) && IsValid(ControlSetting))
{
if (const FGMS_MovementStateSetting* TempMS = ControlSetting->GetMovementStateSetting(DesiredMovementState, true))
{
if (UCommonLegacyMovementSettings* LegacyMovementSettings = MoverComponent->FindSharedSettings_Mutable<UCommonLegacyMovementSettings>())
{
LegacyMovementSettings->MaxSpeed = TempMS->Speed;
LegacyMovementSettings->Acceleration = TempMS->Acceleration;
LegacyMovementSettings->Deceleration = TempMS->BrakingDeceleration;
}
}
}
}
void UGMS_MoverMovementSystemComponent::RefreshView(float DeltaTime)
{
ViewState.PreviousYawAngle = UE_REAL_TO_FLOAT(ViewState.Rotation.Yaw);
if (OwnerPawn->IsLocallyControlled() || (OwnerPawn->GetLocalRole() >= ROLE_Authority && IsValid(OwnerPawn->GetController())))
{
// The character movement component already sends the view rotation to the
// server if movement is replicated, so we don't have to do this ourselves.
SetReplicatedViewRotation(OwnerPawn->GetViewRotation().GetNormalized(), true);
}
ViewState.Rotation = ReplicatedViewRotation;
// Set the yaw speed by comparing the current and previous view yaw angle, divided by
// delta seconds. This represents the speed the camera is rotating from left to right.
if (DeltaTime > UE_SMALL_NUMBER)
{
ViewState.YawSpeed = FMath::Abs(UE_REAL_TO_FLOAT(ViewState.Rotation.Yaw - ViewState.PreviousYawAngle)) / DeltaTime;
}
}
void UGMS_MoverMovementSystemComponent::ServerSetReplicatedViewRotation_Implementation(const FRotator& NewViewRotation)
{
Super::ServerSetReplicatedViewRotation_Implementation(NewViewRotation);
// Mover doesn't send control rotation to server, so we do it.
if (OwnerPawn->GetController() && !OwnerPawn->GetController()->GetControlRotation().Equals(NewViewRotation))
{
OwnerPawn->GetController()->SetControlRotation(NewViewRotation);
}
}
TScriptInterface<IPoseSearchTrajectoryPredictorInterface> UGMS_MoverMovementSystemComponent::GetTrajectoryPredictor() const
{
return TrajectoryPredictor;
}
bool UGMS_MoverMovementSystemComponent::IsCrouching() const
{
if (UCharacterMoverComponent* CharacterMover = Cast<UCharacterMoverComponent>(MoverComponent))
{
return CharacterMover->IsCrouching();
}
return false;
}
float UGMS_MoverMovementSystemComponent::GetMaxSpeed() const
{
const UCommonLegacyMovementSettings* CommonLegacySettings = MoverComponent->FindSharedSettings<UCommonLegacyMovementSettings>();
return CommonLegacySettings->MaxSpeed;
}
float UGMS_MoverMovementSystemComponent::GetScaledCapsuleRadius() const
{
if (UCapsuleComponent* Capsule = Cast<UCapsuleComponent>(MoverComponent->GetUpdatedComponent()))
{
return Capsule->GetScaledCapsuleRadius();
}
return GetDefault<UCapsuleComponent>()->GetUnscaledCapsuleRadius();
}
float UGMS_MoverMovementSystemComponent::GetScaledCapsuleHalfHeight() const
{
if (UCapsuleComponent* Capsule = Cast<UCapsuleComponent>(MoverComponent->GetUpdatedComponent()))
{
return Capsule->GetScaledCapsuleHalfHeight();
}
return GetDefault<UCapsuleComponent>()->GetUnscaledCapsuleHalfHeight();
}
float UGMS_MoverMovementSystemComponent::GetMaxAcceleration() const
{
const UCommonLegacyMovementSettings* CommonLegacySettings = MoverComponent->FindSharedSettings<UCommonLegacyMovementSettings>();
return CommonLegacySettings->Acceleration;
}
float UGMS_MoverMovementSystemComponent::GetMaxBrakingDeceleration() const
{
const UCommonLegacyMovementSettings* CommonLegacySettings = MoverComponent->FindSharedSettings<UCommonLegacyMovementSettings>();
return CommonLegacySettings->Deceleration;
}
float UGMS_MoverMovementSystemComponent::GetWalkableFloorZ() const
{
const UCommonLegacyMovementSettings* CommonLegacySettings = MoverComponent->FindSharedSettings<UCommonLegacyMovementSettings>();
return CommonLegacySettings->MaxWalkSlopeCosine;
}
float UGMS_MoverMovementSystemComponent::GetGravityZ() const
{
return MoverComponent->GetGravityAcceleration().Z;
}
USkeletalMeshComponent* UGMS_MoverMovementSystemComponent::GetMesh() const
{
return MeshComponent;
}
bool UGMS_MoverMovementSystemComponent::IsMovingOnGround() const
{
return IsValid(MoverComponent) ? MoverComponent->HasGameplayTag(Mover_IsOnGround, true) : false;
}
void UGMS_MoverMovementSystemComponent::RefreshLocomotionEarly()
{
}
void UGMS_MoverMovementSystemComponent::RefreshLocomotion(float DeltaTime)
{
LocomotionState.Velocity = MoverComponent->GetVelocity();
// Determine if the character is moving by getting its speed. The speed equals the length
// of the horizontal velocity, so it does not take vertical movement into account. If the
// character is moving, update the last velocity rotation. This value is saved because it might
// be useful to know the last orientation of a movement even after the character has stopped.
LocomotionState.Speed = UE_REAL_TO_FLOAT(LocomotionState.Velocity.Size2D());
static constexpr auto HasSpeedThreshold{1.0f};
LocomotionState.bHasVelocity = LocomotionState.Speed >= HasSpeedThreshold;
if (LocomotionState.bHasVelocity)
{
LocomotionState.VelocityYawAngle = UE_REAL_TO_FLOAT(UGMS_Vector::DirectionToAngleXY(LocomotionState.Velocity));
}
// Character is moving if has speed and current acceleration, or if the speed is greater than the moving speed threshold.
LocomotionState.bMoving = (LocomotionState.bHasInput && LocomotionState.bHasVelocity) ||
LocomotionState.Speed > ControlSetting->MovingSpeedThreshold;
}
void UGMS_MoverMovementSystemComponent::RefreshDynamicMovementState()
{
}
void UGMS_MoverMovementSystemComponent::RefreshLocomotionLate(float DeltaTime)
{
if (!LocomotionMode.IsValid())
{
RefreshTargetYawAngleUsingActorRotation();
}
}
void UGMS_MoverMovementSystemComponent::RefreshTargetYawAngleUsingActorRotation()
{
const auto YawAngle{UE_REAL_TO_FLOAT(OwnerPawn->GetActorRotation().Yaw)};
SetTargetYawAngle(YawAngle);
}
void UGMS_MoverMovementSystemComponent::SetTargetYawAngle(const float TargetYawAngle)
{
LocomotionState.TargetYawAngle = FRotator3f::NormalizeAxis(TargetYawAngle);
RefreshViewRelativeTargetYawAngle();
LocomotionState.SmoothTargetYawAngle = LocomotionState.TargetYawAngle;
}
void UGMS_MoverMovementSystemComponent::RefreshViewRelativeTargetYawAngle()
{
LocomotionState.ViewRelativeTargetYawAngle = FRotator3f::NormalizeAxis(UE_REAL_TO_FLOAT(
ViewState.Rotation.Yaw - LocomotionState.TargetYawAngle));
}
FGMS_PredictGroundMovementPivotLocationParams UGMS_MoverMovementSystemComponent::GetPredictGroundMovementPivotLocationParams() const
{
FGMS_PredictGroundMovementPivotLocationParams Params;
if (MoverComponent)
{
if (const UCommonLegacyMovementSettings* CommonLegacySettings = MoverComponent->FindSharedSettings<UCommonLegacyMovementSettings>())
{
Params.Acceleration = MoverComponent->GetMovementIntent() * CommonLegacySettings->Acceleration;
Params.Velocity = MoverComponent->GetVelocity();
Params.GroundFriction = CommonLegacySettings->GroundFriction;
}
}
return Params;
}
FGMS_PredictGroundMovementStopLocationParams UGMS_MoverMovementSystemComponent::GetPredictGroundMovementStopLocationParams() const
{
FGMS_PredictGroundMovementStopLocationParams Params;
if (MoverComponent)
{
if (const UCommonLegacyMovementSettings* CommonLegacySettings = MoverComponent->FindSharedSettings<UCommonLegacyMovementSettings>())
{
Params.Velocity = MoverComponent->GetVelocity();
Params.bUseSeparateBrakingFriction = true;
Params.BrakingFriction = CommonLegacySettings->BrakingFriction;
Params.GroundFriction = CommonLegacySettings->GroundFriction;
Params.BrakingFrictionFactor = CommonLegacySettings->BrakingFrictionFactor;
Params.BrakingDecelerationWalking = CommonLegacySettings->Deceleration;
}
}
return Params;
}
void UGMS_MoverMovementSystemComponent::RefreshMovementBase()
{
UPrimitiveComponent* BasePrimitive = MoverComponent->GetMovementBase();
FName BaseBoneName = MoverComponent->GetMovementBaseBoneName();
if (BasePrimitive != MovementBase.Primitive || BaseBoneName != MovementBase.BoneName)
{
MovementBase.Primitive = BasePrimitive;
MovementBase.BoneName = BaseBoneName;
MovementBase.bBaseChanged = true;
}
else
{
MovementBase.bBaseChanged = false;
}
MovementBase.bHasRelativeLocation = UBasedMovementUtils::IsADynamicBase(BasePrimitive);
MovementBase.bHasRelativeRotation = MovementBase.bHasRelativeLocation && bUseBaseRelativeMovement;
const auto PreviousRotation{MovementBase.Rotation};
UBasedMovementUtils::GetMovementBaseTransform(BasePrimitive, BaseBoneName,
MovementBase.Location, MovementBase.Rotation);
MovementBase.DeltaRotation = MovementBase.bHasRelativeLocation && !MovementBase.bBaseChanged
? (MovementBase.Rotation * PreviousRotation.Inverse()).Rotator()
: FRotator::ZeroRotator;
}
void UGMS_MoverMovementSystemComponent::ProduceInput_Implementation(int32 SimTimeMs, FMoverInputCmdContext& InputCmdResult)
{
InputCmdResult = OnProduceInput(static_cast<float>(SimTimeMs), InputCmdResult);
}
#pragma region MovementState
const FGameplayTag& UGMS_MoverMovementSystemComponent::GetDesiredMovementState() const
{
return DesiredMovementState;
}
void UGMS_MoverMovementSystemComponent::SetDesiredMovement(const FGameplayTag& NewDesiredMovement)
{
DesiredMovementState = NewDesiredMovement;
}
const FGameplayTag& UGMS_MoverMovementSystemComponent::GetMovementState() const
{
return MovementState;
}
void UGMS_MoverMovementSystemComponent::ApplyMovementState(const FGameplayTag& NewMovementState)
{
if (MovementState == NewMovementState || GetOwner()->GetLocalRole() < ROLE_AutonomousProxy)
{
return;
}
FGameplayTag Prev = MovementState;
MovementState = NewMovementState;
MARK_PROPERTY_DIRTY_FROM_NAME(ThisClass, MovementState, this)
OnMovementStateChanged(Prev);
}
#pragma endregion
#pragma region RotationMode
const FGameplayTag& UGMS_MoverMovementSystemComponent::GetDesiredRotationMode() const
{
return DesiredRotationMode;
}
void UGMS_MoverMovementSystemComponent::SetDesiredRotationMode(const FGameplayTag& NewDesiredRotationMode)
{
DesiredRotationMode = NewDesiredRotationMode;
}
const FGameplayTag& UGMS_MoverMovementSystemComponent::GetRotationMode() const
{
return RotationMode;
}
void UGMS_MoverMovementSystemComponent::ApplyRotationMode(const FGameplayTag& NewRotationMode)
{
if (RotationMode == NewRotationMode || GetOwner()->GetLocalRole() < ROLE_AutonomousProxy)
{
return;
}
FGameplayTag Prev = RotationMode;
RotationMode = NewRotationMode;
MARK_PROPERTY_DIRTY_FROM_NAME(ThisClass, RotationMode, this)
OnRotationModeChanged(Prev);
}
#pragma endregion
FVector UGMS_MoverMovementSystemComponent::GetMovementIntent() const
{
if (const FCharacterDefaultInputs* CharacterInputs = MoverComponent->GetLastInputCmd().InputCollection.FindDataByType<FCharacterDefaultInputs>())
{
return CharacterInputs->GetMoveInput_WorldSpace();
}
return MoverComponent->GetMovementIntent();
}
FVector UGMS_MoverMovementSystemComponent::AdjustOrientationIntent(float DeltaSeconds, const FVector& OrientationIntent) const
{
FVector Intent = OrientationIntent;
if (GetRotationMode() == GMS_RotationModeTags::VelocityDirection)
{
if (const FGMS_VelocityDirectionSetting_RateBased* Setting = GetControlSetting()->VelocityDirectionSetting.GetPtr<FGMS_VelocityDirectionSetting_RateBased>())
{
float YawDelta = CachedTurnInput.Yaw * DeltaSeconds * Setting->TurnRate;
Intent.Z += YawDelta;
}
}
if (GetRotationMode() == GMS_RotationModeTags::ViewDirection)
{
// Use curve to drive actor rotation only if no root bone rotation. 仅在没有使用根骨旋转时采用曲线驱动Actor旋转。
if (UGMS_MainAnimInstance* AnimInst = Cast<UGMS_MainAnimInstance>(MainAnimInstance))
{
if (AnimInst->GetOffsetRootBoneRotationMode() == EOffsetRootBoneMode::Release)
{
const float CurveValue = AnimationInstance->GetCurveValue(UGMS_Constants::RotationYawSpeedCurveName());
const float DeltaYawAngle{CurveValue * DeltaSeconds};
if (FMath::Abs(DeltaYawAngle) > UE_SMALL_NUMBER)
{
Intent = Intent.RotateAngleAxis(DeltaYawAngle, FVector::ZAxisVector);
}
}
}
}
return Intent;
}
FMoverInputCmdContext UGMS_MoverMovementSystemComponent::OnProduceInput_Implementation(float DeltaMs, FMoverInputCmdContext InputCmdResult)
{
check(OwnerPawn)
FCharacterDefaultInputs& CharacterInputs = InputCmdResult.InputCollection.FindOrAddMutableDataByType<FCharacterDefaultInputs>();
FGMS_MoverMovementControlInputs& ControlInputs = InputCmdResult.InputCollection.FindOrAddMutableDataByType<FGMS_MoverMovementControlInputs>();
ControlInputs.DesiredMovementSet = MovementSet;
ControlInputs.DesiredRotationMode = DesiredRotationMode;
ControlInputs.DesiredMovementState = DesiredMovementState;
if (OwnerPawn->Controller == nullptr)
{
if (OwnerPawn->GetLocalRole() == ROLE_Authority && OwnerPawn->GetRemoteRole() == ROLE_SimulatedProxy)
{
static const FCharacterDefaultInputs DoNothingInput;
// If we get here, that means this pawn is not currently possessed and we're choosing to provide default do-nothing input
CharacterInputs = DoNothingInput;
}
// We don't have a local controller so we can't run the code below. This is ok. Simulated proxies will just use previous input when extrapolating
return InputCmdResult;
}
CharacterInputs.ControlRotation = FRotator::ZeroRotator;
// APlayerController* PC = Cast<APlayerController>(OwnerPawn->Controller);
// if (PC)
// {
// CharacterInputs.ControlRotation = PC->GetControlRotation();
// }
CharacterInputs.ControlRotation = ViewState.Rotation;
bool bRequestedNavMovement = false;
if (NavMoverComponent)
{
bRequestedNavMovement = NavMoverComponent->ConsumeNavMovementData(CachedMoveInputIntent, CachedMoveInputVelocity);
}
// setup move input based on velocity/raw input.
{
// Favor velocity input
bool bUsingInputIntentForMove = CachedMoveInputVelocity.IsZero();
if (bUsingInputIntentForMove)
{
const FVector FinalDirectionalIntent = CharacterInputs.ControlRotation.RotateVector(CachedMoveInputIntent);
// FRotator Rotator = CharacterInputs.ControlRotation;
// FVector FinalDirectionalIntent;
// if (const UCharacterMoverComponent* MoverComp = Cast<UCharacterMoverComponent>(MoverComponent))
// {
// if (MoverComp->IsOnGround() || MoverComp->IsFalling())
// {
// const FVector RotationProjectedOntoUpDirection = FVector::VectorPlaneProject(Rotator.Vector(), MoverComp->GetUpDirection()).GetSafeNormal();
// Rotator = RotationProjectedOntoUpDirection.Rotation();
// }
// FinalDirectionalIntent = Rotator.RotateVector(CachedMoveInputIntent);
// }
CharacterInputs.SetMoveInput(EMoveInputType::DirectionalIntent, FinalDirectionalIntent);
}
else
{
CharacterInputs.SetMoveInput(EMoveInputType::Velocity, CachedMoveInputVelocity);
}
// Normally cached input is cleared by OnMoveCompleted input event but that won't be called if movement came from nav movement
if (bRequestedNavMovement)
{
CachedMoveInputIntent = FVector::ZeroVector;
CachedMoveInputVelocity = FVector::ZeroVector;
}
}
static float RotationMagMin(1e-3);
const bool bHasAffirmativeMoveInput = (CharacterInputs.GetMoveInput().Size() >= RotationMagMin);
// Figure out intended orientation
CharacterInputs.OrientationIntent = FVector::ZeroVector;
// setup orientation.
{
const bool bVelocityDirection = GetRotationMode() == GMS_RotationModeTags::VelocityDirection;
const bool bViewDirection = !bVelocityDirection;
if (!bHasAffirmativeMoveInput && bVelocityDirection)
{
if (const FGMS_VelocityDirectionSetting_RateBased* Setting = GetControlSetting()->VelocityDirectionSetting.GetPtr<FGMS_VelocityDirectionSetting_RateBased>())
{
const float DeltaSeconds = DeltaMs * 0.001f;
CharacterInputs.OrientationIntent = AdjustOrientationIntent(DeltaSeconds, MoverComponent->GetTargetOrientation().Vector());
}
else if (bMaintainLastInputOrientation)
{
// There is no movement intent, so use the last-known affirmative move input
CharacterInputs.OrientationIntent = LastAffirmativeMoveInput;
}
}
if (bHasAffirmativeMoveInput && bVelocityDirection)
{
if (const FGMS_VelocityDirectionSetting_RateBased* Setting = GetControlSetting()->VelocityDirectionSetting.GetPtr<FGMS_VelocityDirectionSetting_RateBased>())
{
const float DeltaSeconds = DeltaMs * 0.001f;
CharacterInputs.OrientationIntent = AdjustOrientationIntent(DeltaSeconds, MoverComponent->GetTargetOrientation().Vector());
}
else
{
// set the intent to the actors movement direction
CharacterInputs.OrientationIntent = CharacterInputs.GetMoveInput().GetSafeNormal();
}
}
if (!bHasAffirmativeMoveInput && bViewDirection)
{
if (GetControlSetting()->ViewDirectionSetting.Get().bEnableRotationWhenNotMoving)
{
// set intent to the the control rotation - often a player's camera rotation
CharacterInputs.OrientationIntent = CharacterInputs.ControlRotation.Vector().GetSafeNormal();
}
else
{
const float DeltaSeconds = DeltaMs * 0.001f;
CharacterInputs.OrientationIntent = AdjustOrientationIntent(DeltaSeconds, MoverComponent->GetTargetOrientation().Vector());
}
}
if (bHasAffirmativeMoveInput && bViewDirection)
{
// set intent to the control rotation - often a player's camera rotation
CharacterInputs.OrientationIntent = CharacterInputs.ControlRotation.Vector().GetSafeNormal();
}
}
if (bHasAffirmativeMoveInput)
{
LastAffirmativeMoveInput = CharacterInputs.GetMoveInput();
}
if (bShouldRemainVertical)
{
// canceling out any z intent if the actor is supposed to remain vertical
CharacterInputs.OrientationIntent = CharacterInputs.OrientationIntent.GetSafeNormal2D();
}
CharacterInputs.bIsJumpPressed = bIsJumpPressed;
CharacterInputs.bIsJumpJustPressed = bIsJumpJustPressed;
if (bShouldToggleFlying)
{
if (!bIsFlyingActive)
{
CharacterInputs.SuggestedMovementMode = DefaultModeNames::Flying;
}
else
{
CharacterInputs.SuggestedMovementMode = DefaultModeNames::Falling;
}
bIsFlyingActive = !bIsFlyingActive;
}
else
{
CharacterInputs.SuggestedMovementMode = NAME_None;
}
// Convert inputs to be relative to the current movement base (depending on options and state)
CharacterInputs.bUsingMovementBase = false;
if (bUseBaseRelativeMovement)
{
if (const UCharacterMoverComponent* MoverComp = OwnerPawn->GetComponentByClass<UCharacterMoverComponent>())
{
if (UPrimitiveComponent* MovementBasePtr = MoverComp->GetMovementBase())
{
FName MovementBaseBoneName = MoverComp->GetMovementBaseBoneName();
FVector RelativeMoveInput, RelativeOrientDir;
UBasedMovementUtils::TransformWorldDirectionToBased(MovementBasePtr, MovementBaseBoneName, CharacterInputs.GetMoveInput(), RelativeMoveInput);
UBasedMovementUtils::TransformWorldDirectionToBased(MovementBasePtr, MovementBaseBoneName, CharacterInputs.OrientationIntent, RelativeOrientDir);
CharacterInputs.SetMoveInput(CharacterInputs.GetMoveInputType(), RelativeMoveInput);
CharacterInputs.OrientationIntent = RelativeOrientDir;
CharacterInputs.bUsingMovementBase = true;
CharacterInputs.MovementBase = MovementBasePtr;
CharacterInputs.MovementBaseBoneName = MovementBaseBoneName;
}
}
}
// Clear/consume temporal movement inputs. We are not consuming others in the event that the game world is ticking at a lower rate than the Mover simulation.
// In that case, we want most input to carry over between simulation frames.
{
bIsJumpJustPressed = false;
bShouldToggleFlying = false;
}
return InputCmdResult;
}
#if WITH_EDITOR
EDataValidationResult UGMS_MoverMovementSystemComponent::IsDataValid(class FDataValidationContext& Context) const
{
// if (IsTemplate() && InputProducerClass.IsNull())
// {
// Context.AddError(FText::FromString("Input Producer Class is required!"));
// return EDataValidationResult::Invalid;
// }
return Super::IsDataValid(Context);
}
#endif