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