第一次提交

This commit is contained in:
不明不惑
2026-03-03 01:23:02 +08:00
commit 3e434877e8
1053 changed files with 102411 additions and 0 deletions

View File

@@ -0,0 +1,128 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "Locomotions/GMS_AnimLayer.h"
#include "GameFramework/Pawn.h"
#include "GMS_MovementSystemComponent.h"
#include "Locomotions/GMS_MainAnimInstance.h"
#include "Misc/DataValidation.h"
#include "Settings/GMS_SettingObjectLibrary.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_AnimLayer)
bool UGMS_AnimLayerSetting::GetOverrideAnimLayerClass_Implementation(TSubclassOf<UGMS_AnimLayer>& OutLayerClass) const
{
return false;
}
bool UGMS_AnimLayerSetting::K2_IsDataValid_Implementation(FText& ErrorText) const
{
return true;
}
#if WITH_EDITOR
EDataValidationResult UGMS_AnimLayerSetting::IsDataValid(class FDataValidationContext& Context) const
{
FText ErrorText;
if (!IsTemplate() && !K2_IsDataValid(ErrorText))
{
Context.AddError(ErrorText);
return EDataValidationResult::Invalid;
}
return Super::IsDataValid(Context);
}
#endif
void UGMS_AnimLayer::OnLinked_Implementation()
{
// make sure to get reference to parent when linked.
if (!Parent.IsValid())
{
Parent = Cast<UGMS_MainAnimInstance>(GetSkelMeshComponent()->GetAnimInstance());
checkf(Parent!=nullptr, TEXT("Parent is not GMS_MainAnimInstance!"));
}
if (!PawnOwner)
{
PawnOwner = Cast<APawn>(GetOwningActor());
checkf(PawnOwner!=nullptr, TEXT("PawnOwner is not valid!"));
}
if (!MovementSystem)
{
MovementSystem = PawnOwner->FindComponentByClass<UGMS_MovementSystemComponent>();
checkf(MovementSystem!=nullptr, TEXT("Movement Sysytem Component is not valid!"));
}
if (!AnimStateNameToTagMapping.IsEmpty())
{
Parent->RegisterStateNameToTagMapping(this, AnimStateNameToTagMapping);
}
}
void UGMS_AnimLayer::OnUnlinked_Implementation()
{
if (Parent.IsValid())
{
if (!AnimStateNameToTagMapping.IsEmpty())
{
Parent->UnregisterStateNameToTagMapping(this);
}
Parent = nullptr;
}
}
UGMS_AnimLayer::UGMS_AnimLayer()
{
bUseMainInstanceMontageEvaluationData = true;
}
UGMS_MainAnimInstance* UGMS_AnimLayer::GetParent() const
{
return Parent.Get();
}
void UGMS_AnimLayer::ApplySetting_Implementation(const UGMS_AnimLayerSetting* Setting)
{
}
void UGMS_AnimLayer::ResetSetting_Implementation()
{
}
void UGMS_AnimLayer::NativeInitializeAnimation()
{
Super::NativeInitializeAnimation();
Parent = Cast<UGMS_MainAnimInstance>(GetSkelMeshComponent()->GetAnimInstance());
PawnOwner = Cast<APawn>(GetOwningActor());
#if WITH_EDITOR
if (!GetWorld()->IsGameWorld())
{
// Use default objects for editor preview.
if (!Parent.IsValid())
{
Parent = GetMutableDefault<UGMS_MainAnimInstance>();
}
if (!IsValid(PawnOwner))
{
PawnOwner = GetMutableDefault<APawn>();
}
}
#endif
}
void UGMS_AnimLayer::NativeBeginPlay()
{
Super::NativeBeginPlay();
ensure(PawnOwner);
MovementSystem = PawnOwner->FindComponentByClass<UGMS_MovementSystemComponent>();
}

View File

@@ -0,0 +1,6 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "Locomotions/GMS_AnimLayer_Additive.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_AnimLayer_Additive)

View File

@@ -0,0 +1,7 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "Locomotions/GMS_AnimLayer_Overlay.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_AnimLayer_Overlay)

View File

