// 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& 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(); if (MoverComponent) { TrajectoryPredictor = NewObject(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(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(); MeshComponent = OwnerPawn->FindComponentByClass(); 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(); const FGMS_MoverMovementControlInputs* ControlInputs = InputCmd.InputCollection.FindDataByType(); 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()) { 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 UGMS_MoverMovementSystemComponent::GetTrajectoryPredictor() const { return TrajectoryPredictor; } bool UGMS_MoverMovementSystemComponent::IsCrouching() const { if (UCharacterMoverComponent* CharacterMover = Cast(MoverComponent)) { return CharacterMover->IsCrouching(); } return false; } float UGMS_MoverMovementSystemComponent::GetMaxSpeed() const { const UCommonLegacyMovementSettings* CommonLegacySettings = MoverComponent->FindSharedSettings(); return CommonLegacySettings->MaxSpeed; } float UGMS_MoverMovementSystemComponent::GetScaledCapsuleRadius() const { if (UCapsuleComponent* Capsule = Cast(MoverComponent->GetUpdatedComponent())) { return Capsule->GetScaledCapsuleRadius(); } return GetDefault()->GetUnscaledCapsuleRadius(); } float UGMS_MoverMovementSystemComponent::GetScaledCapsuleHalfHeight() const { if (UCapsuleComponent* Capsule = Cast(MoverComponent->GetUpdatedComponent())) { return Capsule->GetScaledCapsuleHalfHeight(); } return GetDefault()->GetUnscaledCapsuleHalfHeight(); } float UGMS_MoverMovementSystemComponent::GetMaxAcceleration() const { const UCommonLegacyMovementSettings* CommonLegacySettings = MoverComponent->FindSharedSettings(); return CommonLegacySettings->Acceleration; } float UGMS_MoverMovementSystemComponent::GetMaxBrakingDeceleration() const { const UCommonLegacyMovementSettings* CommonLegacySettings = MoverComponent->FindSharedSettings(); return CommonLegacySettings->Deceleration; } float UGMS_MoverMovementSystemComponent::GetWalkableFloorZ() const { const UCommonLegacyMovementSettings* CommonLegacySettings = MoverComponent->FindSharedSettings(); 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()) { 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()) { 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(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()) { 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()) { 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(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(); FGMS_MoverMovementControlInputs& ControlInputs = InputCmdResult.InputCollection.FindOrAddMutableDataByType(); 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(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(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()) { 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()) { 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()) { 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