第一次提交

This commit is contained in:
不明不惑
2026-03-03 01:23:02 +08:00
commit 3e434877e8
1053 changed files with 102411 additions and 0 deletions

View File

@@ -0,0 +1,898 @@
// 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