@@ -0,0 +1,448 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "Locomotions/GMS_AnimLayer_Overlay_ParallelPoseStack.h"
#include "Animation/AnimSequence.h"
#include "SequenceEvaluatorLibrary.h"
#include "Locomotions/GMS_MainAnimInstance.h"
#include "UObject/ObjectSaveContext.h"
#include "Utility/GMS_Log.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_AnimLayer_Overlay_ParallelPoseStack)
void FGMS_AnimData_BodyPose_Full::UpdateLayeringState(FGMS_AnimState_Layering& LayeringState) const
{
if (Pose != nullptr)
{
LayeringState.HeadBlendAmount = HeadBlend.BlendAmount;
LayeringState.HeadAdditiveBlendAmount = HeadBlend.AdditiveBlendAmount;
LayeringState.HeadSlotBlendAmount = HeadBlend.SlotBlendAmount;
LayeringState.ArmLeftBlendAmount = ArmLeftBlend.BlendAmount;
LayeringState.ArmLeftAdditiveBlendAmount = ArmLeftBlend.AdditiveBlendAmount;
LayeringState.ArmLeftSlotBlendAmount = ArmLeftBlend.SlotBlendAmount;
LayeringState.ArmLeftLocalSpaceBlendAmount = ArmLeftBlend.bMeshSpace ? 0.f : 1.f;
LayeringState.ArmLeftMeshSpaceBlendAmount = ArmLeftBlend.bMeshSpace ? 1.f : 0.f;
LayeringState.ArmRightBlendAmount = ArmRightBlend.BlendAmount;
LayeringState.ArmRightAdditiveBlendAmount = ArmRightBlend.AdditiveBlendAmount;
LayeringState.ArmRightSlotBlendAmount = ArmRightBlend.SlotBlendAmount;
LayeringState.ArmRightLocalSpaceBlendAmount = ArmRightBlend.bMeshSpace ? 0.f : 1.f;
LayeringState.ArmRightMeshSpaceBlendAmount = ArmRightBlend.bMeshSpace ? 1.f : 0.f;
LayeringState.HandLeftBlendAmount = HandLeftBlend.BlendAmount;
LayeringState.HandRightBlendAmount = HandRightBlend.BlendAmount;
LayeringState.SpineBlendAmount = SpineBlend.BlendAmount;
LayeringState.SpineAdditiveBlendAmount = SpineBlend.AdditiveBlendAmount;
LayeringState.SpineSlotBlendAmount = SpineBlend.SlotBlendAmount;
LayeringState.PelvisBlendAmount = PelvisBlend.BlendAmount;
LayeringState.PelvisSlotBlendAmount = PelvisBlend.SlotBlendAmount;
LayeringState.LegsBlendAmount = LegsBlend.BlendAmount;
LayeringState.LegsSlotBlendAmount = LegsBlend.SlotBlendAmount;
}
}
void FGMS_AnimData_BodyPose_Upper::UpdateLayeringState(FGMS_AnimState_Layering& LayeringState) const
{
if (Pose != nullptr)
{
LayeringState.HeadBlendAmount = HeadBlend.BlendAmount;
LayeringState.HeadAdditiveBlendAmount = HeadBlend.AdditiveBlendAmount;
LayeringState.HeadSlotBlendAmount = HeadBlend.SlotBlendAmount;
LayeringState.ArmLeftBlendAmount = ArmLeftBlend.BlendAmount;
LayeringState.ArmLeftAdditiveBlendAmount = ArmLeftBlend.AdditiveBlendAmount;
LayeringState.ArmLeftSlotBlendAmount = ArmLeftBlend.SlotBlendAmount;
LayeringState.ArmLeftLocalSpaceBlendAmount = ArmLeftBlend.bMeshSpace ? 0.f : 1.f;
LayeringState.ArmLeftMeshSpaceBlendAmount = ArmLeftBlend.bMeshSpace ? 1.f : 0.f;
LayeringState.ArmRightBlendAmount = ArmRightBlend.BlendAmount;
LayeringState.ArmRightAdditiveBlendAmount = ArmRightBlend.AdditiveBlendAmount;
LayeringState.ArmRightSlotBlendAmount = ArmRightBlend.SlotBlendAmount;
LayeringState.ArmRightLocalSpaceBlendAmount = ArmRightBlend.bMeshSpace ? 0.f : 1.f;
LayeringState.ArmRightMeshSpaceBlendAmount = ArmRightBlend.bMeshSpace ? 1.f : 0.f;
LayeringState.HandLeftBlendAmount = HandLeftBlend.BlendAmount;
LayeringState.HandRightBlendAmount = HandRightBlend.BlendAmount;
LayeringState.SpineBlendAmount = SpineBlend.BlendAmount;
LayeringState.SpineAdditiveBlendAmount = SpineBlend.AdditiveBlendAmount;
LayeringState.SpineSlotBlendAmount = SpineBlend.SlotBlendAmount;
}
}
void FGMS_AnimData_BodyPose_Head::UpdateLayeringState(FGMS_AnimState_Layering& LayeringState) const
{
if (Pose != nullptr)
{
LayeringState.HeadBlendAmount = Blend.BlendAmount;
LayeringState.HeadAdditiveBlendAmount = Blend.AdditiveBlendAmount;
LayeringState.HeadSlotBlendAmount = Blend.SlotBlendAmount;
}
}
void FGMS_AnimData_BodyPose_ArmLeft::UpdateLayeringState(FGMS_AnimState_Layering& LayeringState) const
{
if (Pose != nullptr)
{
LayeringState.ArmLeftBlendAmount = Blend.BlendAmount;
LayeringState.ArmLeftAdditiveBlendAmount = Blend.AdditiveBlendAmount;
LayeringState.ArmLeftSlotBlendAmount = Blend.SlotBlendAmount;
LayeringState.ArmLeftLocalSpaceBlendAmount = Blend.bMeshSpace ? 0.f : 1.f;
LayeringState.ArmLeftMeshSpaceBlendAmount = Blend.bMeshSpace ? 1.f : 0.f;
LayeringState.HandLeftBlendAmount = HandBlend.BlendAmount;
}
}
void FGMS_AnimData_BodyPose_ArmRight::UpdateLayeringState(FGMS_AnimState_Layering& LayeringState) const
{
if (Pose != nullptr)
{
LayeringState.ArmRightBlendAmount = Blend.BlendAmount;
LayeringState.ArmRightAdditiveBlendAmount = Blend.AdditiveBlendAmount;
LayeringState.ArmRightSlotBlendAmount = Blend.SlotBlendAmount;
LayeringState.ArmRightLocalSpaceBlendAmount = Blend.bMeshSpace ? 0.f : 1.f;
LayeringState.ArmRightMeshSpaceBlendAmount = Blend.bMeshSpace ? 1.f : 0.f;
LayeringState.HandRightBlendAmount = HandBlend.BlendAmount;
}
}
void FGMS_AnimData_BodyPose_Lower::UpdateLayeringState(FGMS_AnimState_Layering& LayeringState) const
{
if (Pose != nullptr)
{
LayeringState.PelvisBlendAmount = PelvisBlend.BlendAmount;
LayeringState.PelvisSlotBlendAmount = PelvisBlend.SlotBlendAmount;
LayeringState.LegsBlendAmount = LegsBlend.BlendAmount;
LayeringState.LegsSlotBlendAmount = LegsBlend.SlotBlendAmount;
}
}
// FGMS_BodyPartOverridePolicy 实现
FGMS_BodyPartOverridePolicy::FGMS_BodyPartOverridePolicy()
{
FallbackChain = {
{EGMS_BodyMask::Head, {EGMS_BodyMask::UpperBody, EGMS_BodyMask::FullBody}},
// {EGMS_BodyMask::HandLeft, {EGMS_BodyMask::ArmLeft, EGMS_BodyMask::UpperBody, EGMS_BodyMask::FullBody}},
// {EGMS_BodyMask::HandRight, {EGMS_BodyMask::ArmRight, EGMS_BodyMask::UpperBody, EGMS_BodyMask::FullBody}},
{EGMS_BodyMask::ArmLeft, {EGMS_BodyMask::UpperBody, EGMS_BodyMask::FullBody}},
{EGMS_BodyMask::ArmRight, {EGMS_BodyMask::UpperBody, EGMS_BodyMask::FullBody}},
{EGMS_BodyMask::UpperBody, {EGMS_BodyMask::FullBody}},
{EGMS_BodyMask::LowerBody, {EGMS_BodyMask::FullBody}},
{EGMS_BodyMask::FullBody, {}},
};
}
#if WITH_EDITORONLY_DATA
void FGMS_AnimData_BodyPose::PreSave()
{
bool bValid = Pose != nullptr && PoseExplicitTime >= 0 && Pose->GetSkeleton() != nullptr;
EditorFriendlyName = bValid ? GetNameSafe(Pose) : TEXT("Invalid Pose!");
}
void FGMS_OverlayModeSetting_ParallelPoseStack::PreSave()
{
for (auto& Pose : FullBodyPoses)
{
Pose.GetMutable().PreSave();
}
}
void UGMS_AnimLayerSetting_Overlay_ParallelPoseStack::PreSave(FObjectPreSaveContext SaveContext)
{
Super::PreSave(SaveContext);
AcceleratedOverlayModes.Empty();
for (FGMS_OverlayModeSetting_ParallelPoseStack& Mode : OverlayModes)
{
Mode.PreSave();
AcceleratedOverlayModes.Emplace(Mode.Tag, Mode);
}
}
#endif
bool FGMS_BodyPartOverridePolicy::CanOverride(EGMS_BodyMask NewPart, EGMS_BodyMask ExistingPart, int32 NewPriority, int32 ExistingPriority) const
{
return static_cast<uint8>(NewPart) < static_cast<uint8>(ExistingPart) ||
(NewPart == ExistingPart && NewPriority < ExistingPriority);
}
void FGMS_BodyPartOverridePolicy::ApplyCoverage(EGMS_BodyMask BodyPart, TArray<TInstancedStruct<FGMS_AnimData_BodyPose>>& SelectedPoses,
const TInstancedStruct<FGMS_AnimData_BodyPose>& NewPose, int32 NewPriority) const
{
int32 BodyPartIndex = static_cast<int32>(BodyPart);
SelectedPoses[BodyPartIndex] = NewPose;
}
void UGMS_AnimLayer_Overlay_ParallelPoseStack::ApplySetting_Implementation(const UGMS_AnimLayerSetting* Setting)
{
check(IsValid(Setting));
if (CurrentSetting != Setting || CurrentOverlayMode != GetParent()->OverlayMode)
{
ResetSetting();
}
if (const UGMS_AnimLayerSetting_Overlay_ParallelPoseStack* DS = Cast<UGMS_AnimLayerSetting_Overlay_ParallelPoseStack>(Setting))
{
if (CurrentSetting == DS && CurrentOverlayMode == GetParent()->OverlayMode)
{
return;
}
if (!DS->AcceleratedOverlayModes.Contains(GetParent()->OverlayMode))
{
return;
}
const FGMS_OverlayModeSetting_ParallelPoseStack& ModeSetting = DS->AcceleratedOverlayModes[GetParent()->OverlayMode];
if (ModeSetting.BasePose == nullptr)
{
bHasValidSetting = false;
UE_LOG(LogGMS, Warning, TEXT("BasePose is null for overlay mode %s"), *GetParent()->OverlayMode.ToString());
return;
}
CurrentSetting = DS;
CurrentOverlayMode = GetParent()->OverlayMode;
bHasValidSetting = true;
BasePose = ModeSetting.BasePose;
}
}
void UGMS_AnimLayer_Overlay_ParallelPoseStack::ResetSetting_Implementation()
{
bHasValidSetting = false;
CurrentOverlayMode = FGameplayTag::EmptyTag;
CurrentSetting = nullptr;
BasePose = nullptr;
for (auto& Pose : SelectedBodyPoses)
{
Pose.GetMutable().Reset();
}
LayeringState.ZeroOut();
}
void UGMS_AnimLayer_Overlay_ParallelPoseStack::NativeInitializeAnimation()
{
Super::NativeInitializeAnimation();
// 预分配 SelectedBodyPoses
SelectedBodyPoses.SetNum(static_cast<int32>(EGMS_BodyMask::MAX));
// 保证数组足够。
SelectedBodyPoses = {
TInstancedStruct<FGMS_AnimData_BodyPose>::Make(FGMS_AnimData_BodyPose_Head()),
TInstancedStruct<FGMS_AnimData_BodyPose>::Make(FGMS_AnimData_BodyPose_ArmLeft()),
TInstancedStruct<FGMS_AnimData_BodyPose>::Make(FGMS_AnimData_BodyPose_ArmRight()),
TInstancedStruct<FGMS_AnimData_BodyPose>::Make(FGMS_AnimData_BodyPose_Upper()),
TInstancedStruct<FGMS_AnimData_BodyPose>::Make(FGMS_AnimData_BodyPose_Lower()),
TInstancedStruct<FGMS_AnimData_BodyPose>::Make(FGMS_AnimData_BodyPose_Full()),
};
}
void UGMS_AnimLayer_Overlay_ParallelPoseStack::NativeThreadSafeUpdateAnimation(float DeltaSeconds)
{
Super::NativeThreadSafeUpdateAnimation(DeltaSeconds);
if (!bHasValidSetting)
{
LayeringState.ZeroOut();
for (auto& Pose : SelectedBodyPoses)
{
Pose.GetMutable().Reset();
}
return;
}
// Check if tags or nodes have changed
SelectPoses(GetParent()->OwnedTags, GetParent()->NodeRelevanceTags);
// Update LayeringState based on selected poses
UpdateLayeringState(DeltaSeconds);
UpdateLayeringSmoothState(DeltaSeconds);
}
#define SELECT_POSE(PoseArray, PoseType, TargetPose, Nodes, Tags) \
for (int32 Index = 0; Index < PoseArray.Num(); ++Index) \
{ \
const TInstancedStruct<PoseType>& PoseInst = PoseArray[Index];; \
const PoseType& Pose = PoseInst.Get<PoseType>(); \
if (!Pose.IsValid()) \
{ \
continue; \
} \
if (!Pose.RelevanceQuery.IsEmpty() && !Pose.RelevanceQuery.Matches(Nodes)) \
{ \
continue; \
} \
if (!Pose.TagQuery.IsEmpty() && !Pose.TagQuery.Matches(Tags)) \
{ \
continue; \
} \
if (!TargetPose.GetMutable().IsValid() || Index < TargetPose.GetMutable().Priority) \
{ \
TargetPose = PoseInst; \
TargetPose.GetMutable().Priority = Index; \
} \
if (!TargetPose.GetMutable().IsValid()) \
{ \
TargetPose.GetMutable().Reset(); \
} \
}
void UGMS_AnimLayer_Overlay_ParallelPoseStack::SelectPoses(const FGameplayTagContainer& Tags, const FGameplayTagContainer& Nodes)
{
//Reset SelectedBodyPoses
for (auto& Pose : SelectedBodyPoses)
{
Pose.GetMutable().Reset();
}
SELECT_POSE(GetOverlayModeSetting().FullBodyPoses, FGMS_AnimData_BodyPose_Full, SelectedBodyPoses[static_cast<int32>(EGMS_BodyMask::FullBody)], Nodes, Tags)
SELECT_POSE(GetOverlayModeSetting().UpperBodyPoses, FGMS_AnimData_BodyPose_Upper, SelectedBodyPoses[static_cast<int32>(EGMS_BodyMask::UpperBody)], Nodes, Tags)
SELECT_POSE(GetOverlayModeSetting().ArmLeftPoses, FGMS_AnimData_BodyPose_ArmLeft, SelectedBodyPoses[static_cast<int32>(EGMS_BodyMask::ArmLeft)], Nodes, Tags)
SELECT_POSE(GetOverlayModeSetting().ArmRightPoses, FGMS_AnimData_BodyPose_ArmRight, SelectedBodyPoses[static_cast<int32>(EGMS_BodyMask::ArmRight)], Nodes, Tags)
SELECT_POSE(GetOverlayModeSetting().HeadPoses, FGMS_AnimData_BodyPose_Head, SelectedBodyPoses[static_cast<int32>(EGMS_BodyMask::Head)], Nodes, Tags)
}
void UGMS_AnimLayer_Overlay_ParallelPoseStack::UpdateLayeringState(float DeltaSeconds)
{
// Initialize LayeringState
// LayeringState.ZeroOut();
LayeringState.HeadBlendAmount = 0.0f;
LayeringState.HeadAdditiveBlendAmount = 0.0f;
LayeringState.HeadSlotBlendAmount = 0.0f;
LayeringState.ArmLeftBlendAmount = 0.0f;
LayeringState.ArmLeftAdditiveBlendAmount = 0.0f;
LayeringState.ArmLeftSlotBlendAmount = 0.0f;
//Make this layer always enabled.(and override later)
// LayeringState.ArmLeftLocalSpaceBlendAmount = 1.0f;
// LayeringState.ArmLeftMeshSpaceBlendAmount = 1.0f;
LayeringState.ArmRightBlendAmount = 0.0f;
LayeringState.ArmRightAdditiveBlendAmount = 0.0f;
LayeringState.ArmRightSlotBlendAmount = 0.0f;
//Make this layer always enabled.(and override later)
// LayeringState.ArmRightLocalSpaceBlendAmount = 1.0f;
// LayeringState.ArmRightMeshSpaceBlendAmount = 1.0f;
LayeringState.HandLeftBlendAmount = 0.0f;
LayeringState.HandRightBlendAmount = 0.0f;
LayeringState.SpineBlendAmount = 0.0f;
LayeringState.SpineAdditiveBlendAmount = 0.0f;
LayeringState.SpineSlotBlendAmount = 0.0f;
LayeringState.PelvisBlendAmount = 0.0f;
LayeringState.PelvisSlotBlendAmount = 0.0f;
LayeringState.LegsBlendAmount = 0.0f;
LayeringState.LegsSlotBlendAmount = 0.0f;
for (int32 i = SelectedBodyPoses.Num() - 1; i >= 0; i--)
{
if (!SelectedBodyPoses[i].IsValid())
{
continue;
}
const FGMS_AnimData_BodyPose& Pose = SelectedBodyPoses[i].Get();
Pose.UpdateLayeringState(LayeringState);
}
}
void UGMS_AnimLayer_Overlay_ParallelPoseStack::UpdateLayeringSmoothState(float DeltaSeconds)
{
static constexpr float Speed = 2.0f;
LayeringSmoothState.HeadBlendAmount = LayeringState.HeadBlendAmount <= 0.0f
? FMath::FInterpConstantTo(LayeringSmoothState.HeadBlendAmount, 0.0f, DeltaSeconds, Speed)
: LayeringState.HeadBlendAmount;
LayeringSmoothState.ArmLeftBlendAmount = LayeringState.ArmLeftBlendAmount <= 0.0f
? FMath::FInterpConstantTo(LayeringSmoothState.ArmLeftBlendAmount, 0.0f, DeltaSeconds, Speed)
: LayeringState.ArmLeftBlendAmount;
LayeringSmoothState.ArmRightBlendAmount = LayeringState.ArmRightBlendAmount <= 0.0f
? FMath::FInterpConstantTo(LayeringSmoothState.ArmRightBlendAmount, 0.0f, DeltaSeconds, Speed)
: LayeringState.ArmRightBlendAmount;
LayeringSmoothState.SpineBlendAmount = LayeringState.SpineBlendAmount <= 0.0f
? FMath::FInterpConstantTo(LayeringSmoothState.SpineBlendAmount, 0.0f, DeltaSeconds, Speed)
: LayeringState.SpineBlendAmount;
LayeringSmoothState.PelvisBlendAmount = LayeringState.PelvisBlendAmount <= 0.0f
? FMath::FInterpConstantTo(LayeringSmoothState.PelvisBlendAmount, 0.0f, DeltaSeconds, Speed)
: LayeringState.PelvisBlendAmount;
LayeringSmoothState.LegsBlendAmount = LayeringState.LegsBlendAmount <= 0.0f
? FMath::FInterpConstantTo(LayeringSmoothState.LegsBlendAmount, 0.0f, DeltaSeconds, Speed)
: LayeringState.LegsBlendAmount;
}
const FGMS_OverlayModeSetting_ParallelPoseStack& UGMS_AnimLayer_Overlay_ParallelPoseStack::GetOverlayModeSetting() const
{
return CurrentSetting->AcceleratedOverlayModes[CurrentOverlayMode];
}
void UGMS_AnimLayer_Overlay_ParallelPoseStack::UpdateAnim(const FAnimUpdateContext& Context, const FAnimNodeReference& Node, const EGMS_BodyMask& BodyMask, const FGMS_AnimData_BodyPose& BodyPose)
{
if (BodyPose.Pose == nullptr)
{
GMS_LOG(Warning, "Trying to update an empty pose for BodyMask %s.", *UEnum::GetValueAsString(BodyMask));
return;
}
EAnimNodeReferenceConversionResult Result = EAnimNodeReferenceConversionResult::Succeeded;
FSequenceEvaluatorReference SequenceEvaluator = USequenceEvaluatorLibrary::ConvertToSequenceEvaluator(Node, Result);
if (Result == EAnimNodeReferenceConversionResult::Succeeded && USequenceEvaluatorLibrary::GetSequence(SequenceEvaluator) != BodyPose.Pose)
{
GMS_LOG(Verbose, "Updated pose %s for BodyMask %s ", *GetNameSafe(BodyPose.Pose), *UEnum::GetValueAsString(BodyMask));
USequenceEvaluatorLibrary::SetExplicitTime(SequenceEvaluator, BodyPose.PoseExplicitTime);
USequenceEvaluatorLibrary::SetSequenceWithInertialBlending(Context, SequenceEvaluator, BodyPose.Pose, 0.1f);
}
}
void UGMS_AnimLayer_Overlay_ParallelPoseStack::BasePose_AnimUpdate_Implementation(FAnimUpdateContext& Context, FAnimNodeReference& Node)
{
if (!bHasValidSetting)
{
return;
}
const FGMS_OverlayModeSetting_ParallelPoseStack& ModeSetting = GetOverlayModeSetting();
EAnimNodeReferenceConversionResult Result = EAnimNodeReferenceConversionResult::Succeeded;
FSequenceEvaluatorReference SequenceEvaluator = USequenceEvaluatorLibrary::ConvertToSequenceEvaluator(Node, Result);
if (Result == EAnimNodeReferenceConversionResult::Succeeded)
{
USequenceEvaluatorLibrary::SetExplicitTime(SequenceEvaluator, 0.0f);
USequenceEvaluatorLibrary::SetSequenceWithInertialBlending(Context, SequenceEvaluator, ModeSetting.BasePose);
}
}
void UGMS_AnimLayer_Overlay_ParallelPoseStack::BodyPart_AnimUpdate_Implementation(FAnimUpdateContext& Context, FAnimNodeReference& Node, EGMS_BodyMask BodyMask)
{
if (!bHasValidSetting)
{
return;
}
int32 BodyPartIndex = static_cast<int32>(BodyMask);
if (SelectedBodyPoses[BodyPartIndex].Get().IsValid())
{
UpdateAnim(Context, Node, BodyMask, SelectedBodyPoses[BodyPartIndex].Get());
return;
}
// 回退逻辑
if (const TArray<EGMS_BodyMask>* FallbackParts = OverridePolicy.FallbackChain.Find(BodyMask))
{
for (EGMS_BodyMask FallbackPart : *FallbackParts)
{
int32 FallbackIndex = static_cast<int32>(FallbackPart);
if (SelectedBodyPoses[FallbackIndex].Get().IsValid())
{
UpdateAnim(Context, Node, BodyMask, SelectedBodyPoses[FallbackIndex].Get());
return;
}
}
}
}

