Files
2026-03-03 01:23:02 +08:00

959 lines
32 KiB
C++

// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "Locomotions/GMS_MainAnimInstance.h"
#include "AnimationWarpingLibrary.h"
#include "PoseSearch/PoseSearchTrajectoryPredictor.h"
#include "DrawDebugHelpers.h"
#include "GMS_CharacterMovementSystemComponent.h"
#include "GMS_MovementSystemComponent.h"
#include "KismetAnimationLibrary.h"
#include "Curves/CurveFloat.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Kismet/KismetMathLibrary.h"
#include "Locomotions/GMS_AnimLayer.h"
#include "Locomotions/GMS_AnimLayer_Additive.h"
#include "Locomotions/GMS_AnimLayer_Overlay.h"
#include "Locomotions/GMS_AnimLayer_States.h"
#include "Locomotions/GMS_AnimLayer_View.h"
#include "Locomotions/GMS_AnimLayer_SkeletalControls.h"
#include "PoseSearch/PoseSearchTrajectoryLibrary.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_MainAnimInstance)
UGMS_MainAnimInstance::UGMS_MainAnimInstance()
{
RootMotionMode = ERootMotionMode::RootMotionFromMontagesOnly;
MovementIntent = FVector::ZeroVector;
}
UGMS_MovementSystemComponent* UGMS_MainAnimInstance::GetMovementSystemComponent() const
{
return MovementSystem;
}
void UGMS_MainAnimInstance::RegisterStateNameToTagMapping(UAnimInstance* SourceAnimInstance, TArray<FGMS_AnimStateNameToTag> Mapping)
{
if (SourceAnimInstance && SourceAnimInstance->Blueprint_GetMainAnimInstance() == this && !Mapping.IsEmpty())
{
FGMS_AnimStateNameToTagWrapper Wrapper;
Wrapper.AnimStateNameToTagMapping = Mapping;
RuntimeAnimStateNameToTagMappings.Emplace(SourceAnimInstance, Wrapper);
}
}
void UGMS_MainAnimInstance::UnregisterStateNameToTagMapping(UAnimInstance* SourceAnimInstance)
{
if (SourceAnimInstance && SourceAnimInstance->Blueprint_GetMainAnimInstance() == this && RuntimeAnimStateNameToTagMappings.Contains(SourceAnimInstance))
{
TArray<FGameplayTag> Tags;
for (const FGMS_AnimStateNameToTag& Mapping : RuntimeAnimStateNameToTagMappings[SourceAnimInstance].AnimStateNameToTagMapping)
{
Tags.Add(Mapping.Tag);
}
NodeRelevanceTags.RemoveTags(FGameplayTagContainer::CreateFromArray(Tags));
RuntimeAnimStateNameToTagMappings.Remove(SourceAnimInstance);
}
}
#pragma region Definition
void UGMS_MainAnimInstance::RefreshLayerSettings_Implementation()
{
const FGMS_MovementSetSetting& MSSetting = MovementSystem->GetMovementSetSetting();
const auto& States = MSSetting.bUseInstancedStatesSetting ? MSSetting.AnimLayerSetting_States : MSSetting.DA_AnimLayerSetting_States;
SetAnimLayerBySetting(States, StateLayerInstance);
const auto& Overlay = MSSetting.bUseInstancedOverlaySetting ? MSSetting.AnimLayerSetting_Overlay : MSSetting.DA_AnimLayerSetting_Overlay;
SetAnimLayerBySetting(Overlay, OverlayLayerInstance);
SetAnimLayerBySetting(MSSetting.AnimLayerSetting_View, ViewLayerInstance);
SetAnimLayerBySetting(MSSetting.AnimLayerSetting_Additive, AdditiveLayerInstance);
SetAnimLayerBySetting(MSSetting.AnimLayerSetting_SkeletalControls, SkeletonControlsLayerInstance);
}
void UGMS_MainAnimInstance::SetOffsetRootBoneRotationMode_Implementation(EOffsetRootBoneMode NewRotationMode)
{
if (GeneralSetting.bEnableOffsetRootBoneRotation && RootState.RotationMode != NewRotationMode)
{
RootState.RotationMode = NewRotationMode;
}
}
EOffsetRootBoneMode UGMS_MainAnimInstance::GetOffsetRootBoneRotationMode_Implementation() const
{
if (bAnyMontagePlaying)
{
return EOffsetRootBoneMode::Release;
}
// Temporal solution:prevent root rotation offset when standing at moving platform.
// if (GetMovementSystemComponent() && GetMovementSystemComponent()->GetMovementBase().bHasRelativeRotation)
// {
// return EOffsetRootBoneMode::Release;
// }
return GeneralSetting.bEnableOffsetRootBoneRotation ? RootState.RotationMode : EOffsetRootBoneMode::Release;
}
void UGMS_MainAnimInstance::SetOffsetRootBoneTranslationMode_Implementation(EOffsetRootBoneMode NewTranslationMode)
{
if (GeneralSetting.bEnableOffsetRootBoneTranslation && RootState.TranslationMode != NewTranslationMode)
{
RootState.TranslationMode = NewTranslationMode;
}
}
EOffsetRootBoneMode UGMS_MainAnimInstance::GetOffsetRootBoneTranslationMode_Implementation() const
{
if (bAnyMontagePlaying)
{
return EOffsetRootBoneMode::Release;
}
return GeneralSetting.bEnableOffsetRootBoneTranslation ? RootState.TranslationMode : EOffsetRootBoneMode::Release;
}
void UGMS_MainAnimInstance::OnLocomotionModeChanged_Implementation(const FGameplayTag& Prev)
{
check(IsInGameThread())
check(IsValid(MovementSystem))
// GMS_ANIMATION_CLOG(Verbose, "Refresh layer settings due to LocomotionMode Changed.")
LocomotionMode = MovementSystem->GetLocomotionMode();
LocomotionModeContainer = LocomotionMode.GetSingleTagContainer();
if (Prev == GMS_MovementModeTags::InAir)
{
InAirState.bJumping = false;
InAirState.bFalling = false;
}
bLocomotionModeChanged = true;
GetWorld()->GetTimerManager().SetTimerForNextTick([this]()
{
bLocomotionModeChanged = false;
});
}
void UGMS_MainAnimInstance::OnRotationModeChanged_Implementation(const FGameplayTag& Prev)
{
check(IsInGameThread())
check(IsValid(MovementSystem))
check(IsValid(MovementSystem->GetControlSetting()))
// GMS_ANIMATION_CLOG(Verbose, "Refresh layer settings due to RotationMode Changed.")
RotationMode = MovementSystem->GetRotationMode();
RotationModeContainer = MovementSystem->GetRotationMode().GetSingleTagContainer();
RefreshLayerSettings();
bRotationModeChanged = true;
GetWorld()->GetTimerManager().SetTimerForNextTick([this]()
{
bRotationModeChanged = false;
});
}
void UGMS_MainAnimInstance::OnMovementSetChanged_Implementation(const FGameplayTag& Prev)
{
check(IsInGameThread())
check(IsValid(MovementSystem))
// GMS_ANIMATION_CLOG(Verbose, "Refresh layer settings due to MovementSet Changed.")
MovementSet = MovementSystem->GetMovementSet();
MovementSetContainer = MovementSet.GetSingleTagContainer();
RefreshLayerSettings();
bMovementSetChanged = true;
GetWorld()->GetTimerManager().SetTimerForNextTick([this]()
{
bMovementSetChanged = false;
});
}
void UGMS_MainAnimInstance::OnMovementStateChanged_Implementation(const FGameplayTag& Prev)
{
check(IsInGameThread())
check(IsValid(MovementSystem))
// GMS_ANIMATION_CLOG(Verbose, "Refresh layer settings due to MovementState Changed.")
MovementState = MovementSystem->GetMovementState();
MovementStateContainer = MovementState.GetSingleTagContainer();
RefreshLayerSettings();
bMovementStateChanged = true;
GetWorld()->GetTimerManager().SetTimerForNextTick([this]()
{
bMovementStateChanged = false;
});
}
void UGMS_MainAnimInstance::OnOverlayModeChanged_Implementation(const FGameplayTag& Prev)
{
check(IsInGameThread())
check(IsValid(MovementSystem))
// GMS_ANIMATION_CLOG(Verbose, "Refresh layer settings due to OverlayMode Changed.")
OverlayMode = MovementSystem->GetOverlayMode();
OverlayModeContainer = MovementSystem->GetOverlayMode().GetSingleTagContainer();
RefreshLayerSettings();
bOverlayModeChanged = true;
GetWorld()->GetTimerManager().SetTimerForNextTick([this]()
{
bOverlayModeChanged = false;
});
}
#pragma endregion Definition
void UGMS_MainAnimInstance::NativeInitializeAnimation()
{
Super::NativeInitializeAnimation();
PawnOwner = Cast<APawn>(GetOwningActor());
#if WITH_EDITOR
if (GetWorld() && !GetWorld()->IsGameWorld() && !IsValid(PawnOwner))
{
// Use default objects for editor preview.
PawnOwner = GetMutableDefault<APawn>();
MovementSystem = PawnOwner->FindComponentByClass<UGMS_MovementSystemComponent>();
}
#endif
}
void UGMS_MainAnimInstance::NativeUninitializeAnimation()
{
if (IsValid(MovementSystem))
{
MovementSystem->OnLocomotionModeChangedEvent.RemoveDynamic(this, &ThisClass::OnLocomotionModeChanged);
MovementSystem->OnRotationModeChangedEvent.RemoveDynamic(this, &ThisClass::OnRotationModeChanged);
MovementSystem->OnMovementSetChangedEvent.RemoveDynamic(this, &ThisClass::OnMovementSetChanged);
MovementSystem->OnMovementStateChangedEvent.RemoveDynamic(this, &ThisClass::OnMovementStateChanged);
MovementSystem->OnOverlayModeChangedEvent.RemoveDynamic(this, &ThisClass::OnOverlayModeChanged);
}
if (InitialTimerHandle.IsValid())
{
GetWorld()->GetTimerManager().ClearTimer(InitialTimerHandle);
}
Super::NativeUninitializeAnimation();
}
void UGMS_MainAnimInstance::NativeBeginPlay()
{
Super::NativeBeginPlay();
ensure(PawnOwner);
MovementSystem = PawnOwner->FindComponentByClass<UGMS_MovementSystemComponent>();
ensure(MovementSystem);
if (IsValid(MovementSystem))
{
// TrajectoryPredictor = MovementSystem->GetTrajectoryPredictor();
MovementSystem->MainAnimInstance = this;
MovementSystem->OnLocomotionModeChangedEvent.AddDynamic(this, &ThisClass::OnLocomotionModeChanged);
MovementSystem->OnRotationModeChangedEvent.AddDynamic(this, &ThisClass::OnRotationModeChanged);
MovementSystem->OnMovementSetChangedEvent.AddDynamic(this, &ThisClass::OnMovementSetChanged);
MovementSystem->OnMovementStateChangedEvent.AddDynamic(this, &ThisClass::OnMovementStateChanged);
MovementSystem->OnOverlayModeChangedEvent.AddDynamic(this, &ThisClass::OnOverlayModeChanged);
//Grab latest info and intialize.
FTimerDelegate Delegate = FTimerDelegate::CreateLambda([this]()
{
InitialTimerHandle.Invalidate();
const FGMS_MovementSetSetting& MSSetting = MovementSystem->GetMovementSetSetting();
LocomotionMode = MovementSystem->GetLocomotionMode();
LocomotionModeContainer = LocomotionMode.GetSingleTagContainer();
MovementSet = MovementSystem->GetMovementSet();
MovementSetContainer = MovementSet.GetSingleTagContainer();
MovementState = MovementSystem->GetMovementState();
MovementStateContainer = MovementState.GetSingleTagContainer();
RotationMode = MovementSystem->GetRotationMode();
RotationModeContainer = MovementSystem->GetRotationMode().GetSingleTagContainer();
OverlayMode = MovementSystem->GetOverlayMode();
OverlayModeContainer = MovementSystem->GetOverlayMode().GetSingleTagContainer();
RefreshLayerSettings();
});
GetWorld()->GetTimerManager().SetTimer(InitialTimerHandle, Delegate, 0.2f, false);
}
else
{
GMS_ANIMATION_CLOG(Error, "Missing Movement system component, This anim instance(%s) will not work properly!", *GetClass()->GetName())
}
}
void UGMS_MainAnimInstance::NativeUpdateAnimation(const float DeltaTime)
{
DECLARE_SCOPE_CYCLE_COUNTER(TEXT("UGMS_MainAnimInstance::NativeUpdateAnimation"), STAT_GMS_MainAnimInstance_NativeUpdateAnimation, STATGROUP_GMS)
TRACE_CPUPROFILER_EVENT_SCOPE_STR(__FUNCTION__)
Super::NativeUpdateAnimation(DeltaTime);
if (!IsValid(PawnOwner) || !IsValid(MovementSystem))
{
return;
}
if (UGMS_CharacterMovementSystemComponent* CharacterMovementSystemComponent = Cast<UGMS_CharacterMovementSystemComponent>(MovementSystem))
{
if (!IsValid(CharacterMovementSystemComponent->GetCharacterMovement()))
{
return;
}
}
RefreshStateOnGameThread();
RefreshRelevanceOnGameThread();
bAnyMontagePlaying = IsAnyMontagePlaying();
}
void UGMS_MainAnimInstance::NativeThreadSafeUpdateAnimation(const float DeltaTime)
{
DECLARE_SCOPE_CYCLE_COUNTER(TEXT("UGMS_MainAnimInstance::NativeThreadSafeUpdateAnimation"), STAT_GMS_MainAnimInstance_NativeThreadSafeUpdateAnimation, STATGROUP_GMS)
TRACE_CPUPROFILER_EVENT_SCOPE_STR(__FUNCTION__)
Super::NativeThreadSafeUpdateAnimation(DeltaTime);
if (!IsValid(PawnOwner) || !IsValid(MovementSystem))
{
return;
}
RefreshTrajectoryState(DeltaTime);
RefreshLocomotion(DeltaTime);
RefreshGrounded();
RefreshInAir();
RefreshView(DeltaTime);
}
void UGMS_MainAnimInstance::SetAnimLayerBySetting(const UGMS_AnimLayerSetting* LayerSetting, TObjectPtr<UGMS_AnimLayer>& LayerInstance)
{
check(IsInGameThread() && IsValid(MovementSystem) && IsValid(MovementSystem->AnimGraphSetting))
//invalid setting
if (!IsValid(LayerSetting))
{
if (IsValid(LayerInstance))
{
UnlinkAnimClassLayers(LayerInstance->GetClass());
LayerInstance->OnUnlinked();
LayerInstance = nullptr;
}
return;
}
TSubclassOf<UGMS_AnimLayer> LayerClass = nullptr;
if (!LayerSetting->GetOverrideAnimLayerClass(LayerClass))
{
bool bValidMapping = MovementSystem->AnimGraphSetting->AnimLayerSettingToInstanceMapping.Contains(LayerSetting->GetClass()) && MovementSystem->AnimGraphSetting->
AnimLayerSettingToInstanceMapping[LayerSetting->
GetClass()] != nullptr;
if (!bValidMapping)
{
GMS_ANIMATION_CLOG(Error, "Can't find exising anim instance mapping for anim layer setting(%s) or mapped a invalid anim instance. Please check anim graph setting:%s",
*LayerSetting->GetClass()->GetName(), *MovementSystem->AnimGraphSetting->GetName())
if (IsValid(LayerInstance))
{
UnlinkAnimClassLayers(LayerInstance->GetClass());
LayerInstance->OnUnlinked();
LayerInstance = nullptr;
}
return;
}
LayerClass = MovementSystem->AnimGraphSetting->AnimLayerSettingToInstanceMapping[LayerSetting->GetClass()];
}
if (IsValid(LayerInstance) && LayerClass != LayerInstance->GetClass())
{
UnlinkAnimClassLayers(LayerInstance->GetClass());
LayerInstance->OnUnlinked();
LayerInstance = nullptr;
}
if (!IsValid(LayerInstance))
{
LinkAnimClassLayers(LayerClass);
LayerInstance = Cast<UGMS_AnimLayer>(GetLinkedAnimLayerInstanceByClass(LayerClass));
if (LayerInstance)
{
LayerInstance->OnLinked();
}
else
{
GMS_ANIMATION_CLOG(Error, "Failed to link anim layer by class(%s), It will happen if this class doesn't implement any anim layer interface required on main anim instance. ",
*LayerClass->GetName());
}
}
if (LayerInstance)
{
LayerInstance->ApplySetting(LayerSetting);
}
}
void UGMS_MainAnimInstance::RefreshTrajectoryState(float DeltaTime)
{
// if (TScriptInterface<IPoseSearchTrajectoryPredictorInterface> Predictor = MovementSystem->GetTrajectoryPredictor())
// {
// UPoseSearchTrajectoryLibrary::PoseSearchGenerateTransformTrajectoryWithPredictor(Predictor, GetDeltaSeconds(), TrajectoryState.Trajectory, TrajectoryState.DesiredControllerYaw,
// TrajectoryState.Trajectory, -1.0f, 30, 0.1, 15);
//
// UPoseSearchTrajectoryLibrary::GetTransformTrajectoryVelocity(TrajectoryState.Trajectory, -0.3f, -0.4f, TrajectoryState.PastVelocity, false);
// UPoseSearchTrajectoryLibrary::GetTransformTrajectoryVelocity(TrajectoryState.Trajectory, 0.0f, 0.2f, TrajectoryState.CurrentVelocity, false);
// UPoseSearchTrajectoryLibrary::GetTransformTrajectoryVelocity(TrajectoryState.Trajectory, 0.4f, 0.5f, TrajectoryState.FutureVelocity, false);
// }
}
void UGMS_MainAnimInstance::RefreshView(const float DeltaTime)
{
// ViewState.YawAngle = FRotator3f::NormalizeAxis(UE_REAL_TO_FLOAT(ViewState.Rotation.Yaw - LocomotionState.Rotation.Yaw - RootState.YawOffset));
ViewState.YawAngle = FRotator3f::NormalizeAxis(UE_REAL_TO_FLOAT(ViewState.Rotation.Yaw - LocomotionState.Rotation.Yaw));
ViewState.PitchAngle = FRotator3f::NormalizeAxis(UE_REAL_TO_FLOAT(ViewState.Rotation.Pitch - LocomotionState.Rotation.Pitch));
ViewState.PitchAmount = 0.5f - ViewState.PitchAngle / 180.0f;
}
void UGMS_MainAnimInstance::RefreshLocomotion(const float DeltaTime)
{
const auto& ActorTransform = GetOwningActor()->GetActorTransform();
const auto ActorDeltaTime{GetDeltaSeconds() * GetOwningActor()->CustomTimeDilation};
const auto bCanCalculateRateOfChange{ActorDeltaTime > UE_SMALL_NUMBER};
// update location data
LocomotionState.PreviousDisplacement = (GetOwningActor()->GetActorLocation() - LocomotionState.Location).Size2D();
LocomotionState.Location = ActorTransform.GetLocation();
auto PreviousYawAngle{LocomotionState.Rotation.Yaw};
if (MovementBase.bHasRelativeRotation)
{
// Offset the angle to keep it relative to the movement base.
PreviousYawAngle = FMath::UnwindDegrees(UE_REAL_TO_FLOAT(PreviousYawAngle + MovementBase.DeltaRotation.Yaw));
}
// update rotation data
LocomotionState.Rotation = ActorTransform.Rotator();
LocomotionState.RotationQuaternion = ActorTransform.GetRotation();
FVector PreviousVelocity{
MovementBase.bHasRelativeRotation
? MovementBase.DeltaRotation.RotateVector(LocomotionState.Velocity)
: LocomotionState.Velocity
};
if (bFirstUpdate)
{
LocomotionState.PreviousDisplacement = 0.0f;
LocomotionState.DisplacementSpeed = 0.0f;
PreviousVelocity = FVector::ZeroVector;
}
// update velocity data
LocomotionState.bHasInput = GameLocomotionState.bHasInput;
LocomotionState.Speed = GameLocomotionState.Speed;
LocomotionState.DisplacementSpeed = GameLocomotionState.Speed;
LocomotionState.Velocity = GameLocomotionState.Velocity;
LocomotionState.VelocityAcceleration = bCanCalculateRateOfChange ? (LocomotionState.Velocity - PreviousVelocity) / DeltaTime : FVector::ZeroVector;
bool bWasMovingLastUpdate = !LocomotionState.LocalVelocity2D.IsZero();
LocomotionState.LocalVelocity2D = LocomotionState.RotationQuaternion.UnrotateVector({LocomotionState.Velocity.X, LocomotionState.Velocity.Y, 0.0f});
LocomotionState.bHasVelocity = !FMath::IsNearlyZero(LocomotionState.LocalVelocity2D.SizeSquared2D());
LocomotionState.LocalVelocityYawAngle = UKismetAnimationLibrary::CalculateDirection(LocomotionState.Velocity.GetSafeNormal2D(), LocomotionState.Rotation);
LocomotionState.LocalVelocityYawAngleWithOffset = LocomotionState.LocalVelocityYawAngle - RootState.YawOffset;
//take root yaw offset in account. 考虑到Offset的方向
LocomotionState.LocalVelocityDirection = SelectCardinalDirectionFromAngle(LocomotionState.LocalVelocityYawAngleWithOffset, 10, LocomotionState.LocalVelocityDirection,
bWasMovingLastUpdate);
LocomotionState.LocalVelocityDirectionNoOffset = SelectCardinalDirectionFromAngle(LocomotionState.LocalVelocityYawAngle, 10, LocomotionState.LocalVelocityDirectionNoOffset,
bWasMovingLastUpdate);
LocomotionState.LocalVelocityOctagonalDirection = SelectOctagonalDirectionFromAngle(LocomotionState.LocalVelocityYawAngleWithOffset, 10, LocomotionState.LocalVelocityOctagonalDirection,
bWasMovingLastUpdate);
LocomotionState.LocalAcceleration2D = UKismetMathLibrary::LessLess_VectorRotator({MovementIntent.X, MovementIntent.Y, 0.0f}, LocomotionState.Rotation);
LocomotionState.bMoving = GameLocomotionState.bMoving;
LocomotionState.YawVelocity = bCanCalculateRateOfChange
? FMath::UnwindDegrees(UE_REAL_TO_FLOAT(
LocomotionState.Rotation.Yaw - PreviousYawAngle)) / ActorDeltaTime
: 0.0f;
bFirstUpdate = false;
}
void UGMS_MainAnimInstance::RefreshBlock()
{
bBlocked = UKismetMathLibrary::VSizeXY(MovementIntent) > 0.1 && LocomotionState.Speed < 200.0f &&
UKismetMathLibrary::InRange_FloatFloat(FVector::DotProduct(MovementIntent.GetSafeNormal(0.0001), LocomotionState.Velocity.GetSafeNormal(0.0001)), -0.6f, 0.6, true, true);
}
void UGMS_MainAnimInstance::RefreshStateOnGameThread()
{
LocomotionMode = MovementSystem->GetLocomotionMode();
LocomotionModeContainer = LocomotionMode.GetSingleTagContainer();
MovementSet = MovementSystem->GetMovementSet();
MovementSetContainer = MovementSet.GetSingleTagContainer();
MovementState = MovementSystem->GetMovementState();
MovementStateContainer = MovementState.GetSingleTagContainer();
RotationMode = MovementSystem->GetRotationMode();
RotationModeContainer = MovementSystem->GetRotationMode().GetSingleTagContainer();
OverlayMode = MovementSystem->GetOverlayMode();
OverlayModeContainer = MovementSystem->GetOverlayMode().GetSingleTagContainer();
OwnedTags = MovementSystem->GetGameplayTags();
ControlSetting = MovementSystem->GetControlSetting();
GeneralSetting = MovementSystem->GetMovementSetSetting().AnimDataSetting_General;
// Apply latest root setting if diff.
if (!GeneralSetting.bEnableOffsetRootBoneRotation && RootState.RotationMode != EOffsetRootBoneMode::Release)
{
RootState.RotationMode = EOffsetRootBoneMode::Release;
}
if (!GeneralSetting.bEnableOffsetRootBoneTranslation && RootState.TranslationMode != EOffsetRootBoneMode::Release)
{
RootState.TranslationMode = EOffsetRootBoneMode::Release;
}
const auto& View{MovementSystem->GetViewState()};
ViewState.Rotation = View.Rotation;
ViewState.YawSpeed = View.YawSpeed;
MovementBase = MovementSystem->GetMovementBase();
GameLocomotionState = MovementSystem->GetLocomotionState();
MovementIntent = MovementSystem->GetMovementIntent();
LocomotionState.MaxAcceleration = MovementSystem->GetMaxAcceleration();
LocomotionState.MaxBrakingDeceleration = MovementSystem->GetMaxBrakingDeceleration();
LocomotionState.WalkableFloorZ = MovementSystem->GetWalkableFloorZ();
LocomotionState.Scale = UE_REAL_TO_FLOAT(GetSkelMeshComponent()->GetComponentScale().Z);
LocomotionState.CapsuleRadius = MovementSystem->GetScaledCapsuleRadius();
LocomotionState.CapsuleHalfHeight = MovementSystem->GetScaledCapsuleHalfHeight();
}
void UGMS_MainAnimInstance::RefreshRelevanceOnGameThread()
{
if (!IsValid(this))
{
return;
}
FGameplayTagContainer TagsToAdd;
for (int i = 0; i < AnimStateNameToTagMapping.Num(); ++i)
{
if (AnimStateNameToTagMapping[i].State.IsRelevant(*this))
{
TagsToAdd.AddTagFast(AnimStateNameToTagMapping[i].Tag);
}
}
for (const TTuple<TObjectPtr<UAnimInstance>, FGMS_AnimStateNameToTagWrapper>& Pair : RuntimeAnimStateNameToTagMappings)
{
if (IsValid(Pair.Key))
{
for (int i = 0; i < Pair.Value.AnimStateNameToTagMapping.Num(); ++i)
{
if (Pair.Value.AnimStateNameToTagMapping[i].State.IsRelevant(*Pair.Key))
{
TagsToAdd.AddTagFast(Pair.Value.AnimStateNameToTagMapping[i].Tag);
}
}
}
}
NodeRelevanceTags = TagsToAdd;
}
void UGMS_MainAnimInstance::RefreshGrounded()
{
#if WITH_EDITOR
if (!IsValid(GetWorld()) || !GetWorld()->IsGameWorld())
{
return;
}
#endif
DECLARE_SCOPE_CYCLE_COUNTER(TEXT("UGMS_MainAnimInstance::RefreshGrounded"), STAT_GMS_MainAnimInstance_RefreshGrounded, STATGROUP_GMS)
TRACE_CPUPROFILER_EVENT_SCOPE_STR(__FUNCTION__)
if (LocomotionMode != GMS_MovementModeTags::Grounded)
{
return;
}
RefreshBlock();
RefreshGroundedLean();
}
void UGMS_MainAnimInstance::RefreshGroundedLean()
{
const auto TargetLeanAmount{GetRelativeAccelerationAmount()};
const auto DeltaTime{GetDeltaSeconds()};
LeanState.RightAmount = FMath::FInterpTo(LeanState.RightAmount, TargetLeanAmount.Y,
DeltaTime, GeneralSetting.LeanInterpolationSpeed);
LeanState.ForwardAmount = FMath::FInterpTo(LeanState.ForwardAmount, TargetLeanAmount.X,
DeltaTime, GeneralSetting.LeanInterpolationSpeed);
}
FVector2f UGMS_MainAnimInstance::GetRelativeAccelerationAmount() const
{
// This value represents the current amount of acceleration / deceleration relative to the
// character rotation. It is normalized to a range of -1 to 1 so that -1 equals the max
// braking deceleration and 1 equals the max acceleration of the character movement component.
const auto MaxAcceleration{
(MovementIntent | LocomotionState.Velocity) >= 0.0f
? LocomotionState.MaxAcceleration
: LocomotionState.MaxBrakingDeceleration
};
// relative to root bone transform.
const FVector3f RelativeAcceleration{
RootState.RootTransform.GetRotation().UnrotateVector(LocomotionState.VelocityAcceleration)
};
return FVector2f{UGMS_Vector::ClampMagnitude01(RelativeAcceleration / MaxAcceleration)};
}
void UGMS_MainAnimInstance::RefreshInAir()
{
#if WITH_EDITOR
if (!IsValid(GetWorld()) || !GetWorld()->IsGameWorld())
{
return;
}
#endif
DECLARE_SCOPE_CYCLE_COUNTER(TEXT("UGMS_MainAnimInstance::RefreshInAir"), STAT_GMS_MainAnimInstance_RefreshInAir, STATGROUP_GMS)
TRACE_CPUPROFILER_EVENT_SCOPE_STR(__FUNCTION__)
// InAirState.bJumping = false;
// InAirState.bFalling = false;
if (LocomotionMode != GMS_MovementModeTags::InAir)
{
// InAirState.VerticalSpeed = 0.0f; Land calculation need it.
return;
}
// A separate variable for vertical speed is used to determine at what speed the character landed on the ground.
if (LocomotionState.Velocity.Z > 0)
{
InAirState.bJumping = true;
InAirState.TimeToJumpApex = (0 - LocomotionState.Velocity.Z) / MovementSystem->GetGravityZ();
InAirState.FallingTime = 0;
}
else
{
InAirState.bFalling = true;
InAirState.TimeToJumpApex = 0;
InAirState.FallingTime += GetDeltaSeconds();
}
InAirState.VerticalSpeed = UE_REAL_TO_FLOAT(LocomotionState.Velocity.Z);
RefreshGroundPrediction();
RefreshInAirLean();
}
void UGMS_MainAnimInstance::RefreshGroundPrediction()
{
if (!bEnableGroundPrediction)
{
return;
}
static constexpr auto VerticalVelocityThreshold{-200.0f};
if (InAirState.VerticalSpeed > VerticalVelocityThreshold)
{
InAirState.bValidGround = false;
InAirState.GroundDistance = -1.0f;
return;
}
const auto SweepStartLocation{LocomotionState.Location};
static constexpr auto MinVerticalVelocity{-4000.0f};
static constexpr auto MaxVerticalVelocity{-200.0f};
auto VelocityDirection{LocomotionState.Velocity};
VelocityDirection.Z = FMath::Clamp(VelocityDirection.Z, MinVerticalVelocity, MaxVerticalVelocity);
VelocityDirection.Normalize();
static constexpr auto MinSweepDistance{150.0f};
static constexpr auto MaxSweepDistance{2000.0f};
const auto SweepVector{
VelocityDirection * FMath::GetMappedRangeValueClamped(FVector2f{MaxVerticalVelocity, MinVerticalVelocity},
{MinSweepDistance, MaxSweepDistance},
InAirState.VerticalSpeed) * LocomotionState.Scale
};
FHitResult Hit;
GetWorld()->SweepSingleByChannel(Hit, SweepStartLocation, SweepStartLocation + SweepVector,
FQuat::Identity, GeneralSetting.GroundPredictionSweepChannel,
FCollisionShape::MakeCapsule(LocomotionState.CapsuleRadius, LocomotionState.CapsuleHalfHeight),
{__FUNCTION__, false, PawnOwner}, GeneralSetting.GroundPredictionSweepResponses);
const auto bGroundValid{Hit.IsValidBlockingHit() && Hit.ImpactNormal.Z >= LocomotionState.WalkableFloorZ};
InAirState.bValidGround = bGroundValid;
InAirState.GroundDistance = Hit.Distance;
}
void UGMS_MainAnimInstance::RefreshInAirLean()
{
if (GeneralSetting.InAirLeanAmountCurve == nullptr)
{
return;
}
// Use the relative velocity direction and amount to determine how much the character should lean
// while in air. The lean amount curve gets the vertical velocity and is used as a multiplier to
// smoothly reverse the leaning direction when transitioning from moving upwards to moving downwards.
static constexpr auto ReferenceSpeed{350.0f};
const auto RelativeVelocity{
FVector3f{LocomotionState.RotationQuaternion.UnrotateVector(LocomotionState.Velocity)} /
ReferenceSpeed * GeneralSetting.InAirLeanAmountCurve->GetFloatValue(InAirState.VerticalSpeed)
};
const auto DeltaTime{GetDeltaSeconds()};
LeanState.RightAmount = FMath::FInterpTo(LeanState.RightAmount, RelativeVelocity.Y,
DeltaTime, GeneralSetting.LeanInterpolationSpeed);
LeanState.ForwardAmount = FMath::FInterpTo(LeanState.ForwardAmount, RelativeVelocity.X,
DeltaTime, GeneralSetting.LeanInterpolationSpeed);
}
void UGMS_MainAnimInstance::RefreshOffsetRootBone_Implementation(FAnimUpdateContext& Context, FAnimNodeReference& Node)
{
if (GetMovementSystemComponent() && GetMovementSystemComponent()->GetControlSetting())
{
// 获取 OffsetRootBone 节点的根骨骼变换(世界空间)
auto RootBoneTransform = UAnimationWarpingLibrary::GetOffsetRootTransform(Node);
// 始终将 RootState.RootTransform 设置为世界空间变换(应用 +90 度 Yaw 调整)
FRotator RootBoneRotation = FRotator(RootBoneTransform.Rotator().Pitch, RootBoneTransform.Rotator().Yaw + 90.0f, RootBoneTransform.Rotator().Roll);
RootState.RootTransform = FTransform(RootBoneRotation, RootBoneTransform.GetTranslation(), RootBoneTransform.GetScale3D());
// 直接使用世界空间旋转计算 YawOffset
RootState.YawOffset = UKismetMathLibrary::NormalizeAxis(RootBoneRotation.Yaw - LocomotionState.Rotation.Yaw);
if (RotationMode == GMS_RotationModeTags::ViewDirection)
{
if (const FGMS_ViewDirectionSetting_Aiming* Setting = GetMovementSystemComponent()->GetControlSetting()->ViewDirectionSetting.GetPtr<FGMS_ViewDirectionSetting_Aiming>())
{
if (FMath::Abs(RootState.YawOffset) <= Setting->MinAimingYawAngleLimit + UE_KINDA_SMALL_NUMBER)
{
RootState.MaxRotationError = Setting->MinAimingYawAngleLimit;
return;
}
}
}
// no limit.
RootState.MaxRotationError = -1.0f;
}
}
float UGMS_MainAnimInstance::GetCurveValueClamped01(const FName& CurveName) const
{
return UGMS_Math::Clamp01(GetCurveValue(CurveName));
}
UBlendProfile* UGMS_MainAnimInstance::GetNamedBlendProfile(const FName& BlendProfileName) const
{
if (CurrentSkeleton)
{
return CurrentSkeleton->GetBlendProfile(BlendProfileName);
}
return nullptr;
}
FGameplayTagContainer UGMS_MainAnimInstance::GetAggregatedTags() const
{
FGameplayTagContainer Result = OwnedTags;
Result.AppendTags(NodeRelevanceTags);
return Result;
}
float UGMS_MainAnimInstance::GetAOYawValue() const
{
if (RootState.RotationMode == EOffsetRootBoneMode::Release)
{
return ViewState.YawAngle;
}
return -RootState.YawOffset;
}
EGMS_MovementDirection UGMS_MainAnimInstance::SelectCardinalDirectionFromAngle(float Angle, float DeadZone, EGMS_MovementDirection CurrentDirection, bool bUseCurrentDirection) const
{
const float AbsAngle = FMath::Abs(Angle);
float FwdDeadZone = DeadZone;
float BwdDeadZone = DeadZone;
if (bUseCurrentDirection)
{
if (CurrentDirection == EGMS_MovementDirection::Forward)
{
FwdDeadZone *= 2;
}
if (CurrentDirection == EGMS_MovementDirection::Backward)
{
BwdDeadZone *= 2;
}
}
if (AbsAngle <= 45 + FwdDeadZone)
{
return EGMS_MovementDirection::Forward;
}
if (AbsAngle >= 135 - BwdDeadZone)
{
return EGMS_MovementDirection::Backward;
}
if (Angle > 0)
{
return EGMS_MovementDirection::Right;
}
return EGMS_MovementDirection::Left;
}
EGMS_MovementDirection_8Way UGMS_MainAnimInstance::SelectOctagonalDirectionFromAngle(float Angle, float DeadZone, EGMS_MovementDirection_8Way CurrentDirection,
bool bUseCurrentDirection) const
{
const float AbsAngle = FMath::Abs(Angle);
float FwdDeadZone = DeadZone;
float BwdDeadZone = DeadZone;
if (bUseCurrentDirection)
{
if (CurrentDirection == EGMS_MovementDirection_8Way::Forward)
{
FwdDeadZone *= 2;
}
if (CurrentDirection == EGMS_MovementDirection_8Way::Backward)
{
BwdDeadZone *= 2;
}
}
if (AbsAngle <= 22.5f + FwdDeadZone)
{
return EGMS_MovementDirection_8Way::Forward;
}
if (AbsAngle >= 157.5f - BwdDeadZone)
{
return EGMS_MovementDirection_8Way::Backward;
}
if (Angle >= 22.5f && Angle < 67.5f)
{
return EGMS_MovementDirection_8Way::ForwardRight;
}
if (Angle >= 67.5f && Angle < 112.5f)
{
return EGMS_MovementDirection_8Way::Right;
}
if (Angle >= 112.5f && Angle < 157.5f)
{
return EGMS_MovementDirection_8Way::BackwardRight;
}
if (Angle >= -157.5f && Angle < -112.5f)
{
return EGMS_MovementDirection_8Way::BackwardLeft;
}
if (Angle >= -112.5f && Angle < -67.5f)
{
return EGMS_MovementDirection_8Way::Left;
}
return EGMS_MovementDirection_8Way::ForwardLeft;
}
EGMS_MovementDirection UGMS_MainAnimInstance::GetOppositeCardinalDirection(EGMS_MovementDirection CurrentDirection) const
{
switch (CurrentDirection)
{
case EGMS_MovementDirection::Forward:
return EGMS_MovementDirection::Backward;
case EGMS_MovementDirection::Backward:
return EGMS_MovementDirection::Forward;
case EGMS_MovementDirection::Left:
return EGMS_MovementDirection::Right;
case EGMS_MovementDirection::Right:
return EGMS_MovementDirection::Left;
default:
return CurrentDirection;
}
}
bool UGMS_MainAnimInstance::HasCoreStateChanges() const
{
return bMovementSetChanged || bMovementStateChanged || bLocomotionModeChanged || bOverlayModeChanged || bRotationModeChanged;
}
bool UGMS_MainAnimInstance::CheckCoreStateChanges(bool bCheckLocomotionMode, bool bCheckMovementSet, bool bCheckRotationMode, bool bCheckMovementState, bool bCheckOverlayMode) const
{
return (bCheckLocomotionMode && bLocomotionModeChanged) ||
(bCheckMovementSet && bMovementSetChanged) ||
(bCheckRotationMode && bRotationModeChanged) ||
(bCheckMovementState && bMovementStateChanged) ||
(bCheckOverlayMode && bOverlayModeChanged);
}