899 lines
24 KiB
C++
899 lines
24 KiB
C++
// 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
|