1084 lines
36 KiB
C++
1084 lines
36 KiB
C++
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||
|
||
#include "GMS_CharacterMovementSystemComponent.h"
|
||
#include "TimerManager.h"
|
||
#include "Components/CapsuleComponent.h"
|
||
#include "Curves/CurveFloat.h"
|
||
#include "GameFramework/CharacterMovementComponent.h"
|
||
#include "GameFramework/PlayerController.h"
|
||
#include "Kismet/KismetMathLibrary.h"
|
||
#include "Net/UnrealNetwork.h"
|
||
#include "Components/SkeletalMeshComponent.h"
|
||
#include "Animation/AnimInstance.h"
|
||
#include "Locomotions/GMS_MainAnimInstance.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_Rotation.h"
|
||
#include "Utility/GMS_Utility.h"
|
||
#include "Utility/GMS_Vector.h"
|
||
|
||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_CharacterMovementSystemComponent)
|
||
|
||
namespace GMS_MovementComponentConstants
|
||
{
|
||
inline static constexpr auto TeleportDistanceThresholdSquared{FMath::Square(50.0f)};
|
||
}
|
||
|
||
UGMS_CharacterMovementSystemComponent::UGMS_CharacterMovementSystemComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
|
||
{
|
||
SetIsReplicatedByDefault(true);
|
||
PrimaryComponentTick.bCanEverTick = true;
|
||
bWantsInitializeComponent = true;
|
||
bReplicateUsingRegisteredSubObjectList = true;
|
||
|
||
MovementIntent = FVector::ZeroVector;
|
||
|
||
MovementModeToTagMapping = {
|
||
{MOVE_None, GMS_MovementModeTags::None},
|
||
{MOVE_Walking, GMS_MovementModeTags::Grounded},
|
||
{MOVE_NavWalking, GMS_MovementModeTags::Grounded},
|
||
{MOVE_Falling, GMS_MovementModeTags::InAir},
|
||
{MOVE_Swimming, GMS_MovementModeTags::Swimming},
|
||
{MOVE_Flying, GMS_MovementModeTags::Flying},
|
||
};
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||
{
|
||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||
|
||
FDoRepLifetimeParams Parameters;
|
||
Parameters.bIsPushBased = true;
|
||
|
||
Parameters.Condition = COND_SkipOwner;
|
||
|
||
DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, DesiredMovementState, Parameters)
|
||
DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, DesiredRotationMode, Parameters)
|
||
DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, MovementIntent, Parameters)
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::InitializeComponent()
|
||
{
|
||
Super::InitializeComponent();
|
||
|
||
OwnerCharacter = Cast<ACharacter>(GetOwner());
|
||
if (OwnerCharacter)
|
||
{
|
||
MovementState = DesiredMovementState;
|
||
RotationMode = DesiredRotationMode;
|
||
|
||
CharacterMovement = Cast<UCharacterMovementComponent>(OwnerCharacter->GetMovementComponent());
|
||
OwnerCharacter->bUseControllerRotationPitch = false;
|
||
OwnerCharacter->bUseControllerRotationRoll = false;
|
||
OwnerCharacter->bUseControllerRotationYaw = false;
|
||
CharacterMovement->bUseControllerDesiredRotation = false;
|
||
CharacterMovement->bOrientRotationToMovement = false;
|
||
|
||
AnimationInstance = GetMesh()->GetAnimInstance();
|
||
// Make sure the mesh and animation blueprint are ticking after the character so they can access the most up-to-date character state.
|
||
GetMesh()->AddTickPrerequisiteComponent(this);
|
||
RefreshTargetYawAngleUsingActorRotation();
|
||
}
|
||
}
|
||
|
||
|
||
void UGMS_CharacterMovementSystemComponent::BeginPlay()
|
||
{
|
||
Super::BeginPlay();
|
||
|
||
//This callback fires on both sides, including simulated proxy.
|
||
OwnerCharacter->MovementModeChangedDelegate.AddDynamic(this, &ThisClass::OnCharacterMovementModeChanged);
|
||
OnCharacterMovementModeChanged(OwnerCharacter, OwnerCharacter->GetCharacterMovement()->GetGroundMovementMode(), 0);
|
||
|
||
RefreshRotationMode();
|
||
|
||
RefreshMovementSetSetting();
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||
{
|
||
if (IsValid(OwnerCharacter))
|
||
{
|
||
OwnerCharacter->MovementModeChangedDelegate.RemoveDynamic(this, &ThisClass::OnCharacterMovementModeChanged);
|
||
}
|
||
Super::EndPlay(EndPlayReason);
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
|
||
{
|
||
DECLARE_SCOPE_CYCLE_COUNTER(TEXT("UGMS_CharacterMovementSystemComponent::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);
|
||
|
||
RefreshRotationMode();
|
||
|
||
RefreshLocomotion(DeltaTime);
|
||
|
||
RefreshDynamicMovementState();
|
||
|
||
RefreshMovementState();
|
||
|
||
RefreshRotation(DeltaTime);
|
||
|
||
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
||
|
||
RefreshLocomotionLate(DeltaTime);
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::OnCharacterMovementModeChanged(ACharacter* InCharacter, EMovementMode PrevMovementMode, uint8 PreviousCustomMode)
|
||
{
|
||
// Use the character movement mode to set the locomotion mode to the right value. This allows you to have a
|
||
// custom set of movement modes but still use the functionality of the default character movement component.
|
||
|
||
EMovementMode CharMovementMode = CharacterMovement->MovementMode;
|
||
uint8 CharCustomMovementMode = CharacterMovement->CustomMovementMode;
|
||
|
||
if (CharMovementMode != MOVE_Custom)
|
||
{
|
||
if (MovementModeToTagMapping.Contains(CharMovementMode) && MovementModeToTagMapping[CharMovementMode].IsValid())
|
||
{
|
||
SetLocomotionMode(MovementModeToTagMapping[CharMovementMode]);
|
||
}
|
||
else
|
||
{
|
||
GMS_CLOG(Error, "No locomotion mode mapping for MovementMode:%s", *UEnum::GetDisplayValueAsText(CharMovementMode).ToString());
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (CustomMovementModeToTagMapping.Contains(CharCustomMovementMode) && CustomMovementModeToTagMapping[CharCustomMovementMode].IsValid())
|
||
{
|
||
SetLocomotionMode(CustomMovementModeToTagMapping[CharCustomMovementMode]);
|
||
}
|
||
else
|
||
{
|
||
GMS_CLOG(Error, "No locomotion mode mapping for CustomMovementMode:%d", CharCustomMovementMode);
|
||
}
|
||
}
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::OnReplicated_LocomotionMode(const FGameplayTag& PreviousLocomotionMode)
|
||
{
|
||
// OnMovementModeChanged will fire on all clients(including simulated pawn), mover is not. so don't call parent.
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::RefreshMovementState()
|
||
{
|
||
if (!DesiredMovementState.IsValid())
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (MovementState == DesiredMovementState)
|
||
{
|
||
return;
|
||
}
|
||
|
||
ApplyMovementSetting();
|
||
|
||
SetMovementState(CalculateActualMovementState());
|
||
}
|
||
|
||
FGameplayTag UGMS_CharacterMovementSystemComponent::CalculateActualMovementState()
|
||
{
|
||
check(GetNumOfMovementStateSettings() != 0)
|
||
|
||
if (ControlSetting->MovementStates.Num() == 1)
|
||
{
|
||
return ControlSetting->MovementStates[0].Tag;
|
||
}
|
||
|
||
for (int32 i = 0; i < ControlSetting->MovementStates.Num(); i++)
|
||
{
|
||
float Speed = ControlSetting->MovementStates[i].Speed;
|
||
if (Speed > 0.0f && LocomotionState.Speed < Speed + 10.0f)
|
||
{
|
||
return ControlSetting->MovementStates[i].Tag;
|
||
}
|
||
}
|
||
|
||
return FGameplayTag::EmptyTag;
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::ApplyMovementSetting()
|
||
{
|
||
if (bAllowRefreshCharacterMovementSettings && IsValid(ControlSetting))
|
||
{
|
||
if (const FGMS_MovementStateSetting* TempMS = ControlSetting->GetMovementStateSetting(DesiredMovementState, true))
|
||
{
|
||
CharacterMovement->MaxWalkSpeed = TempMS->Speed;
|
||
CharacterMovement->MaxAcceleration = TempMS->Acceleration;
|
||
CharacterMovement->BrakingDecelerationWalking = TempMS->BrakingDeceleration;
|
||
CharacterMovement->MaxWalkSpeedCrouched = TempMS->Speed;
|
||
}
|
||
}
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::SetDesiredMovement(const FGameplayTag& NewDesiredMovement)
|
||
{
|
||
SetDesiredMovement(NewDesiredMovement, true);
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::SetMovementState(const FGameplayTag& NewMovementState)
|
||
{
|
||
if (NewMovementState.IsValid() && MovementState != NewMovementState)
|
||
{
|
||
const FGameplayTag PreviousMovementState{MovementState};
|
||
|
||
MovementState = NewMovementState;
|
||
|
||
OnMovementStateChanged(PreviousMovementState);
|
||
}
|
||
}
|
||
|
||
const FGameplayTag& UGMS_CharacterMovementSystemComponent::GetMovementState() const
|
||
{
|
||
return MovementState;
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::SetDesiredMovement(const FGameplayTag& NewDesiredMovement, bool bSendRpc)
|
||
{
|
||
if (DesiredMovementState == NewDesiredMovement || GetOwner()->GetLocalRole() < ROLE_AutonomousProxy)
|
||
{
|
||
return;
|
||
}
|
||
const auto PreviousMovement{DesiredMovementState};
|
||
|
||
DesiredMovementState = NewDesiredMovement;
|
||
|
||
MARK_PROPERTY_DIRTY_FROM_NAME(ThisClass, DesiredMovementState, this)
|
||
|
||
if (bSendRpc)
|
||
{
|
||
if (GetOwner()->GetLocalRole() >= ROLE_Authority)
|
||
{
|
||
ClientSetDesiredMovement(NewDesiredMovement);
|
||
}
|
||
else
|
||
{
|
||
ServerSetDesiredMovement(NewDesiredMovement);
|
||
}
|
||
}
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::ClientSetDesiredMovement_Implementation(const FGameplayTag& NewDesiredMovement)
|
||
{
|
||
SetDesiredMovement(NewDesiredMovement, false);
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::ServerSetDesiredMovement_Implementation(const FGameplayTag& NewDesiredMovement)
|
||
{
|
||
SetDesiredMovement(NewDesiredMovement, false);
|
||
}
|
||
|
||
const FGameplayTag& UGMS_CharacterMovementSystemComponent::GetDesiredRotationMode() const
|
||
{
|
||
return DesiredRotationMode;
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::SetDesiredRotationMode(const FGameplayTag& NewDesiredRotationMode)
|
||
{
|
||
SetDesiredRotationMode(NewDesiredRotationMode, true);
|
||
}
|
||
|
||
const FGameplayTag& UGMS_CharacterMovementSystemComponent::GetRotationMode() const
|
||
{
|
||
return RotationMode;
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::SetRotationMode(const FGameplayTag& NewRotationMode)
|
||
{
|
||
if (RotationMode != NewRotationMode)
|
||
{
|
||
if (bRespectAllowedRotationModesSettings && !GetMovementStateSetting().AllowedRotationModes.Contains(NewRotationMode))
|
||
{
|
||
GMS_CLOG(Warning, "current movement state(%s) doesn't allow new rotation mode(%s).", *MovementState.ToString(), *NewRotationMode.ToString());
|
||
return;
|
||
}
|
||
const auto PreviousRotationMode{RotationMode};
|
||
|
||
RotationMode = NewRotationMode;
|
||
|
||
OnRotationModeChanged(PreviousRotationMode);
|
||
}
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::RefreshRotationMode()
|
||
{
|
||
SetRotationMode(DesiredRotationMode);
|
||
}
|
||
|
||
|
||
void UGMS_CharacterMovementSystemComponent::SetDesiredRotationMode(const FGameplayTag& NewDesiredRotationMode, bool bSendRpc)
|
||
{
|
||
if (DesiredRotationMode == NewDesiredRotationMode || GetOwner()->GetLocalRole() < ROLE_AutonomousProxy)
|
||
{
|
||
return;
|
||
}
|
||
|
||
DesiredRotationMode = NewDesiredRotationMode;
|
||
|
||
MARK_PROPERTY_DIRTY_FROM_NAME(ThisClass, DesiredRotationMode, this)
|
||
|
||
if (bSendRpc)
|
||
{
|
||
if (GetOwner()->GetLocalRole() >= ROLE_Authority)
|
||
{
|
||
ClientSetDesiredRotationMode(DesiredRotationMode);
|
||
}
|
||
else
|
||
{
|
||
ServerSetDesiredRotationMode(DesiredRotationMode);
|
||
}
|
||
}
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::ClientSetDesiredRotationMode_Implementation(const FGameplayTag& NewDesiredRotationMode)
|
||
{
|
||
SetDesiredRotationMode(NewDesiredRotationMode, false);
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::ServerSetDesiredRotationMode_Implementation(const FGameplayTag& NewDesiredRotationMode)
|
||
{
|
||
SetDesiredRotationMode(NewDesiredRotationMode, false);
|
||
}
|
||
|
||
|
||
void UGMS_CharacterMovementSystemComponent::SetMovementIntent(FVector NewMovementIntent)
|
||
{
|
||
NewMovementIntent = NewMovementIntent.GetSafeNormal();
|
||
|
||
COMPARE_ASSIGN_AND_MARK_PROPERTY_DIRTY(ThisClass, MovementIntent, NewMovementIntent, this);
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::RefreshInput(float DeltaTime)
|
||
{
|
||
// Using current acceleration as movement input.
|
||
if (OwnerCharacter->GetLocalRole() >= ROLE_AutonomousProxy)
|
||
{
|
||
SetMovementIntent(CharacterMovement->GetCurrentAcceleration() / CharacterMovement->GetMaxAcceleration());
|
||
}
|
||
|
||
Super::RefreshInput(DeltaTime);
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::OnRotationModeChanged_Implementation(const FGameplayTag& PreviousRotationMode)
|
||
{
|
||
Super::OnRotationModeChanged_Implementation(PreviousRotationMode);
|
||
|
||
// if (PreviousRotationMode == GMS_RotationModeTags::VelocityDirection && !LocomotionState.bMoving)
|
||
// {
|
||
// // This prevents the actor from rotating in the last input direction after the
|
||
// // rotation mode has been changed and the actor is not moving at that moment.
|
||
// // LocomotionState.InputYawAngle = ViewState.Rotation.Yaw;
|
||
// LocomotionState.TargetYawAngle = ViewState.Rotation.Yaw;
|
||
// }
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::RefreshMovementBase()
|
||
{
|
||
const FBasedMovementInfo& BasedMovement = OwnerCharacter->GetBasedMovement();
|
||
if (BasedMovement.MovementBase != MovementBase.Primitive || BasedMovement.BoneName != MovementBase.BoneName)
|
||
{
|
||
MovementBase.Primitive = BasedMovement.MovementBase;
|
||
MovementBase.BoneName = BasedMovement.BoneName;
|
||
MovementBase.bBaseChanged = true;
|
||
}
|
||
else
|
||
{
|
||
MovementBase.bBaseChanged = false;
|
||
}
|
||
|
||
MovementBase.bHasRelativeLocation = BasedMovement.HasRelativeLocation();
|
||
MovementBase.bHasRelativeRotation = MovementBase.bHasRelativeLocation && BasedMovement.bRelativeRotation;
|
||
|
||
const auto PreviousRotation{MovementBase.Rotation};
|
||
|
||
MovementBaseUtility::GetMovementBaseTransform(BasedMovement.MovementBase, BasedMovement.BoneName,
|
||
MovementBase.Location, MovementBase.Rotation);
|
||
|
||
MovementBase.DeltaRotation = MovementBase.bHasRelativeLocation && !MovementBase.bBaseChanged
|
||
? (MovementBase.Rotation * PreviousRotation.Inverse()).Rotator()
|
||
: FRotator::ZeroRotator;
|
||
}
|
||
|
||
const FGameplayTag& UGMS_CharacterMovementSystemComponent::GetDesiredMovementState() const
|
||
{
|
||
return DesiredMovementState;
|
||
}
|
||
|
||
FVector UGMS_CharacterMovementSystemComponent::GetMovementIntent() const
|
||
{
|
||
return MovementIntent;
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::RefreshView(const float DeltaTime)
|
||
{
|
||
if (MovementBase.bHasRelativeRotation)
|
||
{
|
||
// Offset the rotations to keep them relative to the movement base.
|
||
|
||
ViewState.Rotation.Pitch += MovementBase.DeltaRotation.Pitch;
|
||
ViewState.Rotation.Yaw += MovementBase.DeltaRotation.Yaw;
|
||
ViewState.Rotation.Normalize();
|
||
}
|
||
|
||
ViewState.PreviousYawAngle = UE_REAL_TO_FLOAT(ViewState.Rotation.Yaw);
|
||
|
||
// update view/control rotation.
|
||
if (MovementBase.bHasRelativeRotation)
|
||
{
|
||
if (OwnerPawn->IsLocallyControlled())
|
||
{
|
||
// We can't depend on the view rotation sent by the character movement component
|
||
// since it's in world space, so in this case we always send it ourselves.
|
||
|
||
//将相对于平台的视角朝向设置为新的视角朝向。保持视角不变。
|
||
SetReplicatedViewRotation((MovementBase.Rotation.Inverse() * OwnerPawn->GetViewRotation().Quaternion()).Rotator(), true);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (OwnerPawn->IsLocallyControlled() || (OwnerPawn->IsReplicatingMovement() && 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(), !OwnerPawn->IsReplicatingMovement());
|
||
}
|
||
}
|
||
|
||
// update view rotation based on if movement is based.
|
||
ViewState.Rotation = MovementBase.bHasRelativeRotation
|
||
? (MovementBase.Rotation * ReplicatedViewRotation.Quaternion()).Rotator()
|
||
: 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;
|
||
}
|
||
}
|
||
|
||
#pragma region Abstraction
|
||
|
||
bool UGMS_CharacterMovementSystemComponent::IsCrouching() const
|
||
{
|
||
return CharacterMovement->IsCrouching();
|
||
}
|
||
|
||
float UGMS_CharacterMovementSystemComponent::GetMaxSpeed() const
|
||
{
|
||
return CharacterMovement->GetMaxSpeed();
|
||
}
|
||
|
||
float UGMS_CharacterMovementSystemComponent::GetScaledCapsuleRadius() const
|
||
{
|
||
return OwnerCharacter->GetCapsuleComponent()->GetScaledCapsuleRadius();
|
||
}
|
||
|
||
float UGMS_CharacterMovementSystemComponent::GetScaledCapsuleHalfHeight() const
|
||
{
|
||
return OwnerCharacter->GetCapsuleComponent()->GetScaledCapsuleHalfHeight();
|
||
}
|
||
|
||
float UGMS_CharacterMovementSystemComponent::GetMaxAcceleration() const
|
||
{
|
||
return CharacterMovement->GetMaxAcceleration();
|
||
}
|
||
|
||
float UGMS_CharacterMovementSystemComponent::GetMaxBrakingDeceleration() const
|
||
{
|
||
return CharacterMovement->GetMaxBrakingDeceleration();
|
||
}
|
||
|
||
float UGMS_CharacterMovementSystemComponent::GetWalkableFloorZ() const
|
||
{
|
||
return CharacterMovement->GetWalkableFloorZ();
|
||
}
|
||
|
||
float UGMS_CharacterMovementSystemComponent::GetGravityZ() const
|
||
{
|
||
return CharacterMovement->GetGravityZ();
|
||
}
|
||
|
||
USkeletalMeshComponent* UGMS_CharacterMovementSystemComponent::GetMesh() const
|
||
{
|
||
return OwnerCharacter ? OwnerCharacter->GetMesh() : nullptr;
|
||
}
|
||
|
||
bool UGMS_CharacterMovementSystemComponent::IsMovingOnGround() const
|
||
{
|
||
return CharacterMovement->IsMovingOnGround();
|
||
}
|
||
|
||
#pragma endregion
|
||
|
||
#pragma region Locomotion
|
||
void UGMS_CharacterMovementSystemComponent::RefreshLocomotionEarly()
|
||
{
|
||
if (!LocomotionState.bMoving &&
|
||
RotationMode == GMS_RotationModeTags::VelocityDirection &&
|
||
ControlSetting->VelocityDirectionSetting.Get().bInheritBaseRotation)
|
||
{
|
||
DesiredVelocityYawAngle = FMath::UnwindDegrees(UE_REAL_TO_FLOAT(
|
||
DesiredVelocityYawAngle + MovementBase.DeltaRotation.Yaw));
|
||
|
||
LocomotionState.VelocityYawAngle = FMath::UnwindDegrees(UE_REAL_TO_FLOAT(
|
||
LocomotionState.VelocityYawAngle + MovementBase.DeltaRotation.Yaw));
|
||
}
|
||
|
||
if (MovementBase.bHasRelativeLocation)
|
||
{
|
||
// Offset the rotations (the actor's rotation too) to keep them relative to the movement base.
|
||
|
||
LocomotionState.TargetYawAngle = FMath::UnwindDegrees(UE_REAL_TO_FLOAT(
|
||
LocomotionState.TargetYawAngle + MovementBase.DeltaRotation.Yaw));
|
||
|
||
LocomotionState.ViewRelativeTargetYawAngle = FMath::UnwindDegrees(UE_REAL_TO_FLOAT(
|
||
LocomotionState.ViewRelativeTargetYawAngle + MovementBase.DeltaRotation.Yaw));
|
||
|
||
LocomotionState.SmoothTargetYawAngle = FMath::UnwindDegrees(UE_REAL_TO_FLOAT(
|
||
LocomotionState.SmoothTargetYawAngle + MovementBase.DeltaRotation.Yaw));
|
||
|
||
auto NewRotation{OwnerPawn->GetActorRotation()};
|
||
NewRotation.Pitch += MovementBase.DeltaRotation.Pitch;
|
||
NewRotation.Yaw += MovementBase.DeltaRotation.Yaw;
|
||
NewRotation.Normalize();
|
||
|
||
SetActorRotation(NewRotation);
|
||
}
|
||
|
||
LocomotionState.bAimingLimitAppliedThisFrame = false;
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::RefreshLocomotion(const float DeltaTime)
|
||
{
|
||
const auto bHadVelocity{LocomotionState.bHasVelocity};
|
||
|
||
LocomotionState.Velocity = OwnerPawn->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_CharacterMovementSystemComponent::RefreshDynamicMovementState()
|
||
{
|
||
if (bAllowRefreshCharacterMovementSettings)
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (IsValid(SpeedToMovementStateCurve) && OwnerPawn->HasAuthority())
|
||
{
|
||
int32 Index = UKismetMathLibrary::Round(SpeedToMovementStateCurve->GetFloatValue(LocomotionState.Speed));
|
||
FGMS_MovementStateSetting TempSetting;
|
||
if (ControlSetting->GetStateByIndex(Index, TempSetting))
|
||
{
|
||
SetDesiredMovement(TempSetting.Tag);
|
||
}
|
||
else
|
||
{
|
||
GMS_CLOG(Warning, "Found invalid index output from SpeedToMovementStateCurve, Index(%d) of movement definitions can't be found. dynamic adjust movement state failed! Actor:%s",
|
||
Index, *GetOwner()->GetName());
|
||
}
|
||
}
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::RefreshLocomotionLate(const float DeltaTime)
|
||
{
|
||
if (!LocomotionMode.IsValid())
|
||
{
|
||
RefreshTargetYawAngleUsingActorRotation();
|
||
}
|
||
|
||
LocomotionState.bResetAimingLimit = !LocomotionState.bAimingLimitAppliedThisFrame;
|
||
}
|
||
|
||
#pragma endregion
|
||
|
||
void UGMS_CharacterMovementSystemComponent::RefreshRotation_Implementation(float DeltaTime)
|
||
{
|
||
RefreshGroundedRotation(DeltaTime);
|
||
RefreshInAirRotation(DeltaTime);
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::RefreshGroundedRotation(const float DeltaTime)
|
||
{
|
||
if (LocomotionMode != GMS_MovementModeTags::Grounded || GetGameplayTags().HasAny(GroundedRotationBlockingTags))
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (OwnerCharacter->HasAnyRootMotion())
|
||
{
|
||
RefreshTargetYawAngleUsingActorRotation();
|
||
return;
|
||
}
|
||
|
||
if (!LocomotionState.bMoving)
|
||
{
|
||
RefreshGroundedNotMovingRotation(DeltaTime);
|
||
}
|
||
else
|
||
{
|
||
RefreshGroundedMovingRotation(DeltaTime);
|
||
}
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::RefreshGroundedNotMovingRotation(float DeltaTime)
|
||
{
|
||
ApplyRotationYawSpeedAnimationCurve(DeltaTime);
|
||
|
||
if (RefreshCustomGroundedNotMovingRotation(DeltaTime))
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (RotationMode == GMS_RotationModeTags::ViewDirection)
|
||
{
|
||
if (const auto* Setting = ControlSetting->ViewDirectionSetting.GetPtr<FGMS_ViewDirectionSetting_Default>())
|
||
{
|
||
if (Setting->bEnableRotationWhenNotMoving)
|
||
{
|
||
// const auto& TargetYawAngle = LocomotionState.bHasInput ? ViewState.Rotation.Yaw : LocomotionState.TargetYawAngle;
|
||
SetRotationExtraSmooth(ViewState.Rotation.Yaw, DeltaTime, Setting->RotationInterpolationSpeed, Setting->TargetYawAngleRotationSpeed);
|
||
return;
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (const auto* Setting = ControlSetting->ViewDirectionSetting.GetPtr<FGMS_ViewDirectionSetting_Aiming>())
|
||
{
|
||
// refresh not moving aiming rotation.
|
||
{
|
||
if (Setting->bEnableRotationWhenNotMoving) // 吸附到视角
|
||
{
|
||
SetRotationExtraSmooth(ViewState.Rotation.Yaw, DeltaTime, Setting->RotationInterpolationSpeed, Setting->TargetYawAngleRotationSpeed);
|
||
return;
|
||
}
|
||
|
||
SetTargetYawAngle(ViewState.Rotation.Yaw);
|
||
|
||
FRotator NewActorRotation{GetOwner()->GetActorRotation()};
|
||
|
||
//Limit rotation so turn in place can catch up.
|
||
if (ConstrainAimingRotation(NewActorRotation, DeltaTime, true))
|
||
{
|
||
SetActorRotation(NewActorRotation);
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (RotationMode == GMS_RotationModeTags::VelocityDirection)
|
||
{
|
||
if (const auto* Setting = ControlSetting->VelocityDirectionSetting.GetPtr<FGMS_VelocityDirectionSetting_Default>())
|
||
{
|
||
if (Setting->bEnableRotationWhenNotMoving)
|
||
{
|
||
SetRotationExtraSmooth(LocomotionState.TargetYawAngle, DeltaTime, Setting->RotationInterpolationSpeed, Setting->TargetYawAngleRotationSpeed);
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (const auto* Setting = ControlSetting->VelocityDirectionSetting.GetPtr<FGMS_VelocityDirectionSetting_RateBased>())
|
||
{
|
||
if (Setting->bEnableRotationWhenNotMoving)
|
||
{
|
||
SetRotationInstant(DesiredVelocityYawAngle, ETeleportType::None);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
RefreshTargetYawAngleUsingActorRotation();
|
||
}
|
||
|
||
float UGMS_CharacterMovementSystemComponent::CalculateGroundedMovingRotationInterpolationSpeed(TObjectPtr<UCurveFloat> InterpolationSpeedCurve, float Default) const
|
||
{
|
||
// Calculate the rotation speed by using the rotation speed curve in the rotation settings. Using
|
||
// the curve in conjunction with the speed amount gives you a high level of control over the rotation
|
||
// rates for each speed. Increase the speed if the camera is rotating quickly for more responsive rotation.
|
||
|
||
const auto InterpolationHalfLife{
|
||
IsValid(InterpolationSpeedCurve)
|
||
? InterpolationSpeedCurve->GetFloatValue(FMath::Max(1.0f, GetMappedMovementSpeedLevel(UE_REAL_TO_FLOAT(LocomotionState.Velocity.Size2D()))))
|
||
: Default
|
||
};
|
||
|
||
static constexpr auto MinInterpolationHalfLifeMultiplier{0.333333f};
|
||
static constexpr auto ReferenceViewYawSpeed{300.0f};
|
||
|
||
return InterpolationHalfLife * UGMS_Math::LerpClamped(1.0f, MinInterpolationHalfLifeMultiplier,
|
||
ViewState.YawSpeed / ReferenceViewYawSpeed);
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::RefreshGroundedMovingRotation(float DeltaTime)
|
||
{
|
||
// Moving.
|
||
if (RefreshCustomGroundedMovingRotation(DeltaTime))
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (RotationMode == GMS_RotationModeTags::ViewDirection)
|
||
{
|
||
if (const auto* Setting = ControlSetting->ViewDirectionSetting.GetPtr<FGMS_ViewDirectionSetting_Default>())
|
||
{
|
||
//TODO maybe use root yaw offset here?
|
||
float RotationYawOffset = AnimationInstance->GetCurveValue(UGMS_Constants::RotationYawOffsetCurveName());
|
||
const auto& TargetYawAngle = UE_REAL_TO_FLOAT(ViewState.Rotation.Yaw + RotationYawOffset);
|
||
GMS_CLOG(VeryVerbose, "rotating to target yaw angle(%f) with Yaw offset(%f)", TargetYawAngle, RotationYawOffset);
|
||
|
||
const float CalculatedRotationInterpolationSpeed = CalculateGroundedMovingRotationInterpolationSpeed(Setting->RotationInterpolationSpeedCurve, Setting->RotationInterpolationSpeed);
|
||
SetRotationExtraSmooth(TargetYawAngle, DeltaTime, CalculatedRotationInterpolationSpeed, Setting->TargetYawAngleRotationSpeed);
|
||
return;
|
||
}
|
||
|
||
if (const auto* Setting = ControlSetting->ViewDirectionSetting.GetPtr<FGMS_ViewDirectionSetting_Aiming>())
|
||
{
|
||
//TODO Should use this if using root yaw offset?
|
||
FRotator NewActorRotation{GetOwner()->GetActorRotation()};
|
||
|
||
SetTargetYawAngleSmooth(UE_REAL_TO_FLOAT(ViewState.Rotation.Yaw), DeltaTime, Setting->TargetYawAngleRotationSpeed);
|
||
|
||
GMS_CLOG(VeryVerbose, "wants to aiming to target yaw angle(%f) with view yaw(%f)", NewActorRotation.Yaw, ViewState.Rotation.Yaw);
|
||
|
||
NewActorRotation.Yaw = UGMS_Rotation::DamperExactAngle(
|
||
UE_REAL_TO_FLOAT(FMath::UnwindDegrees(NewActorRotation.Yaw)), LocomotionState.SmoothTargetYawAngle, DeltaTime, Setting->RotationInterpolationSpeed);
|
||
if (ConstrainAimingRotation(NewActorRotation, DeltaTime))
|
||
{
|
||
// Cancel the extra smooth rotation, otherwise the actor will rotate too weirdly.
|
||
LocomotionState.SmoothTargetYawAngle = LocomotionState.TargetYawAngle;
|
||
}
|
||
|
||
GMS_CLOG(VeryVerbose, "aiming to target yaw angle(%f)", NewActorRotation.Yaw);
|
||
|
||
SetActorRotation(NewActorRotation);
|
||
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (RotationMode == GMS_RotationModeTags::VelocityDirection)
|
||
{
|
||
if (const auto* Setting = ControlSetting->VelocityDirectionSetting.GetPtr<FGMS_VelocityDirectionSetting_RateBased>())
|
||
{
|
||
SetRotationInstant(DesiredVelocityYawAngle, ETeleportType::None);
|
||
return;
|
||
}
|
||
|
||
if (const auto* Setting = ControlSetting->VelocityDirectionSetting.GetPtr<FGMS_VelocityDirectionSetting_Default>())
|
||
{
|
||
if (LocomotionState.bHasInput)
|
||
{
|
||
const auto& TargetRotation = Setting->bOrientateToMoveInputIntent ? LocomotionState.InputYawAngle : LocomotionState.VelocityYawAngle;
|
||
const float CalculatedRotationInterpolationSpeed = CalculateGroundedMovingRotationInterpolationSpeed(Setting->RotationInterpolationSpeedCurve, Setting->RotationInterpolationSpeed);
|
||
GMS_CLOG(VeryVerbose, "rotating to target yaw angle(%f) at rotation speed(%f)", TargetRotation, CalculatedRotationInterpolationSpeed);
|
||
SetRotationExtraSmooth(TargetRotation, DeltaTime, CalculatedRotationInterpolationSpeed, Setting->TargetYawAngleRotationSpeed);
|
||
}
|
||
return;
|
||
}
|
||
}
|
||
|
||
RefreshTargetYawAngleUsingActorRotation();
|
||
}
|
||
|
||
bool UGMS_CharacterMovementSystemComponent::ConstrainAimingRotation(FRotator& ActorRotation, float DeltaTime, bool bApplySecondaryConstraint)
|
||
{
|
||
const FGMS_ViewDirectionSetting_Aiming* Setting = ControlSetting->ViewDirectionSetting.GetPtr<FGMS_ViewDirectionSetting_Aiming>();
|
||
if (Setting == nullptr)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
LocomotionState.bAimingLimitAppliedThisFrame = true;
|
||
|
||
if (LocomotionState.bResetAimingLimit)
|
||
{
|
||
LocomotionState.AimingYawAngleLimit = 180.0f;
|
||
}
|
||
|
||
// Limit the actor's rotation when aiming to prevent situations where the lower body noticeably
|
||
// fails to keep up with the rotation of the upper body when the camera is rotating very fast.
|
||
|
||
float ViewRelativeAngle{FRotator3f::NormalizeAxis(UE_REAL_TO_FLOAT(ViewState.Rotation.Yaw - ActorRotation.Yaw))};
|
||
|
||
if (FMath::Abs(ViewRelativeAngle) <= Setting->MinAimingYawAngleLimit + UE_KINDA_SMALL_NUMBER)
|
||
{
|
||
LocomotionState.AimingYawAngleLimit = Setting->MinAimingYawAngleLimit;
|
||
return false;
|
||
}
|
||
|
||
ViewRelativeAngle = UGMS_Rotation::RemapAngleForCounterClockwiseRotation(ViewRelativeAngle);
|
||
|
||
// Secondary constraint. Simply increases the actor's rotation speed. Typically only used when the actor is standing still.
|
||
|
||
if (bApplySecondaryConstraint)
|
||
{
|
||
static constexpr auto RotationInterpolationHalfLife{0.1f};
|
||
|
||
// Interpolate the angle only to the point where the constraints no longer apply to ensure a smoother completion of the rotation.
|
||
|
||
const auto TargetViewRelativeAngle{
|
||
FMath::Clamp(ViewRelativeAngle, -Setting->MinAimingYawAngleLimit,
|
||
Setting->MinAimingYawAngleLimit)
|
||
};
|
||
|
||
|
||
const auto DeltaAngle{FMath::UnwindDegrees(TargetViewRelativeAngle - ViewRelativeAngle)};
|
||
|
||
if (FMath::IsNearlyZero(DeltaAngle, UE_KINDA_SMALL_NUMBER))
|
||
{
|
||
ViewRelativeAngle = TargetViewRelativeAngle;
|
||
}
|
||
else
|
||
{
|
||
const auto InterpolationAmount{UGMS_Math::DamperExactAlpha(DeltaTime, RotationInterpolationHalfLife)};
|
||
ViewRelativeAngle = FMath::UnwindDegrees(ViewRelativeAngle + DeltaAngle * InterpolationAmount);
|
||
}
|
||
}
|
||
|
||
// Primary constraint. Prevents the actor from rotating beyond a certain angle relative to the camera.
|
||
|
||
if (FMath::Abs(ViewRelativeAngle) > LocomotionState.AimingYawAngleLimit + UE_KINDA_SMALL_NUMBER)
|
||
{
|
||
ViewRelativeAngle = FMath::Clamp(ViewRelativeAngle, -LocomotionState.AimingYawAngleLimit, LocomotionState.AimingYawAngleLimit);
|
||
}
|
||
else
|
||
{
|
||
LocomotionState.AimingYawAngleLimit = FMath::Max(FMath::Abs(ViewRelativeAngle), Setting->MinAimingYawAngleLimit);
|
||
}
|
||
|
||
const auto PreviousActorYawAngle{ActorRotation.Yaw};
|
||
|
||
ActorRotation.Yaw = FMath::UnwindDegrees(UE_REAL_TO_FLOAT(ViewState.Rotation.Yaw - ViewRelativeAngle));
|
||
|
||
// We use UE_KINDA_SMALL_NUMBER here because even if ViewRelativeAngle hasn't
|
||
// changed, converting it back to ActorRotation.Yaw may introduce a rounding
|
||
// error, and FMath::IsNearlyZero() with default arguments will return false.
|
||
|
||
return !FMath::IsNearlyZero(FMath::UnwindDegrees(ActorRotation.Yaw - PreviousActorYawAngle), UE_KINDA_SMALL_NUMBER);
|
||
}
|
||
|
||
bool UGMS_CharacterMovementSystemComponent::ApplyRotationYawSpeedAnimationCurve(float DeltaTime)
|
||
{
|
||
// 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)
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
const float CurveValue = AnimationInstance->GetCurveValue(UGMS_Constants::RotationYawSpeedCurveName());
|
||
const float DeltaYawAngle{CurveValue * DeltaTime};
|
||
|
||
if (FMath::Abs(DeltaYawAngle) > UE_SMALL_NUMBER)
|
||
{
|
||
auto NewActorRotation{GetOwner()->GetActorRotation()};
|
||
NewActorRotation.Yaw += DeltaYawAngle;
|
||
|
||
SetActorRotation(NewActorRotation);
|
||
|
||
RefreshTargetYawAngleUsingActorRotation();
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
bool UGMS_CharacterMovementSystemComponent::RefreshCustomGroundedMovingRotation_Implementation(float DeltaTime)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
bool UGMS_CharacterMovementSystemComponent::RefreshCustomGroundedNotMovingRotation_Implementation(float DeltaTime)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::RefreshInAirRotation(const float DeltaTime)
|
||
{
|
||
if (LocomotionMode != GMS_MovementModeTags::InAir || GetGameplayTags().HasAny(InAirRotationBlockingTags))
|
||
{
|
||
return;
|
||
}
|
||
|
||
if (RotationMode == GMS_RotationModeTags::VelocityDirection || RotationMode == GMS_RotationModeTags::ViewDirection)
|
||
{
|
||
switch (ControlSetting->InAirRotationMode)
|
||
{
|
||
case EGMS_InAirRotationMode::RotateToVelocityOnJump:
|
||
if (LocomotionState.bMoving)
|
||
{
|
||
SetRotationSmooth(LocomotionState.VelocityYawAngle, DeltaTime, ControlSetting->InAirRotationInterpolationSpeed);
|
||
}
|
||
else
|
||
{
|
||
RefreshTargetYawAngleUsingActorRotation();
|
||
}
|
||
break;
|
||
|
||
case EGMS_InAirRotationMode::KeepRelativeRotation:
|
||
SetRotationSmooth(
|
||
FRotator3f::NormalizeAxis(UE_REAL_TO_FLOAT(ViewState.Rotation.Yaw) - LocomotionState.ViewRelativeTargetYawAngle),
|
||
DeltaTime, ControlSetting->InAirRotationInterpolationSpeed);
|
||
break;
|
||
|
||
default:
|
||
RefreshTargetYawAngleUsingActorRotation();
|
||
break;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
RefreshTargetYawAngleUsingActorRotation();
|
||
}
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::SetRotationInstant_Implementation(const float TargetYawAngle, const ETeleportType Teleport)
|
||
{
|
||
SetTargetYawAngle(TargetYawAngle);
|
||
|
||
auto NewRotation{GetOwner()->GetActorRotation()};
|
||
NewRotation.Yaw = TargetYawAngle;
|
||
|
||
SetActorRotation(NewRotation);
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::SetRotationSmooth_Implementation(const float TargetYawAngle, const float DeltaTime, const float InterpolationHalfLife)
|
||
{
|
||
SetTargetYawAngle(TargetYawAngle);
|
||
|
||
auto DesiredRotation{OwnerPawn->GetActorRotation()};
|
||
DesiredRotation.Yaw = UGMS_Rotation::DamperExactAngle(UE_REAL_TO_FLOAT(FMath::UnwindDegrees(DesiredRotation.Yaw)),
|
||
LocomotionState.SmoothTargetYawAngle, DeltaTime, InterpolationHalfLife);
|
||
|
||
|
||
SetActorRotation(DesiredRotation);
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::SetRotationExtraSmooth_Implementation(const float TargetYawAngle, const float DeltaTime,
|
||
const float InterpolationHalfLife, const float TargetYawAngleRotationSpeed)
|
||
{
|
||
SetTargetYawAngleSmooth(TargetYawAngle, DeltaTime, TargetYawAngleRotationSpeed);
|
||
|
||
auto DesiredRotation{OwnerPawn->GetActorRotation()};
|
||
DesiredRotation.Yaw = UGMS_Rotation::DamperExactAngle(UE_REAL_TO_FLOAT(FMath::UnwindDegrees(DesiredRotation.Yaw)),
|
||
LocomotionState.SmoothTargetYawAngle, DeltaTime, InterpolationHalfLife);
|
||
|
||
SetActorRotation(DesiredRotation);
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::RefreshTargetYawAngleUsingActorRotation()
|
||
{
|
||
const auto YawAngle{UE_REAL_TO_FLOAT(OwnerPawn->GetActorRotation().Yaw)};
|
||
|
||
SetTargetYawAngle(YawAngle);
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::SetTargetYawAngle(const float TargetYawAngle)
|
||
{
|
||
LocomotionState.TargetYawAngle = FMath::UnwindDegrees(TargetYawAngle);
|
||
|
||
RefreshViewRelativeTargetYawAngle();
|
||
|
||
LocomotionState.SmoothTargetYawAngle = LocomotionState.TargetYawAngle;
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::SetTargetYawAngleSmooth(float TargetYawAngle, float DeltaTime, float RotationSpeed)
|
||
{
|
||
LocomotionState.TargetYawAngle = FMath::UnwindDegrees(TargetYawAngle);
|
||
|
||
LocomotionState.SmoothTargetYawAngle = UGMS_Rotation::InterpolateAngleConstant(
|
||
LocomotionState.SmoothTargetYawAngle, LocomotionState.TargetYawAngle, DeltaTime, RotationSpeed);
|
||
|
||
RefreshViewRelativeTargetYawAngle();
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::RefreshViewRelativeTargetYawAngle()
|
||
{
|
||
LocomotionState.ViewRelativeTargetYawAngle = FMath::UnwindDegrees(UE_REAL_TO_FLOAT(
|
||
ViewState.Rotation.Yaw - LocomotionState.TargetYawAngle));
|
||
}
|
||
|
||
void UGMS_CharacterMovementSystemComponent::SetActorRotation(FRotator DesiredRotation)
|
||
{
|
||
const bool bWantsToBeVertical = CharacterMovement->ShouldRemainVertical();
|
||
|
||
if (bWantsToBeVertical)
|
||
{
|
||
DesiredRotation.Pitch = 0.f;
|
||
DesiredRotation.Yaw = FMath::UnwindDegrees(DesiredRotation.Yaw);
|
||
DesiredRotation.Roll = 0.f;
|
||
}
|
||
else
|
||
{
|
||
DesiredRotation.Normalize();
|
||
}
|
||
OwnerPawn->SetActorRotation(DesiredRotation);
|
||
}
|
||
|
||
FGMS_PredictGroundMovementPivotLocationParams UGMS_CharacterMovementSystemComponent::GetPredictGroundMovementPivotLocationParams() const
|
||
{
|
||
FGMS_PredictGroundMovementPivotLocationParams Params;
|
||
if (CharacterMovement)
|
||
{
|
||
Params.Acceleration = CharacterMovement->GetCurrentAcceleration();
|
||
Params.Velocity = CharacterMovement->GetLastUpdateVelocity();
|
||
Params.GroundFriction = CharacterMovement->GroundFriction;
|
||
}
|
||
return Params;
|
||
}
|
||
|
||
FGMS_PredictGroundMovementStopLocationParams UGMS_CharacterMovementSystemComponent::GetPredictGroundMovementStopLocationParams() const
|
||
{
|
||
FGMS_PredictGroundMovementStopLocationParams Params;
|
||
if (CharacterMovement)
|
||
{
|
||
Params.Velocity = CharacterMovement->GetLastUpdateVelocity();
|
||
Params.bUseSeparateBrakingFriction = CharacterMovement->bUseSeparateBrakingFriction;
|
||
Params.BrakingFriction = CharacterMovement->BrakingFriction;
|
||
Params.GroundFriction = CharacterMovement->GroundFriction;
|
||
Params.BrakingFrictionFactor = CharacterMovement->BrakingFrictionFactor;
|
||
Params.BrakingDecelerationWalking = CharacterMovement->BrakingDecelerationWalking;
|
||
}
|
||
return Params;
|
||
}
|