第一次提交
This commit is contained in:
@@ -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>();
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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];
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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()
|
||||
{
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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