View File

@@ -0,0 +1,237 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "Locomotions/GMS_AnimLayer_Overlay_ParallelSequenceStack.h"
#include "Locomotions/GMS_MainAnimInstance.h"
#include "UObject/ObjectSaveContext.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_AnimLayer_Overlay_ParallelSequenceStack)
void UGMS_AnimLayer_Overlay_ParallelSequenceStack::NativeBeginPlay()
{
Super::NativeBeginPlay();
if (OverlayStackStates.IsEmpty())
{
OverlayStackStates.AddDefaulted(MaxLayers);
}
}
void UGMS_AnimLayer_Overlay_ParallelSequenceStack::NativeUpdateAnimation(float DeltaSeconds)
{
Super::NativeUpdateAnimation(DeltaSeconds);
}
void UGMS_AnimLayer_Overlay_ParallelSequenceStack::NativeThreadSafeUpdateAnimation(float DeltaSeconds)
{
Super::NativeThreadSafeUpdateAnimation(DeltaSeconds);
RefreshRelevance();
RefreshBlend(DeltaSeconds);
}
void UGMS_AnimLayer_Overlay_ParallelSequenceStack::ApplySetting_Implementation(const UGMS_AnimLayerSetting* Setting)
{
check(IsValid(Setting))
//setting changes or invalid, reset.
if (PrevSetting != Setting || PrevOverlayMode != GetParent()->OverlayMode)
{
PrevOverlayMode = FGameplayTag::EmptyTag;
ResetSetting();
PrevSetting = nullptr;
}
// Apply new data to anim instance.
if (const UGMS_AnimLayerSetting_Overlay_ParallelSequenceStack* DS = Cast<UGMS_AnimLayerSetting_Overlay_ParallelSequenceStack>(Setting))
{
//same setting and overlay.
if (PrevSetting == DS && PrevOverlayMode == GetParent()->OverlayMode)
{
return;
}
PrevSetting = DS;
PrevOverlayMode = GetParent()->OverlayMode;
if (OverlayStackStates.IsEmpty())
{
OverlayStackStates.AddDefaulted(MaxLayers);
}
if (!DS->AcceleratedOverlayModes.Contains(GetParent()->OverlayMode))
{
return;
}
const TArray<FGMS_ParallelSequenceStack>& Stacks = DS->AcceleratedOverlayModes[GetParent()->OverlayMode].Stacks;
for (int32 i = 0; i < OverlayStackStates.Num(); i++)
{
if (Stacks.IsValidIndex(i))
{
OverlayStacks.EmplaceAt(i, Stacks[i]);
}
else
{
OverlayStacks.EmplaceAt(i, FGMS_ParallelSequenceStack());
}
}
}
}
void UGMS_AnimLayer_Overlay_ParallelSequenceStack::ResetSetting_Implementation()
{
OverlayStacks.Reset(MaxLayers);
for (FGMS_ParallelSequenceStackState& OverlayStackState : OverlayStackStates)
{
OverlayStackState.Overlay.bValid = false;
}
}
void UGMS_AnimLayer_Overlay_ParallelSequenceStack::RefreshRelevance()
{
if (IsValid(GetParent()))
{
for (int32 i = 0; i < OverlayStackStates.Num(); i++)
{
if (OverlayStacks.IsValidIndex(i))
{
OverlayStackStates[i].bRelevant = GetParent()->NodeRelevanceTags.HasAny(OverlayStacks[i].TargetAnimNodes);
}
else
{
OverlayStackStates[i].bRelevant = false;
}
}
}
}
void UGMS_AnimLayer_Overlay_ParallelSequenceStack::RefreshBlend(float DeltaSeconds)
{
if (IsValid(GetParent()))
{
const FGameplayTagContainer& Tags = GetParent()->OwnedTags;
//Refresh current overlay.
for (int32 i = 0; i < OverlayStackStates.Num(); i++)
{
int32 foundJ = INDEX_NONE;
if (OverlayStacks.IsValidIndex(i))
{
for (int32 j = 0; j < OverlayStacks[i].Overlays.Num(); j++)
{
const FGMS_ParallelSequenceStackEntry& Overlay = OverlayStacks[i].Overlays[j];
if (Overlay.bValid && (Overlay.TagQuery.IsEmpty() || Overlay.TagQuery.Matches(Tags)))
{
foundJ = j;
break;
}
}
}
if (foundJ != INDEX_NONE)
{
if (OverlayStacks[i].Overlays[foundJ] != OverlayStackStates[i].Overlay)
{
OverlayStackStates[i].Overlay = OverlayStacks[i].Overlays[foundJ];
OverlayStackStates[i].BlendOutSpeed = OverlayStacks[i].Overlays[foundJ].BlendOutSpeed;
}
}
else
{
OverlayStackStates[i].Overlay.bValid = false;
}
}
//Refresh blends.
for (int32 i = 0; i < OverlayStackStates.Num(); i++)
{
FGMS_ParallelSequenceStackState& CurrentState = OverlayStackStates[i];
if (CurrentState.Overlay.bValid && CurrentState.bRelevant)
{
if (CurrentState.Overlay.BlendInSpeed > 0)
{
CurrentState.BlendWeight = FMath::FInterpTo(CurrentState.BlendWeight, CurrentState.Overlay.BlendWeight, DeltaSeconds, CurrentState.Overlay.BlendInSpeed);
}
else
{
CurrentState.BlendWeight = CurrentState.Overlay.BlendWeight;
}
}
else
{
if (CurrentState.BlendOutSpeed > 0)
{
CurrentState.BlendWeight = FMath::FInterpTo(CurrentState.BlendWeight, 0.0f, DeltaSeconds, CurrentState.BlendOutSpeed);
}
else
{
CurrentState.BlendWeight = 0.0f;
}
}
}
}
}
#if WITH_EDITORONLY_DATA
void FGMS_ParallelSequenceStackEntry::Validate()
{
bValid = true;
if (Sequence == nullptr)
{
bValid = false;
EditorMessage = TEXT("Invalid Overlay,No valid sequence");
return;
}
if (BlendMode == EGMS_LayeredBoneBlendMode::BlendMask)
{
if (Sequence->GetSkeleton() == nullptr || BlendMaskName == NAME_None)
{
bValid = false;
EditorMessage = TEXT("Invalid Overlay,No valid blend mask name!");
return;
}
UBlendProfile* BlendMask = Sequence->GetSkeleton()->GetBlendProfile(BlendMaskName);
if (BlendMask == nullptr)
{
bValid = false;
EditorMessage = TEXT("Invalid Overlay,The skeleton of animation doesn't have specified BlendMask");
return;
}
}
if (bValid)
{
EditorMessage = FString::Format(TEXT("Play ({0}) on ({1}) with condition({2})"), {Sequence->GetName(), BlendMaskName.ToString(), TagQuery.GetDescription()});
}
else
{
EditorMessage = TEXT("Invalid Overlay");
}
}
void UGMS_AnimLayerSetting_Overlay_ParallelSequenceStack::PreSave(FObjectPreSaveContext SaveContext)
{
AcceleratedOverlayModes.Empty();
for (FGMS_OverlayModeSetting_ParallelSequenceStack& Mode : OverlayModes)
{
for (FGMS_ParallelSequenceStack& Stack : Mode.Stacks)
{
for (FGMS_ParallelSequenceStackEntry& Overlay : Stack.Overlays)
{
Overlay.Validate();
}
Stack.EditorFriendlyName = Stack.TargetAnimNodes.IsEmpty()
? TEXT("Invalid Overlay")
: FString::Format(TEXT("Play overlay for states:[{0}]"), {Stack.TargetAnimNodes.ToStringSimple(false).Replace(TEXT("GMS.SM."), TEXT(""))});
}
AcceleratedOverlayModes.Emplace(Mode.Tag, Mode);
}
Super::PreSave(SaveContext);
}
#endif

