第一次提交
This commit is contained in:
@@ -0,0 +1,958 @@
|
||||
// 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);
|
||||
}
|
||||
Reference in New Issue
Block a user