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

899 lines
24 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_MovementSystemComponent.h"
#include "GameFramework/Pawn.h"
#include "Engine/World.h"
#include "TimerManager.h"
#include "GameplayTagAssetInterface.h"
#include "GameFramework/Actor.h"
#include "GameFramework/Pawn.h"
#include "Animation/AnimInstance.h"
#include "Misc/DataValidation.h"
#include "Net/Core/PushModel/PushModel.h"
#include "Net/UnrealNetwork.h"
#include "Settings/GMS_SettingObjectLibrary.h"
#include "Utility/GMS_Log.h"
#include "Utility/GMS_Math.h"
#include "Utility/GMS_Vector.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_MovementSystemComponent)
UGMS_MovementSystemComponent::UGMS_MovementSystemComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
PrimaryComponentTick.bCanEverTick = true;
bWantsInitializeComponent = true;
}
void UGMS_MovementSystemComponent::PostLoad()
{
Super::PostLoad();
#if WITH_EDITOR
PRAGMA_DISABLE_DEPRECATION_WARNINGS
if (!MovementDefinitions.IsEmpty())
{
MovementDefinition = MovementDefinitions.Last();
MovementDefinitions.Empty();
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
#endif
}
void UGMS_MovementSystemComponent::InitializeComponent()
{
Super::InitializeComponent();
OwnerPawn = Cast<APawn>(GetOwner());
check(OwnerPawn)
if (OwnerPawn)
{
// Set some default values here to ensure that the animation instance and the
// camera component can read the most up-to-date values during their initialization.
SetReplicatedViewRotation(OwnerPawn->GetViewRotation().GetNormalized(), false);
ViewState.Rotation = ReplicatedViewRotation;
ViewState.PreviousYawAngle = UE_REAL_TO_FLOAT(OwnerPawn->GetViewRotation().Yaw);
const auto YawAngle{UE_REAL_TO_FLOAT(OwnerPawn->GetActorRotation().Yaw)};
LocomotionState.InputYawAngle = YawAngle;
LocomotionState.VelocityYawAngle = YawAngle;
}
}
void UGMS_MovementSystemComponent::BeginPlay()
{
Super::BeginPlay();
if (GetOwner()->GetClass()->ImplementsInterface(UGameplayTagAssetInterface::StaticClass()))
{
SetGameplayTagsProvider(GetOwner());
}
else
{
TArray<UActorComponent*> Components = GetOwner()->GetComponentsByInterface(UGameplayTagAssetInterface::StaticClass());
if (Components.IsValidIndex(0))
{
SetGameplayTagsProvider(Components[0]);
}
}
}
void UGMS_MovementSystemComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
FDoRepLifetimeParams Parameters;
Parameters.bIsPushBased = true;
Parameters.Condition = COND_SkipOwner;
DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, LocomotionMode, Parameters)
DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, OverlayMode, Parameters)
DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, MovementSet, Parameters)
DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, MovementDefinition, Parameters)
DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, ReplicatedViewRotation, Parameters)
DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, DesiredVelocityYawAngle, Parameters)
DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, OwnedTags, Parameters)
}
UGMS_MovementSystemComponent* UGMS_MovementSystemComponent::GetMovementSystemComponent(const AActor* Actor)
{
return Actor != nullptr ? Actor->FindComponentByClass<UGMS_MovementSystemComponent>() : nullptr;
}
bool UGMS_MovementSystemComponent::K2_FindMovementComponent(const AActor* Actor, UGMS_MovementSystemComponent*& Instance)
{
if (Actor == nullptr)
{
return false;
}
Instance = GetMovementSystemComponent(Actor);
return Instance != nullptr;
}
bool UGMS_MovementSystemComponent::K2_FindMovementComponentExt(const AActor* Actor, TSubclassOf<UGMS_MovementSystemComponent> DesiredClass, UGMS_MovementSystemComponent*& Instance)
{
if (DesiredClass)
{
Instance = GetMovementSystemComponent(Actor);
return Instance != nullptr && Instance->GetClass()->IsChildOf(DesiredClass);
}
return false;
}
FGameplayTagContainer UGMS_MovementSystemComponent::GetGameplayTags() const
{
if (IGameplayTagAssetInterface* TagAssetInterface = Cast<IGameplayTagAssetInterface>(GameplayTagsProvider))
{
FGameplayTagContainer RetTags;
TagAssetInterface->GetOwnedGameplayTags(RetTags);
if (!OwnedTags.IsEmpty())
{
RetTags.AppendTags(OwnedTags);
}
RetTags.AddTagFast(GetMovementState());
RetTags.AddTagFast(GetRotationMode());
return RetTags;
}
return OwnedTags;
}
void UGMS_MovementSystemComponent::SetGameplayTagsProvider(UObject* Provider)
{
if (!IsValid(Provider))
{
GMS_CLOG(Warning, "Passed invalid GameplayTagsProvider.");
return;
}
if (IGameplayTagAssetInterface* TagAssetInterface = Cast<IGameplayTagAssetInterface>(Provider))
{
GameplayTagsProvider = Provider;
}
else
{
GMS_CLOG(Warning, "Passed in GameplayTagsProvider(%s) Doesn't implement GameplayTagAssetInterface, it can't provide gameplay tags.", *Provider->GetName());
}
}
#pragma region GameplayTags
void UGMS_MovementSystemComponent::AddGameplayTag(FGameplayTag TagToAdd)
{
AddGameplayTag(TagToAdd, true);
}
void UGMS_MovementSystemComponent::RemoveGameplay(FGameplayTag TagToRemove)
{
RemoveGameplayTag(TagToRemove, true);
}
void UGMS_MovementSystemComponent::SetGameplayTags(FGameplayTagContainer TagsToSet)
{
SetGameplayTags(TagsToSet, true);
}
void UGMS_MovementSystemComponent::AddGameplayTag(const FGameplayTag& TagToAdd, bool bSendRpc)
{
if (GetOwner()->GetLocalRole() <= ROLE_SimulatedProxy)
{
return;
}
OwnedTags.AddTag(TagToAdd);
MARK_PROPERTY_DIRTY_FROM_NAME(ThisClass, OwnedTags, this)
if (bSendRpc)
{
if (GetOwner()->GetLocalRole() >= ROLE_Authority)
{
ClientAddGameplayTag(TagToAdd);
}
else
{
ServerAddGameplayTag(TagToAdd);
}
}
}
void UGMS_MovementSystemComponent::RemoveGameplayTag(const FGameplayTag& TagToRemove, bool bSendRpc)
{
if (GetOwner()->GetLocalRole() <= ROLE_SimulatedProxy)
{
return;
}
OwnedTags.RemoveTag(TagToRemove);
MARK_PROPERTY_DIRTY_FROM_NAME(ThisClass, OwnedTags, this)
if (bSendRpc)
{
if (GetOwner()->GetLocalRole() >= ROLE_Authority)
{
ClientRemoveGameplayTag(TagToRemove);
}
else
{
ServerRemoveGameplayTag(TagToRemove);
}
}
}
void UGMS_MovementSystemComponent::SetGameplayTags(const FGameplayTagContainer& TagsToSet, bool bSendRpc)
{
if (GetOwner()->GetLocalRole() <= ROLE_SimulatedProxy)
{
return;
}
OwnedTags = TagsToSet;
MARK_PROPERTY_DIRTY_FROM_NAME(ThisClass, OwnedTags, this)
if (bSendRpc)
{
if (GetOwner()->GetLocalRole() >= ROLE_Authority)
{
ClientSetGameplayTags(TagsToSet);
}
else
{
ServerSetGameplayTags(TagsToSet);
}
}
}
void UGMS_MovementSystemComponent::ClientAddGameplayTag_Implementation(const FGameplayTag& TagToAdd)
{
AddGameplayTag(TagToAdd, false);
}
void UGMS_MovementSystemComponent::ServerAddGameplayTag_Implementation(const FGameplayTag& TagToAdd)
{
AddGameplayTag(TagToAdd, false);
}
void UGMS_MovementSystemComponent::ClientRemoveGameplayTag_Implementation(const FGameplayTag& TagToRemove)
{
RemoveGameplayTag(TagToRemove, false);
}
void UGMS_MovementSystemComponent::ServerRemoveGameplayTag_Implementation(const FGameplayTag& TagToRemove)
{
RemoveGameplayTag(TagToRemove, false);
}
void UGMS_MovementSystemComponent::ClientSetGameplayTags_Implementation(const FGameplayTagContainer& TagsToSet)
{
SetGameplayTags(TagsToSet, false);
}
void UGMS_MovementSystemComponent::ServerSetGameplayTags_Implementation(const FGameplayTagContainer& TagsToSet)
{
SetGameplayTags(TagsToSet, false);
}
#pragma endregion GameplayTags
#pragma region Locomotion
const FGMS_LocomotionState& UGMS_MovementSystemComponent::GetLocomotionState() const
{
return LocomotionState;
}
TScriptInterface<IPoseSearchTrajectoryPredictorInterface> UGMS_MovementSystemComponent::GetTrajectoryPredictor() const
{
return nullptr;
}
bool UGMS_MovementSystemComponent::IsCrouching() const
{
return false;
}
float UGMS_MovementSystemComponent::GetMaxSpeed() const
{
return 0.0;
}
float UGMS_MovementSystemComponent::GetMaxAcceleration() const
{
return 1000.0f;
}
bool UGMS_MovementSystemComponent::IsMovingOnGround() const
{
return true;
}
void UGMS_MovementSystemComponent::SetDesiredVelocityYawAngle(float NewDesiredVelocityYawAngle)
{
COMPARE_ASSIGN_AND_MARK_PROPERTY_DIRTY(ThisClass, DesiredVelocityYawAngle, NewDesiredVelocityYawAngle, this);
}
void UGMS_MovementSystemComponent::ServerSetDesiredVelocityYawAngle_Implementation(float NewDesiredVelocityYawAngle)
{
SetDesiredVelocityYawAngle(NewDesiredVelocityYawAngle);
}
const FGameplayTag& UGMS_MovementSystemComponent::GetLocomotionMode() const
{
return LocomotionMode;
}
const FGMS_MovementBaseState& UGMS_MovementSystemComponent::GetMovementBase() const
{
return MovementBase;
}
void UGMS_MovementSystemComponent::SetLocomotionMode(const FGameplayTag& NewLocomotionMode)
{
if (LocomotionMode != NewLocomotionMode)
{
const auto PreviousLocomotionMode{LocomotionMode};
LocomotionMode = NewLocomotionMode;
MARK_PROPERTY_DIRTY_FROM_NAME(ThisClass, LocomotionMode, this)
OnLocomotionModeChanged(PreviousLocomotionMode);
}
}
void UGMS_MovementSystemComponent::OnReplicated_LocomotionMode(const FGameplayTag& PreviousLocomotionMode)
{
OnLocomotionModeChanged(PreviousLocomotionMode);
}
void UGMS_MovementSystemComponent::OnLocomotionModeChanged_Implementation(const FGameplayTag& PreviousLocomotionMode)
{
GMS_CLOG(Verbose, "locomotion mode changed from %s to %s", *PreviousLocomotionMode.ToString(), *LocomotionMode.ToString());
OnLocomotionModeChangedEvent.Broadcast(PreviousLocomotionMode);
}
#pragma endregion
void UGMS_MovementSystemComponent::RefreshMovementBase()
{
}
#pragma region MovementSet
const FGameplayTag& UGMS_MovementSystemComponent::GetMovementSet() const
{
return MovementSet;
}
const FGMS_MovementSetSetting& UGMS_MovementSystemComponent::GetMovementSetSetting() const
{
return MovementSetSetting;
}
const FGMS_MovementStateSetting& UGMS_MovementSystemComponent::GetMovementStateSetting() const
{
return MovementStateSetting;
}
const UGMS_MovementControlSetting_Default* UGMS_MovementSystemComponent::GetControlSetting() const
{
return ControlSetting;
}
int32 UGMS_MovementSystemComponent::GetNumOfMovementStateSettings() const
{
return ControlSetting->MovementStates.Num();
}
TSoftObjectPtr<const UGMS_MovementDefinition> UGMS_MovementSystemComponent::GetMovementDefinition() const
{
return MovementDefinition;
}
TSoftObjectPtr<const UGMS_MovementDefinition> UGMS_MovementSystemComponent::GetPrevMovementDefinition() const
{
return PrevMovementDefinition;
}
const UGMS_MovementDefinition* UGMS_MovementSystemComponent::GetLoadedMovementDefinition() const
{
return MovementDefinition.IsValid() ? MovementDefinition.Get() : nullptr;
}
void UGMS_MovementSystemComponent::SetMovementSet(const FGameplayTag& NewMovementSet)
{
SetMovementSet(NewMovementSet, true);
}
void UGMS_MovementSystemComponent::SetMovementDefinition(TSoftObjectPtr<UGMS_MovementDefinition> NewDefinition)
{
if (NewDefinition.IsNull())
{
return;
}
if (NewDefinition != MovementDefinition)
{
auto LoadedDefinition = NewDefinition.LoadSynchronous();
if (LoadedDefinition->MovementSets.Contains(MovementSet))
{
InternalSetMovementDefinition(NewDefinition, true);
}
}
}
void UGMS_MovementSystemComponent::LocalSetMovementDefinition(TSoftObjectPtr<UGMS_MovementDefinition> NewDefinition)
{
InternalSetMovementDefinition(NewDefinition, false);
}
void UGMS_MovementSystemComponent::PushAvailableMovementDefinition(TSoftObjectPtr<UGMS_MovementDefinition> NewDefinition, bool bPopCurrent)
{
InternalSetMovementDefinition(NewDefinition, true);
}
void UGMS_MovementSystemComponent::PopAvailableMovementDefinition()
{
// PopMovementDefinition(true);
}
void UGMS_MovementSystemComponent::InternalSetMovementDefinition(const TSoftObjectPtr<UGMS_MovementDefinition> NewDefinition, bool bSendRpc)
{
if (!NewDefinition.IsNull() && NewDefinition != MovementDefinition)
{
PrevMovementDefinition = MovementDefinition;
MovementDefinition = NewDefinition;
MARK_PROPERTY_DIRTY_FROM_NAME(ThisClass, MovementDefinition, this)
OnMovementSetChanged(MovementSet);
if (bSendRpc)
{
if (GetOwner()->GetLocalRole() >= ROLE_Authority)
{
ClientSetMovementDefinition(NewDefinition);
}
else
{
ServerSetMovementDefinition(NewDefinition);
}
}
}
}
void UGMS_MovementSystemComponent::ClientSetMovementDefinition_Implementation(const TSoftObjectPtr<UGMS_MovementDefinition>& NewDefinition)
{
InternalSetMovementDefinition(NewDefinition, false);
}
void UGMS_MovementSystemComponent::ServerSetMovementDefinition_Implementation(const TSoftObjectPtr<UGMS_MovementDefinition>& NewDefinition)
{
InternalSetMovementDefinition(NewDefinition, false);
}
void UGMS_MovementSystemComponent::OnReplicated_MovementDefinition()
{
OnMovementSetChanged(MovementSet);
}
void UGMS_MovementSystemComponent::SetMovementSet(const FGameplayTag& NewMovementSet, bool bSendRpc)
{
if (MovementSet == NewMovementSet || GetOwner()->GetLocalRole() <= ROLE_SimulatedProxy)
{
return;
}
const auto PreviousMovementSet{MovementSet};
MovementSet = NewMovementSet;
MARK_PROPERTY_DIRTY_FROM_NAME(ThisClass, MovementSet, this)
OnMovementSetChanged(PreviousMovementSet);
if (bSendRpc)
{
if (GetOwner()->GetLocalRole() >= ROLE_Authority)
{
ClientSetMovementSet(MovementSet);
}
else
{
ServerSetMovementSet(MovementSet);
}
}
}
void UGMS_MovementSystemComponent::ClientSetMovementSet_Implementation(const FGameplayTag& NewMovementSet)
{
SetMovementSet(NewMovementSet, false);
}
void UGMS_MovementSystemComponent::ServerSetMovementSet_Implementation(const FGameplayTag& NewMovementSet)
{
SetMovementSet(NewMovementSet, false);
}
void UGMS_MovementSystemComponent::OnReplicated_MovementSet(const FGameplayTag& PreviousMovementSet)
{
OnMovementSetChanged(PreviousMovementSet);
}
void UGMS_MovementSystemComponent::RefreshMovementSetSetting()
{
const UGMS_MovementDefinition* LoadedDefinition = MovementDefinition.LoadSynchronous();
if (!LoadedDefinition)
{
GMS_CLOG(Warning, "Missing valid movement definition!")
return;
}
if (!LoadedDefinition->MovementSets.Contains(MovementSet))
{
GMS_CLOG(Warning, "No movement set(%s) found in movement definition(%s)!", *MovementSet.ToString(), *GetNameSafe(LoadedDefinition))
return;
}
MovementSetSetting = MovementDefinition->MovementSets[MovementSet];
RefreshControlSetting();
// bool bFoundMovementSet{false};
// for (int32 i = MovementDefinitions.Num() - 1; i >= 0; i--)
// {
// if (MovementDefinitions[i].IsNull())
// {
// continue;
// }
// if (!MovementDefinitions[i].IsValid())
// {
// MovementDefinitions[i].LoadSynchronous();
// }
// if (MovementDefinitions[i]->MovementSets.Contains(MovementSet))
// {
// MovementDefinition = MovementDefinitions[i].Get();
// MovementSetSetting = MovementDefinition->MovementSets[MovementSet];
// bFoundMovementSet = true;
// RefreshControlSetting();
// break;
// }
// }
// if (!bFoundMovementSet)
// {
// GMS_CLOG(Error, "No movement set(%s) found in movement definitions!", *MovementSet.ToString())
// }
}
void UGMS_MovementSystemComponent::RefreshControlSetting()
{
const UGMS_MovementControlSetting_Default* NewSetting = MovementSetSetting.bControlSettingPerOverlayMode && MovementSetSetting.ControlSettings.Contains(OverlayMode)
? MovementSetSetting.ControlSettings[OverlayMode]
: MovementSetSetting.ControlSetting;
if (NewSetting != nullptr && !NewSetting->MovementStates.IsEmpty())
{
ControlSetting = NewSetting;
RefreshMovementStateSetting();
ApplyMovementSetting();
}
else
{
ControlSetting = nullptr;
GMS_CLOG(Error, "Empty MovementState settings are found in the movement set(%s) of definition(%s), which is not allowed!", *MovementSet.ToString(),
*MovementDefinition->GetName())
}
}
void UGMS_MovementSystemComponent::OnMovementSetChanged_Implementation(const FGameplayTag& PreviousMovementSet)
{
RefreshMovementSetSetting();
OnMovementSetChangedEvent.Broadcast(PreviousMovementSet);
}
const FGameplayTag& UGMS_MovementSystemComponent::GetDesiredMovementState() const
{
return FGameplayTag::EmptyTag;
}
void UGMS_MovementSystemComponent::RefreshMovementStateSetting()
{
if (!IsValid(ControlSetting))
{
return;
}
FGMS_MovementStateSetting NewStateSetting;
if (!ControlSetting->GetStateByTag(GetMovementState(), NewStateSetting))
{
checkf(!ControlSetting->MovementStates.IsEmpty(), TEXT("Found empty MovementState Settings on %s!"), *ControlSetting->GetName())
NewStateSetting = ControlSetting->MovementStates.Last();
SetDesiredMovement(NewStateSetting.Tag);
GMS_CLOG(Verbose, "No MovementState setting for current movement state(%s), Change desired last one(%s) in list.", *GetMovementState().ToString(),
*NewStateSetting.Tag.ToString());
}
MovementStateSetting = NewStateSetting;
if (bRespectAllowedRotationModesSettings)
{
if (!MovementStateSetting.AllowedRotationModes.Contains(GetDesiredRotationMode()))
{
FGameplayTag AdjustedRotationMode = MovementStateSetting.AllowedRotationModes.Last();
GMS_CLOG(Warning, "current movement state(%s) doesn't allow current desired rotation mode(%s), adjusted to:%s", *GetMovementState().ToString(), *GetDesiredRotationMode().ToString(),
*AdjustedRotationMode.ToString());
SetDesiredRotationMode(AdjustedRotationMode);
}
}
}
void UGMS_MovementSystemComponent::ApplyMovementSetting()
{
}
#pragma endregion
#pragma region MovementState
void UGMS_MovementSystemComponent::SetDesiredMovement(const FGameplayTag& NewDesiredMovement)
{
}
void UGMS_MovementSystemComponent::CycleDesiredMovementState(bool bForward)
{
if (GetNumOfMovementStateSettings() == 1)
{
return;
}
int32 Index = ControlSetting->MovementStates.IndexOfByKey(GetMovementState());
if (Index == INDEX_NONE)
{
return;
}
if (bForward && ControlSetting->MovementStates.IsValidIndex(Index + 1))
{
SetDesiredMovement(ControlSetting->MovementStates[Index + 1].Tag);
}
if (!bForward && ControlSetting->MovementStates.IsValidIndex(Index - 1))
{
SetDesiredMovement(ControlSetting->MovementStates[Index - 1].Tag);
}
}
const FGameplayTag& UGMS_MovementSystemComponent::GetMovementState() const
{
return FGameplayTag::EmptyTag;
}
int32 UGMS_MovementSystemComponent::GetSpeedLevel() const
{
return MovementStateSetting.SpeedLevel;
}
float UGMS_MovementSystemComponent::GetMappedMovementSpeedLevel(float Speed) const
{
if (!ControlSetting)
{
return 0.0f;
}
float SpeedLevelAmount = ControlSetting->MovementStates.Last().SpeedLevel;
for (int32 i = ControlSetting->MovementStates.Num() - 2; i >= 0; i--)
{
const FGMS_MovementStateSetting& Max = ControlSetting->MovementStates[i + 1];
const FGMS_MovementStateSetting& Min = ControlSetting->MovementStates[i];
// In current range.
if (Min.Speed > 0 && Speed >= Min.Speed && Speed <= Max.Speed)
{
SpeedLevelAmount = FMath::GetMappedRangeValueClamped(FVector2f{Min.Speed, Max.Speed}, {static_cast<float>(Min.SpeedLevel), static_cast<float>(Max.SpeedLevel)}, Speed);
}
}
GMS_CLOG(VeryVerbose, "Mapped speed(%f) to speed level(%f)", Speed, SpeedLevelAmount)
return SpeedLevelAmount;
}
void UGMS_MovementSystemComponent::OnMovementStateChanged_Implementation(const FGameplayTag& PreviousMovementState)
{
RefreshMovementStateSetting();
ApplyMovementSetting();
OnMovementStateChangedEvent.Broadcast(PreviousMovementState);
}
#pragma endregion
#pragma region Input
FVector UGMS_MovementSystemComponent::GetMovementIntent() const
{
checkf(0, TEXT("Not implemented"));
return FVector::ZeroVector;
}
void UGMS_MovementSystemComponent::RefreshInput(float DeltaTime)
{
LocomotionState.bHasInput = GetMovementIntent().SizeSquared() > UE_KINDA_SMALL_NUMBER;
if (LocomotionState.bHasInput)
{
LocomotionState.InputYawAngle = UE_REAL_TO_FLOAT(UGMS_Vector::DirectionToAngleXY(GetMovementIntent()));
}
}
void UGMS_MovementSystemComponent::TurnAtRate(float Direction)
{
if (Direction != 0 && GetRotationMode() == GMS_RotationModeTags::VelocityDirection &&
OwnerPawn->GetLocalRole() >= ROLE_AutonomousProxy)
{
if (const auto Setting = ControlSetting->VelocityDirectionSetting.GetPtr<FGMS_VelocityDirectionSetting_RateBased>())
{
const float TurnRate =
IsValid(Setting->TurnRateSpeedCurve)
? Setting->TurnRateSpeedCurve->GetFloatValue(FMath::Max(1.0f, GetMappedMovementSpeedLevel(UE_REAL_TO_FLOAT(LocomotionState.Velocity.Size2D()))))
: Setting->TurnRate;
float YawDelta = Direction * TurnRate * GetWorld()->GetDeltaSeconds();
if (FMath::Abs(YawDelta) > UE_SMALL_NUMBER)
{
float TargetYawAngle = FMath::UnwindDegrees(OwnerPawn->GetActorRotation().Yaw + YawDelta);
// 或者方法2使用 RotateAngleAxis如果需要保持四元数精度
// FQuat DeltaRotation = FQuat(FVector::UpVector, FMath::DegreesToRadians(YawDelta));
// FQuat NewRotation = DeltaRotation * LocomotionState.RotationQuaternion;
// NewDesiredVelocityYawAngle = NewRotation.Rotator().Yaw;
// FRotator RotationDelta = FRotator(0,Direction * GetVelocityDirectionSetting().TurningRate * DeltaTime,0);
//
// NewDesiredVelocityYawAngle = (RotationDelta.Quaternion() * LocomotionState.RotationQuaternion).Rotator().Yaw;
SetDesiredVelocityYawAngle(TargetYawAngle);
if (OwnerPawn->GetLocalRole() < ROLE_Authority)
{
ServerSetDesiredVelocityYawAngle(TargetYawAngle);
}
}
}
}
}
#pragma endregion
#pragma region ViewSystem
const FGMS_ViewState& UGMS_MovementSystemComponent::GetViewState() const
{
return ViewState;
}
void UGMS_MovementSystemComponent::SetReplicatedViewRotation(const FRotator& NewViewRotation, bool bSendRpc)
{
if (!ReplicatedViewRotation.Equals(NewViewRotation))
{
ReplicatedViewRotation = NewViewRotation;
MARK_PROPERTY_DIRTY_FROM_NAME(ThisClass, ReplicatedViewRotation, this)
if (bSendRpc && GetOwner()->GetLocalRole() == ROLE_AutonomousProxy)
{
ServerSetReplicatedViewRotation(ReplicatedViewRotation);
}
}
}
void UGMS_MovementSystemComponent::ServerSetReplicatedViewRotation_Implementation(const FRotator& NewViewRotation)
{
SetReplicatedViewRotation(NewViewRotation, false);
}
void UGMS_MovementSystemComponent::OnReplicated_ReplicatedViewRotation()
{
ViewState.Rotation = MovementBase.bHasRelativeRotation ? (MovementBase.Rotation * ReplicatedViewRotation.Quaternion()).Rotator() : ReplicatedViewRotation;
}
#pragma endregion
#pragma region Rotation Mode
const FGameplayTag& UGMS_MovementSystemComponent::GetDesiredRotationMode() const
{
return FGameplayTag::EmptyTag;
}
void UGMS_MovementSystemComponent::SetDesiredRotationMode(const FGameplayTag& NewDesiredRotationMode)
{
}
const FGameplayTag& UGMS_MovementSystemComponent::GetRotationMode() const
{
return FGameplayTag::EmptyTag;
}
void UGMS_MovementSystemComponent::OnRotationModeChanged_Implementation(const FGameplayTag& PreviousRotationMode)
{
ApplyMovementSetting();
OnRotationModeChangedEvent.Broadcast(PreviousRotationMode);
}
#pragma endregion
#pragma region OverlayMode
const FGameplayTag& UGMS_MovementSystemComponent::GetOverlayMode() const
{
return OverlayMode;
}
void UGMS_MovementSystemComponent::SetOverlayMode(const FGameplayTag& NewOverlayMode)
{
SetOverlayMode(NewOverlayMode, true);
}
void UGMS_MovementSystemComponent::SetOverlayMode(const FGameplayTag& NewOverlayMode, bool bSendRpc)
{
if (OverlayMode == NewOverlayMode || OwnerPawn->GetLocalRole() <= ROLE_SimulatedProxy)
{
return;
}
const auto PreviousOverlayMode{OverlayMode};
OverlayMode = NewOverlayMode;
MARK_PROPERTY_DIRTY_FROM_NAME(ThisClass, OverlayMode, this)
OnOverlayModeChanged(PreviousOverlayMode);
if (bSendRpc)
{
if (OwnerPawn->GetLocalRole() >= ROLE_Authority)
{
ClientSetOverlayMode(OverlayMode);
}
else
{
ServerSetOverlayMode(OverlayMode);
}
}
}
void UGMS_MovementSystemComponent::ClientSetOverlayMode_Implementation(const FGameplayTag& NewOverlayMode)
{
SetOverlayMode(NewOverlayMode, false);
}
void UGMS_MovementSystemComponent::ServerSetOverlayMode_Implementation(const FGameplayTag& NewOverlayMode)
{
SetOverlayMode(NewOverlayMode, false);
}
void UGMS_MovementSystemComponent::OnReplicated_OverlayMode(const FGameplayTag& PreviousOverlayMode)
{
OnOverlayModeChanged(PreviousOverlayMode);
}
void UGMS_MovementSystemComponent::OnOverlayModeChanged_Implementation(const FGameplayTag& PreviousOverlayMode)
{
if (MovementSetSetting.bControlSettingPerOverlayMode)
{
RefreshControlSetting();
}
OnOverlayModeChangedEvent.Broadcast(PreviousOverlayMode);
}
#pragma endregion
#if WITH_EDITOR
EDataValidationResult UGMS_MovementSystemComponent::IsDataValid(class FDataValidationContext& Context) const
{
if (IsTemplate() && AnimGraphSetting == nullptr)
{
Context.AddError(FText::FromString("AnimGraphSetting is required!"));
return EDataValidationResult::Invalid;
}
return Super::IsDataValid(Context);
}
#endif