View File

@@ -0,0 +1,387 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "Locomotions/GMS_AnimLayer_Overlay_PoseStack.h"
#include "AnimationWarpingLibrary.h"
#include "Locomotions/GMS_MainAnimInstance.h"
#include "Utility/GMS_Log.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_AnimLayer_Overlay_PoseStack)
#if WITH_EDITOR
#include "UObject/ObjectSaveContext.h"
void UGMS_AnimLayerSetting_Overlay_PoseStack::RunDataMigration(bool bResetDeprecatedSettings)
{
Modify();
PRAGMA_DISABLE_DEPRECATION_WARNINGS
for (FGMS_OverlayModeSetting_PoseStack& Mode : OverlayModes)
{
Mode.Poses.Empty();
// migrate simple pose.
if (Mode.PoseOverlaySettingType == EGMS_PoseOverlaySettingType::Simple)
{
if (Mode.SimplePoseSetting.IdlePose)
{
FGMS_PoseStackEntry Entry;
Entry.Pose = Mode.SimplePoseSetting.IdlePose;
Entry.ExplicitTime = Mode.SimplePoseSetting.IdlePoseExplicitTime;
Entry.AimingSweepPose = Mode.SimplePoseSetting.AimingSweepPose;
Entry.PoseBlend.ApplyFromSequence(Mode.SimplePoseSetting.IdlePose, Mode.SimplePoseSetting.IdlePoseExplicitTime);
Entry.RelevanceQuery = FGameplayTagQuery::BuildQuery(FGameplayTagQueryExpression().AnyTagsMatch().AddTag(FGameplayTag::RequestGameplayTag(TEXT("GMS.SM.Grounded.Idle"))));
Mode.Poses.Add(Entry);
}
if (Mode.SimplePoseSetting.MovingPose)
{
FGMS_PoseStackEntry Entry;
Entry.Pose = Mode.SimplePoseSetting.MovingPose;
Entry.ExplicitTime = Mode.SimplePoseSetting.MovingPoseExplicitTime;
Entry.AimingSweepPose = Mode.SimplePoseSetting.AimingSweepPose;
Entry.PoseBlend.ApplyFromSequence(Mode.SimplePoseSetting.MovingPose, Mode.SimplePoseSetting.MovingPoseExplicitTime);
Mode.Poses.Add(Entry);
}
}
// migrate layered pose.
if (Mode.PoseOverlaySettingType == EGMS_PoseOverlaySettingType::Layered)
{
for (const FGMS_AnimData_PoseOverlay_Layered& OldSetting : Mode.LayeredPoseSetting)
{
if (OldSetting.MovingPose)
{
// Has no idle tag.
FGameplayTagQueryExpression RelevanceExp = FGameplayTagQueryExpression().NoTagsMatch().AddTag(FGameplayTag::RequestGameplayTag(TEXT("GMS.SM.Grounded.Idle")));
FGMS_PoseStackEntry Entry;
Entry.Pose = OldSetting.MovingPose;
Entry.ExplicitTime = OldSetting.MovingPoseExplicitTime;
Entry.AimingSweepPose = OldSetting.AimingSweepPose;
Entry.PoseBlend.ApplyFromSequence(OldSetting.MovingPose, OldSetting.MovingPoseExplicitTime);
Entry.TagQuery = OldSetting.TagQuery;
Entry.RelevanceQuery = FGameplayTagQuery::BuildQuery(RelevanceExp);
Mode.Poses.Add(Entry);
}
if (OldSetting.IdlePose)
{
// Must have idle tag.
FGameplayTagQueryExpression RelevanceExp = FGameplayTagQueryExpression().AllTagsMatch().AddTag(FGameplayTag::RequestGameplayTag(TEXT("GMS.SM.Grounded.Idle")));
FGameplayTagQueryExpression Exp2;
OldSetting.TagQuery.GetQueryExpr(Exp2);
FGMS_PoseStackEntry Entry;
Entry.Pose = OldSetting.IdlePose;
Entry.ExplicitTime = OldSetting.IdlePoseExplicitTime;
Entry.AimingSweepPose = OldSetting.AimingSweepPose;
Entry.PoseBlend.ApplyFromSequence(OldSetting.IdlePose, OldSetting.IdlePoseExplicitTime);
Entry.RelevanceQuery = FGameplayTagQuery::BuildQuery(FGameplayTagQueryExpression().AllExprMatch().AddExpr(RelevanceExp).AddExpr(Exp2));
Mode.Poses.Add(Entry);
}
}
}
if (bResetDeprecatedSettings)
{
Mode.SimplePoseSetting = FGMS_AnimData_PoseOverlay_Simple();
Mode.LayeredPoseSetting.Empty();
}
}
PRAGMA_ENABLE_DEPRECATION_WARNINGS
}
void UGMS_AnimLayerSetting_Overlay_PoseStack::RunDataMigrationFromDefinition(UGMS_MovementDefinition* InDefinition, bool bResetDeprecatedSettings)
{
if (IsValid(InDefinition))
{
InDefinition->Modify();
for (TPair<FGameplayTag, FGMS_MovementSetSetting>& MovementSet : InDefinition->MovementSets)
{
FGMS_MovementSetSetting& MSS = MovementSet.Value;
if (MSS.bUseInstancedOverlaySetting && MSS.AnimLayerSetting_Overlay)
{
if (auto Overlay = Cast<UGMS_AnimLayerSetting_Overlay_PoseStack>(MSS.AnimLayerSetting_Overlay))
{
Overlay->RunDataMigration(bResetDeprecatedSettings);
}
}
else if (!MSS.bUseInstancedOverlaySetting && MSS.DA_AnimLayerSetting_Overlay)
{
if (auto Overlay = Cast<UGMS_AnimLayerSetting_Overlay_PoseStack>(MSS.DA_AnimLayerSetting_Overlay))
{
Overlay->RunDataMigration(bResetDeprecatedSettings);
}
}
}
}
}
void UGMS_AnimLayerSetting_Overlay_PoseStack::PreSave(FObjectPreSaveContext SaveContext)
{
Super::PreSave(SaveContext);
for (FGMS_OverlayModeSetting_PoseStack& OverlayMode : OverlayModes)
{
for (FGMS_PoseStackEntry& Entry : OverlayMode.Poses)
{
Entry.EditorFriendlyName = FString::Format(TEXT("{0} at {1}"), {
GetNameSafe(Entry.Pose), Entry.ExplicitTime
});
}
}
AcceleratedOverlayModes.Empty();
for (const FGMS_OverlayModeSetting_PoseStack& PoseOverlay : OverlayModes)
{
AcceleratedOverlayModes.Emplace(PoseOverlay.Tag, PoseOverlay);
}
}
#endif
void FGMS_PerBodyPoseBlendSetting::ApplyFromSequence(const UAnimSequence* InSequence, float ExplicitTime)
{
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerHead", ExplicitTime, HeadBlend.BlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerHeadAdditive", ExplicitTime, HeadBlend.AdditiveBlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerHeadSlot", ExplicitTime, HeadBlend.SlotBlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerArmLeft", ExplicitTime, ArmLeftBlend.BlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerArmLeftAdditive", ExplicitTime, ArmLeftBlend.AdditiveBlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerArmLeftSlot", ExplicitTime, ArmLeftBlend.SlotBlendAmount);
float ArmLeftLocalSpaceBlendAmount = 0;
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerArmLeftLocalSpace", ExplicitTime, ArmLeftLocalSpaceBlendAmount);
ArmLeftBlend.bMeshSpace = !FAnimWeight::IsFullWeight(ArmLeftLocalSpaceBlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerArmRight", ExplicitTime, ArmRightBlend.BlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerArmRightAdditive", ExplicitTime, ArmRightBlend.AdditiveBlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerArmRightSlot", ExplicitTime, ArmRightBlend.SlotBlendAmount);
float ArmRightLocalSpaceBlendAmount = 0;
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerArmRightLocalSpace", ExplicitTime, ArmRightLocalSpaceBlendAmount);
ArmRightBlend.bMeshSpace = !FAnimWeight::IsFullWeight(ArmRightLocalSpaceBlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerHandLeft", ExplicitTime, HandLeftBlend.BlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerHandRight", ExplicitTime, HandLeftBlend.BlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerSpine", ExplicitTime, SpineBlend.BlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerSpineAdditive", ExplicitTime, SpineBlend.AdditiveBlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerSpineSlot", ExplicitTime, SpineBlend.SlotBlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerPelvis", ExplicitTime, PelvisBlend.BlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerPelvisSlot", ExplicitTime, PelvisBlend.SlotBlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerLegs", ExplicitTime, LegsBlend.BlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerLegsSlot", ExplicitTime, LegsBlend.BlendAmount);
}
void FGMS_PerBodyPoseBlendSetting::ApplyToLayeringState(FGMS_AnimState_Layering& InLayeringState) const
{
InLayeringState.HeadBlendAmount = HeadBlend.BlendAmount;
InLayeringState.HeadAdditiveBlendAmount = HeadBlend.AdditiveBlendAmount;
InLayeringState.HeadSlotBlendAmount = HeadBlend.SlotBlendAmount;
InLayeringState.ArmLeftBlendAmount = ArmLeftBlend.BlendAmount;
InLayeringState.ArmLeftAdditiveBlendAmount = ArmLeftBlend.AdditiveBlendAmount;
InLayeringState.ArmLeftSlotBlendAmount = ArmLeftBlend.SlotBlendAmount;
InLayeringState.ArmLeftLocalSpaceBlendAmount = ArmLeftBlend.bMeshSpace ? 0.f : 1.f;
InLayeringState.ArmLeftMeshSpaceBlendAmount = ArmLeftBlend.bMeshSpace ? 1.f : 0.f;
InLayeringState.ArmRightBlendAmount = ArmRightBlend.BlendAmount;
InLayeringState.ArmRightAdditiveBlendAmount = ArmRightBlend.AdditiveBlendAmount;
InLayeringState.ArmRightSlotBlendAmount = ArmRightBlend.SlotBlendAmount;
InLayeringState.ArmRightLocalSpaceBlendAmount = ArmRightBlend.bMeshSpace ? 0.f : 1.f;
InLayeringState.ArmRightMeshSpaceBlendAmount = ArmRightBlend.bMeshSpace ? 1.f : 0.f;
InLayeringState.HandLeftBlendAmount = HandLeftBlend.BlendAmount;
InLayeringState.HandRightBlendAmount = HandRightBlend.BlendAmount;
InLayeringState.SpineBlendAmount = SpineBlend.BlendAmount;
InLayeringState.SpineAdditiveBlendAmount = SpineBlend.AdditiveBlendAmount;
InLayeringState.SpineSlotBlendAmount = SpineBlend.SlotBlendAmount;
InLayeringState.PelvisBlendAmount = PelvisBlend.BlendAmount;
InLayeringState.PelvisSlotBlendAmount = PelvisBlend.SlotBlendAmount;
InLayeringState.LegsBlendAmount = LegsBlend.BlendAmount;
InLayeringState.LegsSlotBlendAmount = LegsBlend.SlotBlendAmount;
}
void FGMS_PerBodyPoseBlendSetting::ApplyFromLayeringState(const FGMS_AnimState_Layering& InLayeringState)
{
HeadBlend.BlendAmount = InLayeringState.HeadBlendAmount;
HeadBlend.AdditiveBlendAmount = InLayeringState.HeadAdditiveBlendAmount;
HeadBlend.SlotBlendAmount = InLayeringState.HeadSlotBlendAmount;
ArmLeftBlend.BlendAmount = InLayeringState.ArmLeftBlendAmount;
ArmLeftBlend.AdditiveBlendAmount = InLayeringState.ArmLeftAdditiveBlendAmount;
ArmLeftBlend.SlotBlendAmount = InLayeringState.ArmLeftSlotBlendAmount;
ArmLeftBlend.bMeshSpace = InLayeringState.ArmLeftMeshSpaceBlendAmount > 0.f;
ArmRightBlend.BlendAmount = InLayeringState.ArmRightBlendAmount;
ArmRightBlend.AdditiveBlendAmount = InLayeringState.ArmRightAdditiveBlendAmount;
ArmRightBlend.SlotBlendAmount = InLayeringState.ArmRightSlotBlendAmount;
ArmRightBlend.bMeshSpace = InLayeringState.ArmRightMeshSpaceBlendAmount > 0.f;
HandLeftBlend.BlendAmount = InLayeringState.HandLeftBlendAmount;
HandRightBlend.BlendAmount = InLayeringState.HandRightBlendAmount;
SpineBlend.BlendAmount = InLayeringState.SpineBlendAmount;
SpineBlend.AdditiveBlendAmount = InLayeringState.SpineAdditiveBlendAmount;
SpineBlend.SlotBlendAmount = InLayeringState.SpineSlotBlendAmount;
PelvisBlend.BlendAmount = InLayeringState.PelvisBlendAmount;
PelvisBlend.SlotBlendAmount = InLayeringState.PelvisSlotBlendAmount;
LegsBlend.BlendAmount = InLayeringState.LegsBlendAmount;
LegsBlend.SlotBlendAmount = InLayeringState.LegsSlotBlendAmount;
}
bool UGMS_AnimLayerSetting_Overlay_PoseStack::IsValidForOverlayMode(const FGameplayTag& NewOverlayMode) const
{
return AcceleratedOverlayModes.Contains(NewOverlayMode) && AcceleratedOverlayModes[NewOverlayMode].BasePose != nullptr;
}
void UGMS_AnimLayer_Overlay_PoseStack::ApplySetting_Implementation(const UGMS_AnimLayerSetting* Setting)
{
check(IsValid(Setting));
if (CurrentSetting != Setting || CurrentOverlayMode != GetParent()->OverlayMode)
{
ResetSetting();
}
if (const UGMS_AnimLayerSetting_Overlay_PoseStack* DS = Cast<UGMS_AnimLayerSetting_Overlay_PoseStack>(Setting))
{
if (CurrentSetting == DS && CurrentOverlayMode == GetParent()->OverlayMode)
{
return;
}
if (!DS->AcceleratedOverlayModes.Contains(GetParent()->OverlayMode))
{
return;
}
const FGMS_OverlayModeSetting_PoseStack& ModeSetting = DS->AcceleratedOverlayModes[GetParent()->OverlayMode];
if (ModeSetting.BasePose == nullptr)
{
bHasValidSetting = false;
UE_LOG(LogGMS, Warning, TEXT("BasePose is null for overlay mode %s"), *GetParent()->OverlayMode.ToString());
return;
}
CurrentSetting = DS;
CurrentOverlayMode = GetParent()->OverlayMode;
bHasValidSetting = true;
BasePose = ModeSetting.BasePose;
}
}
void UGMS_AnimLayer_Overlay_PoseStack::ResetSetting_Implementation()
{
bHasValidSetting = false;
CurrentOverlayMode = FGameplayTag::EmptyTag;
CurrentSetting = nullptr;
BasePose = nullptr;
LayeringState.ZeroOut();
}
bool UGMS_AnimLayer_Overlay_PoseStack::SelectPose()
{
const FGMS_OverlayModeSetting_PoseStack& OS = GetOverlayModeSetting();
BasePose = OS.BasePose;
for (int32 i = 0; i < OS.Poses.Num(); i++)
{
const FGMS_PoseStackEntry& Entry = OS.Poses[i];
bool bMatchesOwnedTags = Entry.TagQuery.IsEmpty() || Entry.TagQuery.Matches(GetParent()->OwnedTags);
bool bMatchesRelevanceTags = Entry.RelevanceQuery.IsEmpty() || Entry.RelevanceQuery.Matches(GetParent()->NodeRelevanceTags);
if (bMatchesOwnedTags && bMatchesRelevanceTags)
{
Pose = Entry.Pose;
ExplicitTime = Entry.ExplicitTime;
AimingSweepPose = Entry.AimingSweepPose;
bValidAimingPose = AimingSweepPose != nullptr;
Entry.PoseBlend.ApplyToLayeringState(LayeringState);
return true;
}
}
return false;
}
void UGMS_AnimLayer_Overlay_PoseStack::NativeThreadSafeUpdateAnimation(float DeltaSeconds)
{
Super::NativeThreadSafeUpdateAnimation(DeltaSeconds);
if (!bHasValidSetting)
{
return;
}
if (!SelectPose())
{
Pose = nullptr;
ExplicitTime = 0.0f;
bValidPose = false;
AimingSweepPose = nullptr;
bValidAimingPose = false;
LayeringState.ZeroOut();
}
bValidPose = Pose ? true : false;
UpdateLayeringSmoothState(DeltaSeconds);
}
const FGMS_OverlayModeSetting_PoseStack& UGMS_AnimLayer_Overlay_PoseStack::GetOverlayModeSetting() const
{
return CurrentSetting->AcceleratedOverlayModes[CurrentOverlayMode];
}
void UGMS_AnimLayer_Overlay_PoseStack::UpdateLayeringSmoothState(float DeltaSeconds)
{
if (LayeringSmoothSpeed <= 0.0f)
{
LayeringSmoothState.HeadBlendAmount = LayeringState.HeadBlendAmount;
LayeringSmoothState.ArmLeftBlendAmount = LayeringState.ArmLeftBlendAmount;
LayeringSmoothState.ArmRightBlendAmount = LayeringState.ArmRightBlendAmount;
LayeringSmoothState.SpineBlendAmount = LayeringState.SpineBlendAmount;
LayeringSmoothState.PelvisBlendAmount = LayeringState.PelvisBlendAmount;
LayeringSmoothState.LegsBlendAmount = LayeringState.LegsBlendAmount;
return;
}
LayeringSmoothState.HeadBlendAmount = LayeringState.HeadBlendAmount <= 0.0f
? FMath::FInterpConstantTo(LayeringSmoothState.HeadBlendAmount, 0.0f, DeltaSeconds, LayeringSmoothSpeed)
: LayeringState.HeadBlendAmount;
LayeringSmoothState.ArmLeftBlendAmount = LayeringState.ArmLeftBlendAmount <= 0.0f
? FMath::FInterpConstantTo(LayeringSmoothState.ArmLeftBlendAmount, 0.0f, DeltaSeconds, LayeringSmoothSpeed)
: LayeringState.ArmLeftBlendAmount;
LayeringSmoothState.ArmRightBlendAmount = LayeringState.ArmRightBlendAmount <= 0.0f
? FMath::FInterpConstantTo(LayeringSmoothState.ArmRightBlendAmount, 0.0f, DeltaSeconds, LayeringSmoothSpeed)
: LayeringState.ArmRightBlendAmount;
LayeringSmoothState.SpineBlendAmount = LayeringState.SpineBlendAmount <= 0.0f
? FMath::FInterpConstantTo(LayeringSmoothState.SpineBlendAmount, 0.0f, DeltaSeconds, LayeringSmoothSpeed)
: LayeringState.SpineBlendAmount;
LayeringSmoothState.PelvisBlendAmount = LayeringState.PelvisBlendAmount <= 0.0f
? FMath::FInterpConstantTo(LayeringSmoothState.PelvisBlendAmount, 0.0f, DeltaSeconds, LayeringSmoothSpeed)
: LayeringState.PelvisBlendAmount;
LayeringSmoothState.LegsBlendAmount = LayeringState.LegsBlendAmount <= 0.0f
? FMath::FInterpConstantTo(LayeringSmoothState.LegsBlendAmount, 0.0f, DeltaSeconds, LayeringSmoothSpeed)
: LayeringState.LegsBlendAmount;
// LayeringSmoothState.HeadBlendAmount = FMath::FInterpConstantTo(LayeringSmoothState.HeadBlendAmount, LayeringState.HeadBlendAmount, DeltaSeconds, LayeringSmoothSpeed);
//
// LayeringSmoothState.ArmLeftBlendAmount = FMath::FInterpConstantTo(LayeringSmoothState.ArmLeftBlendAmount, LayeringState.ArmLeftBlendAmount, DeltaSeconds, LayeringSmoothSpeed);
//
// LayeringSmoothState.ArmRightBlendAmount = FMath::FInterpConstantTo(LayeringSmoothState.ArmRightBlendAmount, LayeringState.ArmRightBlendAmount, DeltaSeconds, LayeringSmoothSpeed);
//
// LayeringSmoothState.SpineBlendAmount = FMath::FInterpConstantTo(LayeringSmoothState.SpineBlendAmount, LayeringState.SpineBlendAmount, DeltaSeconds, LayeringSmoothSpeed);
//
//
// LayeringSmoothState.PelvisBlendAmount = FMath::FInterpConstantTo(LayeringSmoothState.PelvisBlendAmount, LayeringState.PelvisBlendAmount, DeltaSeconds, LayeringSmoothSpeed);
//
// LayeringSmoothState.LegsBlendAmount = FMath::FInterpConstantTo(LayeringSmoothState.LegsBlendAmount, LayeringState.LegsBlendAmount, DeltaSeconds, LayeringSmoothSpeed);
}

View File

@@ -0,0 +1,207 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "Locomotions/GMS_AnimLayer_Overlay_SequenceStack.h"
#include "Locomotions/GMS_MainAnimInstance.h"
#include "Utility/GMS_Log.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_AnimLayer_Overlay_SequenceStack)
#if WITH_EDITOR
#include "Locomotions/GMS_AnimLayer_Overlay_ParallelSequenceStack.h"
#include "UObject/ObjectSaveContext.h"
void UGMS_AnimLayerSetting_Overlay_SequenceStack::ConvertToSequenceStack(const UGMS_AnimLayerSetting_Overlay_ParallelSequenceStack* Src)
{
if (!IsValid(Src))
{
return;
}
for (auto& Pair : Src->AcceleratedOverlayModes)
{
auto& OldMode = Pair.Value;
FGMS_OverlayModeSetting_SequenceStack NewMode;
NewMode.Tag = OldMode.Tag;
for (int32 i = OldMode.Stacks.Num() - 1; i >= 0; i--)
{
const FGMS_ParallelSequenceStack& OldStack = OldMode.Stacks[i];
for (int32 j = 0; j < OldStack.Overlays.Num(); j++)
{
const FGMS_ParallelSequenceStackEntry& OldEntry = OldStack.Overlays[j];
UAnimSequence* OldSequence = Cast<UAnimSequence>(OldEntry.Sequence);
if (OldSequence == nullptr)
{
continue;
}
FGMS_SequenceStackEntry NewEntry;
NewEntry.RelevanceQuery = FGameplayTagQuery::BuildQuery(FGameplayTagQueryExpression().AnyTagsMatch().AddTags(OldStack.TargetAnimNodes));
NewEntry.TagQuery = OldEntry.TagQuery;
NewEntry.Sequence = OldSequence;
NewEntry.PlayMode = OldEntry.PlayMode;
NewEntry.BlendWeight = OldEntry.BlendWeight;
NewEntry.MeshSpaceBlend = OldEntry.MeshSpaceBlend;
NewEntry.AnimationTime = OldEntry.PlayMode == EGMS_OverlayPlayMode::SequenceEvaluator ? OldEntry.ExplicitTime : OldEntry.StartPosition;
NewEntry.BlendMode = OldEntry.BlendMode;
NewEntry.BlendMaskName = OldEntry.BlendMaskName;
NewEntry.BranchFilters = OldEntry.BranchFilters;
NewEntry.BlendTime = 0.2f;
NewMode.Sequences.Add(NewEntry);
}
}
OverlayModes.Add(NewMode);
}
}
void UGMS_AnimLayerSetting_Overlay_SequenceStack::ConvertToSequenceStackFromDefinition(UGMS_MovementDefinition* InDefinition)
{
if (IsValid(InDefinition))
{
InDefinition->Modify();
for (TPair<FGameplayTag, FGMS_MovementSetSetting>& MovementSet : InDefinition->MovementSets)
{
FGMS_MovementSetSetting& MSS = MovementSet.Value;
if (MSS.bUseInstancedOverlaySetting && MSS.AnimLayerSetting_Overlay)
{
if (auto Src = Cast<UGMS_AnimLayerSetting_Overlay_ParallelSequenceStack>(MSS.AnimLayerSetting_Overlay))
{
auto NewSequenceStack = NewObject<UGMS_AnimLayerSetting_Overlay_SequenceStack>(InDefinition, StaticClass());
NewSequenceStack->ConvertToSequenceStack(Src);;
MSS.AnimLayerSetting_Overlay = NewSequenceStack;
}
}
else if (!MSS.bUseInstancedOverlaySetting && MSS.DA_AnimLayerSetting_Overlay)
{
if (auto Src = Cast<UGMS_AnimLayerSetting_Overlay_ParallelSequenceStack>(MSS.DA_AnimLayerSetting_Overlay))
{
auto NewSequenceStack = NewObject<UGMS_AnimLayerSetting_Overlay_SequenceStack>(InDefinition, StaticClass());
NewSequenceStack->ConvertToSequenceStack(Src);;
MSS.DA_AnimLayerSetting_Overlay = nullptr;
MSS.bUseInstancedOverlaySetting = true;
MSS.AnimLayerSetting_Overlay = NewSequenceStack;
}
}
}
}
}
void UGMS_AnimLayerSetting_Overlay_SequenceStack::PreSave(FObjectPreSaveContext SaveContext)
{
AcceleratedOverlayModes.Empty();
for (FGMS_OverlayModeSetting_SequenceStack& Mode : OverlayModes)
{
for (FGMS_SequenceStackEntry& Entry : Mode.Sequences)
{
Entry.EditorFriendlyName = FString::Format(TEXT("{0} "), {
GetNameSafe(Entry.Sequence)
});
}
AcceleratedOverlayModes.Emplace(Mode.Tag, Mode);
}
Super::PreSave(SaveContext);
}
#endif
void UGMS_AnimLayer_Overlay_SequenceStack::ApplySetting_Implementation(const UGMS_AnimLayerSetting* Setting)
{
check(IsValid(Setting));
if (CurrentSetting != Setting || CurrentOverlayMode != GetParent()->OverlayMode)
{
ResetSetting();
}
if (const UGMS_AnimLayerSetting_Overlay_SequenceStack* DS = Cast<UGMS_AnimLayerSetting_Overlay_SequenceStack>(Setting))
{
if (CurrentSetting == DS && CurrentOverlayMode == GetParent()->OverlayMode)
{
return;
}
if (!DS->AcceleratedOverlayModes.Contains(GetParent()->OverlayMode))
{
return;
}
const FGMS_OverlayModeSetting_SequenceStack& ModeSetting = DS->AcceleratedOverlayModes[GetParent()->OverlayMode];
CurrentSetting = DS;
CurrentOverlayMode = GetParent()->OverlayMode;
bHasValidSetting = true;
}
}
void UGMS_AnimLayer_Overlay_SequenceStack::ResetSetting_Implementation()
{
Definition = FGMS_SequenceStackEntry();
BlendWeight = 0.0f;
BlendProfile = nullptr;
bHasValidSetting = false;
CurrentOverlayMode = FGameplayTag::EmptyTag;
CurrentSetting = nullptr;
}
bool UGMS_AnimLayer_Overlay_SequenceStack::SelectSequence()
{
const FGMS_OverlayModeSetting_SequenceStack& OS = GetOverlayModeSetting();
for (int32 i = 0; i < OS.Sequences.Num(); i++)
{
const FGMS_SequenceStackEntry& Entry = OS.Sequences[i];
bool bMatchesOwnedTags = Entry.TagQuery.IsEmpty() || Entry.TagQuery.Matches(GetParent()->OwnedTags);
bool bMatchesRelevanceTags = Entry.RelevanceQuery.IsEmpty() || Entry.RelevanceQuery.Matches(GetParent()->NodeRelevanceTags);
if (bMatchesOwnedTags && bMatchesRelevanceTags)
{
Definition = Entry;
BlendOutSpeed = Definition.BlendOutSpeed;
BlendProfile = GetParent()->GetNamedBlendProfile(Definition.BlendProfile);
bHasValidDefinition = true;
return true;
}
}
return false;
}
void UGMS_AnimLayer_Overlay_SequenceStack::NativeThreadSafeUpdateAnimation(float DeltaSeconds)
{
Super::NativeThreadSafeUpdateAnimation(DeltaSeconds);
if (!bHasValidSetting)
{
bHasValidDefinition = false;
return;
}
if (!SelectSequence())
{
bHasValidDefinition = false;
}
if (bHasValidDefinition)
{
if (Definition.BlendInSpeed > 0)
{
BlendWeight = FMath::FInterpTo(BlendWeight, Definition.BlendWeight, DeltaSeconds, Definition.BlendInSpeed);
}
else
{
BlendWeight = Definition.BlendWeight;
}
}
else
{
if (BlendOutSpeed > 0)
{
BlendWeight = FMath::FInterpTo(BlendWeight, 0.0f, DeltaSeconds, BlendOutSpeed);
}
else
{
BlendWeight = 0.0f;
}
}
}
const FGMS_OverlayModeSetting_SequenceStack& UGMS_AnimLayer_Overlay_SequenceStack::GetOverlayModeSetting() const
{
return CurrentSetting->AcceleratedOverlayModes[CurrentOverlayMode];
}

View File

@@ -0,0 +1,6 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "Locomotions/GMS_AnimLayer_SkeletalControls.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_AnimLayer_SkeletalControls)

View File

@@ -0,0 +1,12 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "Locomotions/GMS_AnimLayer_States.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_AnimLayer_States)
void FGMS_AnimData::Validate()
{
}

View File

@@ -0,0 +1,6 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "Locomotions/GMS_AnimLayer_View.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_AnimLayer_View)

View File

@@ -0,0 +1,29 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "Locomotions/GMS_AnimLayer_View_Default.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_AnimLayer_View_Default)
void UGMS_AnimLayer_View_Default::ApplySetting_Implementation(const UGMS_AnimLayerSetting* Setting)
{
if (const UGMS_AnimLayerSetting_View_Default* DS = Cast<UGMS_AnimLayerSetting_View_Default>(Setting))
{
ResetSetting();
BlendSpace = DS->BlendSpace;
YawAngleOffset = DS->YawAngleOffset;
YawAngleLimit = DS->YawAngleLimit;
SmoothInterpSpeed = DS->SmoothInterpSpeed;
bValidBlendSpace = BlendSpace != nullptr;
}
}
void UGMS_AnimLayer_View_Default::ResetSetting_Implementation()
{
bValidBlendSpace = false;
YawAngleOffset = 0.0f;
YawAngleLimit = FVector2D(-90.0f, 90.0f);
SmoothInterpSpeed = 0.0f;
BlendSpace = nullptr;
}

View File

@@ -0,0 +1,73 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "Locomotions/GMS_AnimState.h"
#include "AnimationWarpingLibrary.h"
#include "Animation/AnimSequenceBase.h"
#include "Animation/AnimSequence.h"
#include "Utility/GMS_Constants.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_AnimState)
void FGMS_AnimState_Layering::ApplyValueFromSequence(const UAnimSequence* InSequence, float ExplicitTime)
{
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerHead", ExplicitTime, HeadBlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerHeadAdditive", ExplicitTime, HeadAdditiveBlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerHeadSlot", ExplicitTime, HeadSlotBlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerArmLeft", ExplicitTime, ArmLeftBlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerArmLeftAdditive", ExplicitTime, ArmLeftAdditiveBlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerArmLeftSlot", ExplicitTime, ArmLeftSlotBlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerArmLeftLocalSpace", ExplicitTime, ArmLeftLocalSpaceBlendAmount);
ArmLeftMeshSpaceBlendAmount = 1 - ArmLeftLocalSpaceBlendAmount;
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerArmRight", ExplicitTime, ArmRightBlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerArmRightAdditive", ExplicitTime, ArmRightAdditiveBlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerArmRightSlot", ExplicitTime, ArmRightSlotBlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerArmRightLocalSpace", ExplicitTime, ArmRightLocalSpaceBlendAmount);
ArmRightMeshSpaceBlendAmount = 1 - ArmRightLocalSpaceBlendAmount;
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerHandLeft", ExplicitTime, HandLeftBlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerHandRight", ExplicitTime, HandRightBlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerSpine", ExplicitTime, SpineBlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerSpineAdditive", ExplicitTime, SpineAdditiveBlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerSpineSlot", ExplicitTime, SpineSlotBlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerPelvis", ExplicitTime, PelvisBlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerPelvisSlot", ExplicitTime, PelvisSlotBlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerLegs", ExplicitTime, LegsBlendAmount);
UAnimationWarpingLibrary::GetCurveValueFromAnimation(InSequence, "LayerLegsSlot", ExplicitTime, LegsSlotBlendAmount);
}
void FGMS_AnimState_Layering::ZeroOut()
{
HeadBlendAmount = 0.0f;
HeadAdditiveBlendAmount = 0.0f;
HeadSlotBlendAmount = 0.0f;
ArmLeftBlendAmount = 0.0f;
ArmLeftAdditiveBlendAmount = 0.0f;
ArmLeftSlotBlendAmount = 0.0f;
ArmLeftLocalSpaceBlendAmount = 0.0f;
ArmLeftMeshSpaceBlendAmount = 0.0f;
ArmRightBlendAmount = 0.0f;
ArmRightAdditiveBlendAmount = 0.0f;
ArmRightSlotBlendAmount = 0.0f;
ArmRightLocalSpaceBlendAmount = 0.0f;
ArmRightMeshSpaceBlendAmount = 0.0f;
HandLeftBlendAmount = 0.0f;
HandRightBlendAmount = 0.0f;
SpineBlendAmount = 0.0f;
SpineAdditiveBlendAmount = 0.0f;
SpineSlotBlendAmount = 0.0f;
PelvisBlendAmount = 0.0f;
PelvisSlotBlendAmount = 0.0f;
LegsBlendAmount = 0.0f;
LegsSlotBlendAmount = 0.0f;
}

View File

@@ -0,0 +1,40 @@
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
#include "Locomotions/GMS_LocomotionStructLibrary.h"
#include "Animation/AimOffsetBlendSpace.h"
#include "Animation/AnimSequenceBase.h"
#include "Animation/AnimSequence.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_LocomotionStructLibrary)
bool FGMS_Animations_4Direction::ValidAnimations() const
{
return Forward && Backward && Left && Right;
}
bool FGMS_Animations_4Direction::HasRootMotion() const
{
if (ValidAnimations())
{
return Forward->HasRootMotion() && Backward->HasRootMotion() && Left->HasRootMotion() && Right->HasRootMotion();
}
return false;
}
bool FGMS_Animations_8Direction::ValidAnimations() const
{
return Forward && ForwardLeft && ForwardRight && Backward && BackwardLeft && BackwardRight && Left && Right;
}
bool FGMS_Animations_8Direction::HasRootMotion() const
{
if (ValidAnimations())
{
return Forward->HasRootMotion() && ForwardLeft->HasRootMotion() && ForwardRight->HasRootMotion() && Backward->HasRootMotion() && BackwardLeft->HasRootMotion() && BackwardRight->
HasRootMotion() && Left->
HasRootMotion() && Right->HasRootMotion();
}
return false;
}

View File

@@ -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);
}