第一次提交
This commit is contained in:
18
Plugins/GMS/Config/BaseGenericMovementSystem.ini
Normal file
18
Plugins/GMS/Config/BaseGenericMovementSystem.ini
Normal file
@@ -0,0 +1,18 @@
|
||||
[CoreRedirects]
|
||||
;GMS 1.4 -> 1.5 refactoring.
|
||||
+ClassRedirects = (OldName="/Script/GenericMovementSystem.GMS_AnimLayer_Overlay_PoseBased",NewName="/Script/GenericMovementSystem.GMS_AnimLayer_Overlay_PoseStack")
|
||||
+ClassRedirects = (OldName="/Script/GenericMovementSystem.GMS_AnimLayerSetting_Overlay_PoseBased",NewName="/Script/GenericMovementSystem.GMS_AnimLayerSetting_Overlay_PoseStack")
|
||||
+StructRedirects = (OldName="/Script/GenericMovementSystem.GMS_PoseOverlaySetting",NewName="/Script/GenericMovementSystem.GMS_OverlayModeSetting_PoseStack")
|
||||
+ClassRedirects = (OldName="/Script/GenericMovementSystem.GMS_AnimLayerSetting_Overlay_Stack",NewName="/Script/GenericMovementSystem.GMS_AnimLayerSetting_Overlay_ParallelSequenceStack")
|
||||
+ClassRedirects = (OldName="/Script/GenericMovementSystem.GMS_AnimLayer_Overlay_Stack",NewName="/Script/GenericMovementSystem.GMS_AnimLayer_Overlay_ParallelSequenceStack")
|
||||
+StructRedirects = (OldName="/Script/GenericMovementSystem.GMS_StackedOverlaySetting",NewName="/Script/GenericMovementSystem.GMS_OverlayModeSetting_ParallelSequenceStack")
|
||||
+StructRedirects = (OldName="/Script/GenericMovementSystem.GMS_AnimData_StackedOverlays",NewName="/Script/GenericMovementSystem.GMS_ParallelSequenceStack")
|
||||
+StructRedirects = (OldName="/Script/GenericMovementSystem.GMS_AnimData_Overlay",NewName="/Script/GenericMovementSystem.GMS_ParallelSequenceStackEntry")
|
||||
+PropertyRedirects = (OldName="/Script/GenericMovementSystem.GMS_OverlayModeSetting_ParallelSequenceStack.StackedOverlays",NewName="/Script/GenericMovementSystem.GMS_OverlayModeSetting_ParallelSequenceStack.Stacks")
|
||||
+StructRedirects = (OldName="/Script/GenericMovementSystem.GMS_OverlayStackState",NewName="/Script/GenericMovementSystem.GMS_ParallelSequenceStackState")
|
||||
+ClassRedirects = (OldName="/Game/GenericGame/MovementSystem/Core/AnimLayers/ABPT_GMS_Layer_Overlay_PoseBased.ABPT_GMS_Layer_Overlay_PoseBased_C",NewName="/Game/GenericGame/MovementSystem/Core/AnimLayers/ABPT_GMS_Layer_Overlay_PoseStack.ABPT_GMS_Layer_Overlay_PoseStack_C")
|
||||
+ClassRedirects = (OldName="/Game/GenericGame/MovementSystem/Core/AnimLayers/ABPT_GMS_Layer_Overlay_StackBased.ABPT_GMS_Layer_Overlay_StackBased_C",NewName="/Game/GenericGame/MovementSystem/Core/AnimLayers/ABPT_GMS_Layer_Overlay_ParallelSequenceStack.ABPT_GMS_Layer_Overlay_ParallelSequenceStack_C")
|
||||
|
||||
+StructRedirects = (OldName="/Script/GenericMovementSystem.GMS_AnimData_OverlayEntry_Pose",NewName="/Script/GenericMovementSystem.GMS_PoseStackEntry")
|
||||
|
||||
|
||||
9
Plugins/GMS/Config/FilterPlugin.ini
Normal file
9
Plugins/GMS/Config/FilterPlugin.ini
Normal file
@@ -0,0 +1,9 @@
|
||||
[FilterPlugin]
|
||||
; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and
|
||||
; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively.
|
||||
;
|
||||
; Examples:
|
||||
; /README.txt
|
||||
; /Extras/...
|
||||
; /Binaries/ThirdParty/*.dll
|
||||
/Config/*
|
||||
66
Plugins/GMS/GenericMovementSystem.uplugin
Normal file
66
Plugins/GMS/GenericMovementSystem.uplugin
Normal file
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"FileVersion": 3,
|
||||
"Version": 8,
|
||||
"VersionName": "1.5.1",
|
||||
"FriendlyName": "GenericMovementSystem",
|
||||
"Description": "Powerful and advanced movement control and locomotion(animation)system designed for enhanced flexibility, seamless integration, user-friendly experience, and effortless expandability.",
|
||||
"Category": "Gameplay",
|
||||
"CreatedBy": "YuewuDev",
|
||||
"CreatedByURL": "https://yuewu.dev",
|
||||
"DocsURL": "https://www.yuewu.dev/en/wiki",
|
||||
"MarketplaceURL": "com.epicgames.launcher://ue/Fab/product/c4eca691-0c0c-45fc-ada3-7e9af9beae71",
|
||||
"SupportURL": "https://discord.com/invite/xMRXAB2",
|
||||
"EngineVersion": "5.7.0",
|
||||
"CanContainContent": false,
|
||||
"Installed": true,
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "GenericMovementSystem",
|
||||
"Type": "Runtime",
|
||||
"LoadingPhase": "Default",
|
||||
"PlatformAllowList": [
|
||||
"Win64",
|
||||
"Android",
|
||||
"Linux"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "GenericMovementEditor",
|
||||
"Type": "UncookedOnly",
|
||||
"LoadingPhase": "PreDefault",
|
||||
"PlatformAllowList": [
|
||||
"Win64"
|
||||
]
|
||||
}
|
||||
],
|
||||
"Plugins": [
|
||||
{
|
||||
"Name": "Niagara",
|
||||
"Enabled": true
|
||||
},
|
||||
{
|
||||
"Name": "ModularGameplay",
|
||||
"Enabled": true
|
||||
},
|
||||
{
|
||||
"Name": "AnimationWarping",
|
||||
"Enabled": true
|
||||
},
|
||||
{
|
||||
"Name": "AnimationLocomotionLibrary",
|
||||
"Enabled": true
|
||||
},
|
||||
{
|
||||
"Name": "Mover",
|
||||
"Enabled": true
|
||||
},
|
||||
{
|
||||
"Name": "PoseSearch",
|
||||
"Enabled": true
|
||||
},
|
||||
{
|
||||
"Name": "Chooser",
|
||||
"Enabled": true
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
Plugins/GMS/Resources/Icon128.png
Normal file
BIN
Plugins/GMS/Resources/Icon128.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,33 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class GenericMovementEditor : ModuleRules
|
||||
{
|
||||
public GenericMovementEditor(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
CppCompileWarningSettings.NonInlinedGenCppWarningLevel = WarningLevel.Warning;
|
||||
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core", "CoreUObject", "Engine", "AnimGraphRuntime", "AnimationModifiers", "AnimationBlueprintLibrary",
|
||||
"SlateCore",
|
||||
"GenericMovementSystem",
|
||||
}
|
||||
);
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(new[]
|
||||
{
|
||||
"UnrealEd",
|
||||
"AnimGraph",
|
||||
"BlueprintGraph",
|
||||
"ToolMenus",
|
||||
"Blutility",
|
||||
"AssetTools",
|
||||
"GameplayTags",
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Blutility/GMS_EditorUtilityLibrary.h"
|
||||
#include "AnimationModifier.h"
|
||||
#include "AnimationModifiersAssetUserData.h"
|
||||
#include "EditorUtilityLibrary.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_EditorUtilityLibrary)
|
||||
|
||||
void UGMS_EditorUtilityLibrary::RevertAnimModifierOfClass(TSubclassOf<UAnimationModifier> ModifierClass)
|
||||
{
|
||||
if (!IsValid(ModifierClass))
|
||||
{
|
||||
return;
|
||||
}
|
||||
TArray<UObject*> Anims = UEditorUtilityLibrary::GetSelectedAssetsOfClass(UAnimSequence::StaticClass());
|
||||
|
||||
for (int32 i = 0; i < Anims.Num(); i++)
|
||||
{
|
||||
if (UAnimSequence* AnimSequence = Cast<UAnimSequence>(Anims[i]))
|
||||
{
|
||||
if (UAnimationModifiersAssetUserData* UserData = AnimSequence->GetAssetUserData<UAnimationModifiersAssetUserData>())
|
||||
{
|
||||
const TArray<UAnimationModifier*>& Modifiers = UserData->GetAnimationModifierInstances().FilterByPredicate([&](const UAnimationModifier* Instance)
|
||||
{
|
||||
return Instance && Instance->GetClass()->IsChildOf(ModifierClass);
|
||||
});
|
||||
|
||||
for (const UAnimationModifier* Modifier : Modifiers)
|
||||
{
|
||||
if (Modifier)
|
||||
{
|
||||
Modifier->RevertFromAnimationSequence(AnimSequence);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float UGMS_EditorUtilityLibrary::GetSamplingFrameRate(const UAnimSequence* AnimSequence)
|
||||
{
|
||||
if (AnimSequence)
|
||||
{
|
||||
return AnimSequence->GetSamplingFrameRate().AsDecimal();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
TArray<FName> UGMS_EditorUtilityLibrary::GetAllCurveNames(const UAnimSequence* AnimSequence)
|
||||
{
|
||||
TArray<FName> Names;
|
||||
if (IsValid(AnimSequence))
|
||||
{
|
||||
for (const FFloatCurve& FloatCurve : AnimSequence->GetCurveData().FloatCurves)
|
||||
{
|
||||
Names.Add(FloatCurve.GetName());
|
||||
}
|
||||
}
|
||||
|
||||
return Names;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Factories/GMS_AssetTypeActions.h"
|
||||
#include "GenericMovementEditor.h"
|
||||
#include "Locomotions/GMS_AnimLayer_Overlay_PoseStack.h"
|
||||
#include "Locomotions/GMS_AnimLayer_Overlay_SequenceStack.h"
|
||||
#include "Locomotions/GMS_AnimLayer_States_DefaultLocomotion.h"
|
||||
#include "Settings/GMS_SettingObjectLibrary.h"
|
||||
|
||||
|
||||
uint32 FGMS_AssetTypeAction::GetCategories()
|
||||
{
|
||||
return FGenericMovementEditorModule::GetAssetsCategory();
|
||||
}
|
||||
|
||||
FColor FGMS_AssetTypeAction::GetTypeColor() const
|
||||
{
|
||||
return FColor(114, 40, 199);
|
||||
}
|
||||
|
||||
#define IMPLEMENT_GMS_ASSET_ACTION(ActionName, NameText, DescText) \
|
||||
FText FGMS_AssetTypeAction_##ActionName::GetName() const \
|
||||
{ \
|
||||
return NSLOCTEXT("AssetTypeActions", "AssetTypeActions_" #ActionName "_Name", NameText); \
|
||||
} \
|
||||
FText FGMS_AssetTypeAction_##ActionName::GetAssetDescription(const FAssetData& AssetData) const \
|
||||
{ \
|
||||
return NSLOCTEXT("AssetTypeActions", "AssetTypeActions_" #ActionName "_Description", DescText); \
|
||||
} \
|
||||
UClass* FGMS_AssetTypeAction_##ActionName::GetSupportedClass() const \
|
||||
{ \
|
||||
return UGMS_##ActionName::StaticClass(); \
|
||||
}
|
||||
|
||||
IMPLEMENT_GMS_ASSET_ACTION(MovementDefinition, "Movement Definition",
|
||||
"Data Asset that defines multiple movement set settings together.")
|
||||
IMPLEMENT_GMS_ASSET_ACTION(MovementControlSetting_Default, "Movement Control Setting",
|
||||
"Data Asset that defines movement control settings")
|
||||
IMPLEMENT_GMS_ASSET_ACTION(AnimGraphSetting, "Anim Graph Setting",
|
||||
"Data Asset that stores animation graph-specific settings, one per unique skeleton.")
|
||||
IMPLEMENT_GMS_ASSET_ACTION(AnimLayerSetting_States_Default, "States Anim Layer Setting(Basic Locomotion)",
|
||||
"Data Asset that stores basic locomotion(default states animation layer settings.)")
|
||||
IMPLEMENT_GMS_ASSET_ACTION(AnimLayerSetting_Overlay_PoseStack, "Overlay Anim Layer Setting(Pose Stack)",
|
||||
"Data Asset that stores pose stack overlay settings")
|
||||
IMPLEMENT_GMS_ASSET_ACTION(AnimLayerSetting_Overlay_SequenceStack, "Overlay Anim Layer Setting(Sequence Stack)",
|
||||
"Data Asset that stores sequence stack overlay settings")
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Factories/GMS_DataAssetsFactories.h"
|
||||
|
||||
#include "Locomotions/GMS_AnimLayer_Overlay_PoseStack.h"
|
||||
#include "Locomotions/GMS_AnimLayer_Overlay_SequenceStack.h"
|
||||
#include "Locomotions/GMS_AnimLayer_States_DefaultLocomotion.h"
|
||||
#include "Settings/GMS_SettingObjectLibrary.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_DataAssetsFactories)
|
||||
|
||||
|
||||
UGMS_Factory::UGMS_Factory(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
|
||||
{
|
||||
bEditAfterNew = true;
|
||||
bCreateNew = true;
|
||||
}
|
||||
|
||||
uint32 UGMS_Factory::GetMenuCategories() const
|
||||
{
|
||||
return Super::GetMenuCategories();
|
||||
}
|
||||
|
||||
const TArray<FText>& UGMS_Factory::GetMenuCategorySubMenus() const
|
||||
{
|
||||
return Super::GetMenuCategorySubMenus();
|
||||
}
|
||||
|
||||
#define IMPLEMENT_GMS_FACTORY(FactoryName) \
|
||||
UGMS_Factory_##FactoryName::UGMS_Factory_##FactoryName(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) \
|
||||
{ \
|
||||
SupportedClass = UGMS_##FactoryName::StaticClass(); \
|
||||
} \
|
||||
UObject* UGMS_Factory_##FactoryName::FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) \
|
||||
{ \
|
||||
check(Class->IsChildOf(UGMS_##FactoryName::StaticClass())); \
|
||||
return NewObject<UGMS_##FactoryName>(InParent, Class, Name, Flags | RF_Transactional, Context); \
|
||||
}
|
||||
|
||||
IMPLEMENT_GMS_FACTORY(MovementDefinition)
|
||||
IMPLEMENT_GMS_FACTORY(MovementControlSetting_Default)
|
||||
IMPLEMENT_GMS_FACTORY(AnimGraphSetting)
|
||||
IMPLEMENT_GMS_FACTORY(AnimLayerSetting_States_Default)
|
||||
IMPLEMENT_GMS_FACTORY(AnimLayerSetting_Overlay_PoseStack)
|
||||
IMPLEMENT_GMS_FACTORY(AnimLayerSetting_Overlay_SequenceStack)
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#include "GenericMovementEditor.h"
|
||||
#include "AssetToolsModule.h"
|
||||
#include "Factories/GMS_AssetTypeActions.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FGenericMovementEditorModule"
|
||||
|
||||
TArray<TSharedPtr<IAssetTypeActions>> FGenericMovementEditorModule::AssetTypeActions = {
|
||||
MakeShared<FGMS_AssetTypeAction_MovementDefinition>(),
|
||||
MakeShared<FGMS_AssetTypeAction_AnimGraphSetting>(),
|
||||
MakeShared<FGMS_AssetTypeAction_MovementControlSetting_Default>(),
|
||||
MakeShared<FGMS_AssetTypeAction_AnimLayerSetting_States_Default>(),
|
||||
MakeShared<FGMS_AssetTypeAction_AnimLayerSetting_Overlay_PoseStack>(),
|
||||
MakeShared<FGMS_AssetTypeAction_AnimLayerSetting_Overlay_SequenceStack>()
|
||||
};
|
||||
|
||||
EAssetTypeCategories::Type FGenericMovementEditorModule::AssetsCategory;
|
||||
|
||||
|
||||
void FGenericMovementEditorModule::StartupModule()
|
||||
{
|
||||
IAssetTools& AssetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get();
|
||||
AssetsCategory = AssetTools.RegisterAdvancedAssetCategory(FName(TEXT("GenericMovementSystem")), LOCTEXT("GMS_AssetsCategory", "Generic Movement System"));
|
||||
for (TSharedPtr<IAssetTypeActions>& Action : AssetTypeActions)
|
||||
{
|
||||
AssetTools.RegisterAssetTypeActions(Action.ToSharedRef());
|
||||
}
|
||||
}
|
||||
|
||||
void FGenericMovementEditorModule::ShutdownModule()
|
||||
{
|
||||
if (const FAssetToolsModule* AssetTools = FModuleManager::GetModulePtr<FAssetToolsModule>("AssetTools"))
|
||||
{
|
||||
for (TSharedPtr<IAssetTypeActions>& Action : AssetTypeActions)
|
||||
{
|
||||
AssetTools->Get().UnregisterAssetTypeActions(Action.ToSharedRef());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(FGenericMovementEditorModule, GenericMovementEditor)
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "AssetTypeCategories.h"
|
||||
#include "IAssetTools.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
class FGenericMovementEditorModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
|
||||
static EAssetTypeCategories::Type GetAssetsCategory()
|
||||
{
|
||||
return AssetsCategory;
|
||||
}
|
||||
|
||||
private:
|
||||
static TArray<TSharedPtr<IAssetTypeActions>> AssetTypeActions;
|
||||
static EAssetTypeCategories::Type AssetsCategory;
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Nodes/GMS_AnimGraphNode_CurvesBlend.h"
|
||||
|
||||
|
||||
#define LOCTEXT_NAMESPACE "GMS_CurvesBlendAnimationGraphNode"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_AnimGraphNode_CurvesBlend)
|
||||
|
||||
FText UGMS_AnimGraphNode_CurvesBlend::GetNodeTitle(const ENodeTitleType::Type TitleType) const
|
||||
{
|
||||
return LOCTEXT("Title", "Blend Curves");
|
||||
}
|
||||
|
||||
FText UGMS_AnimGraphNode_CurvesBlend::GetTooltipText() const
|
||||
{
|
||||
return LOCTEXT("Tooltip", "Blend Curves");
|
||||
}
|
||||
|
||||
FString UGMS_AnimGraphNode_CurvesBlend::GetNodeCategory() const
|
||||
{
|
||||
return FString{TEXTVIEW("GMS|Blends")};
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,106 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Nodes/GMS_AnimGraphNode_GameplayTagsBlend.h"
|
||||
|
||||
#include "GameplayTagsManager.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_AnimGraphNode_GameplayTagsBlend)
|
||||
|
||||
#define LOCTEXT_NAMESPACE "GMS_AnimGraphNode_GameplayTagsBlend"
|
||||
|
||||
UGMS_AnimGraphNode_GameplayTagsBlend::UGMS_AnimGraphNode_GameplayTagsBlend()
|
||||
{
|
||||
Node.AddPose();
|
||||
}
|
||||
|
||||
void UGMS_AnimGraphNode_GameplayTagsBlend::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
|
||||
{
|
||||
if (PropertyChangedEvent.GetPropertyName() == GET_MEMBER_NAME_CHECKED(FGMS_AnimNode_GameplayTagsBlend, Tags))
|
||||
{
|
||||
ReconstructNode();
|
||||
}
|
||||
|
||||
Super::PostEditChangeProperty(PropertyChangedEvent);
|
||||
}
|
||||
|
||||
FText UGMS_AnimGraphNode_GameplayTagsBlend::GetNodeTitle(const ENodeTitleType::Type TitleType) const
|
||||
{
|
||||
return LOCTEXT("Title", "Blend Poses by Gameplay Tag");
|
||||
}
|
||||
|
||||
FText UGMS_AnimGraphNode_GameplayTagsBlend::GetTooltipText() const
|
||||
{
|
||||
return LOCTEXT("Tooltip", "Blend Poses by Gameplay Tag");
|
||||
}
|
||||
|
||||
void UGMS_AnimGraphNode_GameplayTagsBlend::ReallocatePinsDuringReconstruction(TArray<UEdGraphPin*>& PreviousPins)
|
||||
{
|
||||
Node.RefreshPoses();
|
||||
|
||||
Super::ReallocatePinsDuringReconstruction(PreviousPins);
|
||||
}
|
||||
|
||||
FString UGMS_AnimGraphNode_GameplayTagsBlend::GetNodeCategory() const
|
||||
{
|
||||
return FString{TEXTVIEW("GMS|Blends")};
|
||||
}
|
||||
|
||||
FName GetSimpleTagName(const FGameplayTag& Tag)
|
||||
{
|
||||
const auto TagNode{UGameplayTagsManager::Get().FindTagNode(Tag)};
|
||||
|
||||
return TagNode.IsValid() ? TagNode->GetSimpleTagName() : NAME_None;
|
||||
}
|
||||
|
||||
void UGMS_AnimGraphNode_GameplayTagsBlend::CustomizePinData(UEdGraphPin* Pin, const FName SourcePropertyName, const int32 PinIndex) const
|
||||
{
|
||||
Super::CustomizePinData(Pin, SourcePropertyName, PinIndex);
|
||||
|
||||
bool bBlendPosePin;
|
||||
bool bBlendTimePin;
|
||||
GetBlendPinProperties(Pin, bBlendPosePin, bBlendTimePin);
|
||||
|
||||
if (!bBlendPosePin && !bBlendTimePin)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Pin->PinFriendlyName = PinIndex <= 0
|
||||
? LOCTEXT("Default", "Default")
|
||||
: PinIndex > Node.Tags.Num()
|
||||
? LOCTEXT("Invalid", "Invalid")
|
||||
: FText::AsCultureInvariant(GetSimpleTagName(Node.Tags[PinIndex - 1]).ToString());
|
||||
|
||||
if (bBlendPosePin)
|
||||
{
|
||||
static const FTextFormat BlendPosePinFormat{LOCTEXT("Pose", "{PinName} Pose")};
|
||||
|
||||
Pin->PinFriendlyName = FText::Format(BlendPosePinFormat, {{FString{TEXTVIEW("PinName")}, Pin->PinFriendlyName}});
|
||||
}
|
||||
else if (bBlendTimePin)
|
||||
{
|
||||
static const FTextFormat BlendTimePinFormat{LOCTEXT("BlendTime", "{PinName} Blend Time")};
|
||||
|
||||
Pin->PinFriendlyName = FText::Format(BlendTimePinFormat, {{FString{TEXTVIEW("PinName")}, Pin->PinFriendlyName}});
|
||||
}
|
||||
}
|
||||
|
||||
void UGMS_AnimGraphNode_GameplayTagsBlend::GetBlendPinProperties(const UEdGraphPin* Pin, bool& bBlendPosePin, bool& bBlendTimePin)
|
||||
{
|
||||
const auto PinFullName{Pin->PinName.ToString()};
|
||||
const auto SeparatorIndex{PinFullName.Find(TEXT("_"), ESearchCase::CaseSensitive)};
|
||||
|
||||
if (SeparatorIndex <= 0)
|
||||
{
|
||||
bBlendPosePin = false;
|
||||
bBlendTimePin = false;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto PinName{PinFullName.Left(SeparatorIndex)};
|
||||
bBlendPosePin = PinName == TEXTVIEW("BlendPose");
|
||||
bBlendTimePin = PinName == TEXTVIEW("BlendTime");
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,111 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#include "Nodes/GMS_AnimGraphNode_LayeredBoneBlend.h"
|
||||
#include "ToolMenus.h"
|
||||
#include "Kismet2/BlueprintEditorUtils.h"
|
||||
|
||||
#include "AnimGraphCommands.h"
|
||||
#include "ScopedTransaction.h"
|
||||
|
||||
#include "Kismet2/CompilerResultsLog.h"
|
||||
#include "UObject/UE5ReleaseStreamObjectVersion.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_AnimGraphNode_LayeredBoneBlend)
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
// UGMS_AnimGraphNode_LayeredBoneBlend
|
||||
|
||||
#define LOCTEXT_NAMESPACE "A3Nodes"
|
||||
|
||||
UGMS_AnimGraphNode_LayeredBoneBlend::UGMS_AnimGraphNode_LayeredBoneBlend(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
Node.AddPose();
|
||||
}
|
||||
|
||||
FLinearColor UGMS_AnimGraphNode_LayeredBoneBlend::GetNodeTitleColor() const
|
||||
{
|
||||
return FLinearColor(0.2f, 0.8f, 0.3f);
|
||||
}
|
||||
|
||||
FText UGMS_AnimGraphNode_LayeredBoneBlend::GetTooltipText() const
|
||||
{
|
||||
return LOCTEXT("AnimGraphNode_LayeredBoneBlend_Tooltip", "Generic Layered blend per bone");
|
||||
}
|
||||
|
||||
FText UGMS_AnimGraphNode_LayeredBoneBlend::GetNodeTitle(ENodeTitleType::Type TitleType) const
|
||||
{
|
||||
return LOCTEXT("AnimGraphNode_LayeredBoneBlend_Title", "Generic Layered blend per bone");
|
||||
}
|
||||
|
||||
FString UGMS_AnimGraphNode_LayeredBoneBlend::GetNodeCategory() const
|
||||
{
|
||||
return TEXT("Animation|Blends");
|
||||
}
|
||||
|
||||
void UGMS_AnimGraphNode_LayeredBoneBlend::AddPinToBlendByFilter()
|
||||
{
|
||||
FScopedTransaction Transaction( LOCTEXT("AddPinToBlend", "AddPinToBlendByFilter") );
|
||||
Modify();
|
||||
|
||||
Node.AddPose();
|
||||
ReconstructNode();
|
||||
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(GetBlueprint());
|
||||
}
|
||||
|
||||
void UGMS_AnimGraphNode_LayeredBoneBlend::RemovePinFromBlendByFilter(UEdGraphPin* Pin)
|
||||
{
|
||||
FScopedTransaction Transaction( LOCTEXT("RemovePinFromBlend", "RemovePinFromBlendByFilter") );
|
||||
Modify();
|
||||
|
||||
FProperty* AssociatedProperty;
|
||||
int32 ArrayIndex;
|
||||
GetPinAssociatedProperty(GetFNodeType(), Pin, /*out*/ AssociatedProperty, /*out*/ ArrayIndex);
|
||||
|
||||
if (ArrayIndex != INDEX_NONE)
|
||||
{
|
||||
//@TODO: ANIMREFACTOR: Need to handle moving pins below up correctly
|
||||
// setting up removed pins info
|
||||
RemovedPinArrayIndex = ArrayIndex;
|
||||
Node.RemovePose(ArrayIndex);
|
||||
ReconstructNode();
|
||||
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(GetBlueprint());
|
||||
}
|
||||
}
|
||||
|
||||
void UGMS_AnimGraphNode_LayeredBoneBlend::GetNodeContextMenuActions(UToolMenu* Menu, UGraphNodeContextMenuContext* Context) const
|
||||
{
|
||||
if (!Context->bIsDebugging)
|
||||
{
|
||||
{
|
||||
FToolMenuSection& Section = Menu->AddSection("AnimGraphNodeLayeredBoneblend", LOCTEXT("LayeredBoneBlend", "Generic Layered Bone Blend"));
|
||||
if (Context->Pin != NULL)
|
||||
{
|
||||
// we only do this for normal BlendList/BlendList by enum, BlendList by Bool doesn't support add/remove pins
|
||||
if (Context->Pin->Direction == EGPD_Input)
|
||||
{
|
||||
//@TODO: Only offer this option on arrayed pins
|
||||
Section.AddMenuEntry(FAnimGraphCommands::Get().RemoveBlendListPin);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Section.AddMenuEntry(FAnimGraphCommands::Get().AddBlendListPin);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGMS_AnimGraphNode_LayeredBoneBlend::ValidateAnimNodeDuringCompilation(class USkeleton* ForSkeleton, class FCompilerResultsLog& MessageLog)
|
||||
{
|
||||
UAnimGraphNode_Base::ValidateAnimNodeDuringCompilation(ForSkeleton, MessageLog);
|
||||
|
||||
// ensure to cache the per-bone blend weights
|
||||
if (!Node.ArePerBoneBlendWeightsValid(ForSkeleton))
|
||||
{
|
||||
Node.RebuildPerBoneBlendWeights(ForSkeleton);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,287 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#include "Nodes/GMS_AnimGraphNode_OrientationWarping.h"
|
||||
#include "Animation/AnimRootMotionProvider.h"
|
||||
#include "Kismet2/BlueprintEditorUtils.h"
|
||||
#include "Kismet2/CompilerResultsLog.h"
|
||||
#include "DetailCategoryBuilder.h"
|
||||
#include "DetailLayoutBuilder.h"
|
||||
#include "PropertyHandle.h"
|
||||
#include "ScopedTransaction.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "GMS_AnimGraphNode_OrientationWarping"
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_AnimGraphNode_OrientationWarping)
|
||||
|
||||
UGMS_AnimGraphNode_OrientationWarping::UGMS_AnimGraphNode_OrientationWarping(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
|
||||
{
|
||||
}
|
||||
|
||||
FText UGMS_AnimGraphNode_OrientationWarping::GetControllerDescription() const
|
||||
{
|
||||
return LOCTEXT("OrientationWarping", "Generic Orientation Warping");
|
||||
}
|
||||
|
||||
FText UGMS_AnimGraphNode_OrientationWarping::GetTooltipText() const
|
||||
{
|
||||
return LOCTEXT("OrientationWarpingTooltip", "Rotates the root and lower body by the specified angle, while counter rotating the upper body to maintain the forward facing direction.");
|
||||
}
|
||||
|
||||
FText UGMS_AnimGraphNode_OrientationWarping::GetNodeTitle(ENodeTitleType::Type TitleType) const
|
||||
{
|
||||
return GetControllerDescription();
|
||||
}
|
||||
|
||||
FLinearColor UGMS_AnimGraphNode_OrientationWarping::GetNodeTitleColor() const
|
||||
{
|
||||
return FLinearColor(FColor(0.f, 255.f, 0.f));
|
||||
}
|
||||
|
||||
FString UGMS_AnimGraphNode_OrientationWarping::GetNodeCategory() const
|
||||
{
|
||||
return TEXT("GMS|Animation Warping");
|
||||
}
|
||||
|
||||
void UGMS_AnimGraphNode_OrientationWarping::CustomizePinData(UEdGraphPin* Pin, FName SourcePropertyName, int32 ArrayIndex) const
|
||||
{
|
||||
Super::CustomizePinData(Pin, SourcePropertyName, ArrayIndex);
|
||||
|
||||
return; //直接返回,编辑器模式下不根据配置情况动态隐藏/显示属性
|
||||
|
||||
// if (Pin->PinName == GET_MEMBER_NAME_STRING_CHECKED(FGMS_AnimNode_OrientationWarping, OrientationAngle))
|
||||
// {
|
||||
// Pin->bHidden = (Node.Mode == EWarpingEvaluationMode::Graph);
|
||||
// }
|
||||
//
|
||||
// if (Pin->PinName == GET_MEMBER_NAME_STRING_CHECKED(FGMS_AnimNode_OrientationWarping, LocomotionAngle))
|
||||
// {
|
||||
// Pin->bHidden = (Node.Mode == EWarpingEvaluationMode::Manual);
|
||||
// }
|
||||
//
|
||||
// if (Pin->PinName == GET_MEMBER_NAME_STRING_CHECKED(FGMS_AnimNode_OrientationWarping, LocomotionAngleDeltaThreshold))
|
||||
// {
|
||||
// Pin->bHidden = (Node.Mode == EWarpingEvaluationMode::Manual);
|
||||
// }
|
||||
}
|
||||
|
||||
void UGMS_AnimGraphNode_OrientationWarping::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
|
||||
{
|
||||
DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC()
|
||||
Super::CustomizeDetails(DetailBuilder);
|
||||
|
||||
return; //直接返回,编辑器模式下不根据配置情况动态隐藏/显示属性
|
||||
|
||||
// DetailBuilder.SortCategories([](const TMap<FName, IDetailCategoryBuilder*>& CategoryMap)
|
||||
// {
|
||||
// for (const TPair<FName, IDetailCategoryBuilder*>& Pair : CategoryMap)
|
||||
// {
|
||||
// int32 SortOrder = Pair.Value->GetSortOrder();
|
||||
// const FName CategoryName = Pair.Key;
|
||||
//
|
||||
// if (CategoryName == "Evaluation")
|
||||
// {
|
||||
// SortOrder += 1;
|
||||
// }
|
||||
// else if (CategoryName == "Settings")
|
||||
// {
|
||||
// SortOrder += 2;
|
||||
// }
|
||||
// else if (CategoryName == "Debug")
|
||||
// {
|
||||
// SortOrder += 3;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// const int32 ValueSortOrder = Pair.Value->GetSortOrder();
|
||||
// if (ValueSortOrder >= SortOrder && ValueSortOrder < SortOrder + 10)
|
||||
// {
|
||||
// SortOrder += 10;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Pair.Value->SetSortOrder(SortOrder);
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// TSharedRef<IPropertyHandle> NodeHandle = DetailBuilder.GetProperty(FName(TEXT("Node")), GetClass());
|
||||
//
|
||||
// if (Node.Mode == EWarpingEvaluationMode::Graph)
|
||||
// {
|
||||
// DetailBuilder.HideProperty(NodeHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FGMS_AnimNode_OrientationWarping, OrientationAngle)));
|
||||
// }
|
||||
//
|
||||
// if (Node.Mode == EWarpingEvaluationMode::Manual)
|
||||
// {
|
||||
// DetailBuilder.HideProperty(NodeHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FGMS_AnimNode_OrientationWarping, LocomotionAngle)));
|
||||
// DetailBuilder.HideProperty(NodeHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FGMS_AnimNode_OrientationWarping, LocomotionAngleDeltaThreshold)));
|
||||
// DetailBuilder.HideProperty(NodeHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FGMS_AnimNode_OrientationWarping, MinRootMotionSpeedThreshold)));
|
||||
// }
|
||||
}
|
||||
|
||||
void UGMS_AnimGraphNode_OrientationWarping::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent)
|
||||
{
|
||||
DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC()
|
||||
Super::PostEditChangeProperty(PropertyChangedEvent);
|
||||
|
||||
return; //直接返回,编辑器模式下不根据配置情况动态隐藏/显示属性
|
||||
|
||||
// bool bRequiresNodeReconstruct = false;
|
||||
// FProperty* ChangedProperty = PropertyChangedEvent.Property;
|
||||
//
|
||||
// if (ChangedProperty)
|
||||
// {
|
||||
// // Evaluation mode
|
||||
// if (ChangedProperty->GetFName() == GET_MEMBER_NAME_STRING_CHECKED(FGMS_AnimNode_OrientationWarping, Mode))
|
||||
// {
|
||||
// FScopedTransaction Transaction(LOCTEXT("ChangeEvaluationMode", "Change Evaluation Mode"));
|
||||
// Modify();
|
||||
//
|
||||
// // Break links to pins going away
|
||||
// for (int32 PinIndex = 0; PinIndex < Pins.Num(); ++PinIndex)
|
||||
// {
|
||||
// UEdGraphPin* Pin = Pins[PinIndex];
|
||||
// if (Pin->PinName == GET_MEMBER_NAME_STRING_CHECKED(FGMS_AnimNode_OrientationWarping, OrientationAngle))
|
||||
// {
|
||||
// if (Node.Mode == EWarpingEvaluationMode::Graph)
|
||||
// {
|
||||
// Pin->BreakAllPinLinks();
|
||||
// }
|
||||
// }
|
||||
// else if (Pin->PinName == GET_MEMBER_NAME_STRING_CHECKED(FGMS_AnimNode_OrientationWarping, LocomotionAngle))
|
||||
// {
|
||||
// if (Node.Mode == EWarpingEvaluationMode::Manual)
|
||||
// {
|
||||
// Pin->BreakAllPinLinks();
|
||||
// }
|
||||
// }
|
||||
// else if (Pin->PinName == GET_MEMBER_NAME_STRING_CHECKED(FGMS_AnimNode_OrientationWarping, LocomotionAngleDeltaThreshold))
|
||||
// {
|
||||
// if (Node.Mode == EWarpingEvaluationMode::Manual)
|
||||
// {
|
||||
// Pin->BreakAllPinLinks();
|
||||
// }
|
||||
// }
|
||||
// else if (Pin->PinName == GET_MEMBER_NAME_STRING_CHECKED(FGMS_AnimNode_OrientationWarping, MinRootMotionSpeedThreshold))
|
||||
// {
|
||||
// if (Node.Mode == EWarpingEvaluationMode::Manual)
|
||||
// {
|
||||
// Pin->BreakAllPinLinks();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// bRequiresNodeReconstruct = true;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (bRequiresNodeReconstruct)
|
||||
// {
|
||||
// ReconstructNode();
|
||||
// FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(GetBlueprint());
|
||||
// }
|
||||
}
|
||||
|
||||
void UGMS_AnimGraphNode_OrientationWarping::GetInputLinkAttributes(FNodeAttributeArray& OutAttributes) const
|
||||
{
|
||||
if (Node.Mode == EWarpingEvaluationMode::Graph)
|
||||
{
|
||||
OutAttributes.Add(UE::Anim::IAnimRootMotionProvider::AttributeName);
|
||||
}
|
||||
}
|
||||
|
||||
void UGMS_AnimGraphNode_OrientationWarping::GetOutputLinkAttributes(FNodeAttributeArray& OutAttributes) const
|
||||
{
|
||||
if (Node.Mode == EWarpingEvaluationMode::Graph)
|
||||
{
|
||||
OutAttributes.Add(UE::Anim::IAnimRootMotionProvider::AttributeName);
|
||||
}
|
||||
}
|
||||
|
||||
void UGMS_AnimGraphNode_OrientationWarping::ValidateAnimNodeDuringCompilation(USkeleton* ForSkeleton, FCompilerResultsLog& MessageLog)
|
||||
{
|
||||
return Super::ValidateAnimNodeDuringCompilation(ForSkeleton, MessageLog); //直接返回,编辑器模式下不做任何校验。
|
||||
// auto HasInvalidBoneName = [](const FName& BoneName)
|
||||
// {
|
||||
// return BoneName == NAME_None;
|
||||
// };
|
||||
//
|
||||
// auto HasInvalidBoneIndex = [&](const FName& BoneName)
|
||||
// {
|
||||
// return ForSkeleton && ForSkeleton->GetReferenceSkeleton().FindBoneIndex(BoneName) == INDEX_NONE;
|
||||
// };
|
||||
//
|
||||
// auto InvalidBoneNameMessage = [&](const FName& BoneName)
|
||||
// {
|
||||
// FFormatNamedArguments Args;
|
||||
// Args.Add(TEXT("BoneName"), FText::FromName(BoneName));
|
||||
// const FText Message = FText::Format(NSLOCTEXT("OrientationWarping", "Invalid{BoneName}BoneName", "@@ - {BoneName} bone not found in Skeleton"), Args);
|
||||
// MessageLog.Warning(*Message.ToString(), this);
|
||||
// };
|
||||
//
|
||||
// auto InvalidBoneIndexMessage = [&](const FName& BoneName)
|
||||
// {
|
||||
// FFormatNamedArguments Args;
|
||||
// Args.Add(TEXT("BoneName"), FText::FromName(BoneName));
|
||||
// const FText Message = FText::Format(NSLOCTEXT("OrientationWarping", "Invalid{BoneName}BoneInSkeleton", "@@ - {BoneName} bone definition is required"), Args);
|
||||
// MessageLog.Warning(*Message.ToString(), this);
|
||||
// };
|
||||
//
|
||||
// if (Node.RotationAxis == EAxis::None)
|
||||
// {
|
||||
// MessageLog.Warning(*NSLOCTEXT("OrientationWarping", "InvalidRotationAxis", "@@ - Rotation Axis choice of X, Y, or Z is required").ToString(), this);
|
||||
// }
|
||||
//
|
||||
// if (Node.SpineBones.IsEmpty())
|
||||
// {
|
||||
// MessageLog.Warning(*NSLOCTEXT("OrientationWarping", "InvalidSpineBones", "@@ - Spine bone definitions are required").ToString(), this);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// for (const auto& Bone : Node.SpineBones)
|
||||
// {
|
||||
// if (HasInvalidBoneName(Bone.BoneName))
|
||||
// {
|
||||
// InvalidBoneIndexMessage("Spine");
|
||||
// }
|
||||
// else if (HasInvalidBoneIndex(Bone.BoneName))
|
||||
// {
|
||||
// InvalidBoneNameMessage(Bone.BoneName);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (HasInvalidBoneName(Node.IKFootRootBone.BoneName))
|
||||
// {
|
||||
// InvalidBoneIndexMessage("IK Foot Root");
|
||||
// }
|
||||
// else if (HasInvalidBoneIndex(Node.IKFootRootBone.BoneName))
|
||||
// {
|
||||
// InvalidBoneNameMessage(Node.IKFootRootBone.BoneName);
|
||||
// }
|
||||
//
|
||||
// if (Node.SpineBones.IsEmpty())
|
||||
// {
|
||||
// MessageLog.Warning(*NSLOCTEXT("OrientationWarping", "InvalidIKFootBones", "@@ - IK Foot bone definitions are required").ToString(), this);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// for (const auto& Bone : Node.IKFootBones)
|
||||
// {
|
||||
// if (HasInvalidBoneName(Bone.BoneName))
|
||||
// {
|
||||
// InvalidBoneIndexMessage("IK Foot");
|
||||
// }
|
||||
// else if (HasInvalidBoneIndex(Bone.BoneName))
|
||||
// {
|
||||
// InvalidBoneNameMessage(Bone.BoneName);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Super::ValidateAnimNodeDuringCompilation(ForSkeleton, MessageLog);
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "GMS_EditorUtilityLibrary.generated.h"
|
||||
|
||||
class UAnimationModifier;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS(Blueprintable)
|
||||
class GENERICMOVEMENTEDITOR_API UGMS_EditorUtilityLibrary : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
#if WITH_EDITOR
|
||||
UFUNCTION(BlueprintCallable, Category="GMS|Development|Editor")
|
||||
static void RevertAnimModifierOfClass(TSubclassOf<UAnimationModifier> ModifierClass);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="GMS|Development|Editor")
|
||||
static float GetSamplingFrameRate(const UAnimSequence* AnimSequence);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="GMS|Development|Editor")
|
||||
static TArray<FName> GetAllCurveNames(const UAnimSequence* AnimSequence);
|
||||
#endif
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "AssetTypeActions/AssetTypeActions_DataAsset.h"
|
||||
|
||||
class FGMS_AssetTypeAction : public FAssetTypeActions_DataAsset
|
||||
{
|
||||
public:
|
||||
virtual uint32 GetCategories() override;
|
||||
virtual FColor GetTypeColor() const override;
|
||||
};
|
||||
|
||||
#define DEFINE_GMS_ASSET_ACTION(ActionName) \
|
||||
class FGMS_AssetTypeAction_##ActionName final : public FGMS_AssetTypeAction \
|
||||
{ \
|
||||
public: \
|
||||
virtual FText GetName() const override; \
|
||||
virtual FText GetAssetDescription(const FAssetData& AssetData) const override; \
|
||||
virtual UClass* GetSupportedClass() const override; \
|
||||
};
|
||||
|
||||
DEFINE_GMS_ASSET_ACTION(MovementDefinition)
|
||||
|
||||
DEFINE_GMS_ASSET_ACTION(MovementControlSetting_Default)
|
||||
|
||||
DEFINE_GMS_ASSET_ACTION(AnimGraphSetting)
|
||||
|
||||
DEFINE_GMS_ASSET_ACTION(AnimLayerSetting_States_Default)
|
||||
|
||||
DEFINE_GMS_ASSET_ACTION(AnimLayerSetting_Overlay_PoseStack)
|
||||
|
||||
DEFINE_GMS_ASSET_ACTION(AnimLayerSetting_Overlay_SequenceStack)
|
||||
@@ -0,0 +1,89 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "UObject/ObjectMacros.h"
|
||||
#include "Templates/SubclassOf.h"
|
||||
#include "Factories/Factory.h"
|
||||
#include "GMS_DataAssetsFactories.generated.h"
|
||||
|
||||
UCLASS(Abstract)
|
||||
class UGMS_Factory : public UFactory
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UGMS_Factory(const FObjectInitializer& ObjectInitializer);
|
||||
virtual uint32 GetMenuCategories() const override;
|
||||
virtual const TArray<FText>& GetMenuCategorySubMenus() const override;
|
||||
};
|
||||
|
||||
#define DEFINE_GMS_FACTORY(FactoryName) \
|
||||
UCLASS() \
|
||||
class UGMS_Factory_##FactoryName : public UGMS_Factory \
|
||||
{ \
|
||||
GENERATED_BODY() \
|
||||
public: \
|
||||
UGMS_Factory_##FactoryName(const FObjectInitializer& ObjectInitializer); \
|
||||
virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override; \
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class UGMS_Factory_MovementDefinition : public UGMS_Factory
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UGMS_Factory_MovementDefinition(const FObjectInitializer& ObjectInitializer);
|
||||
virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override;
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class UGMS_Factory_MovementControlSetting_Default : public UGMS_Factory
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UGMS_Factory_MovementControlSetting_Default(const FObjectInitializer& ObjectInitializer);
|
||||
virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override;
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class UGMS_Factory_AnimGraphSetting : public UGMS_Factory
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UGMS_Factory_AnimGraphSetting(const FObjectInitializer& ObjectInitializer);
|
||||
virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override;
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class UGMS_Factory_AnimLayerSetting_States_Default : public UGMS_Factory
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UGMS_Factory_AnimLayerSetting_States_Default(const FObjectInitializer& ObjectInitializer);
|
||||
virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override;
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class UGMS_Factory_AnimLayerSetting_Overlay_PoseStack : public UGMS_Factory
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UGMS_Factory_AnimLayerSetting_Overlay_PoseStack(const FObjectInitializer& ObjectInitializer);
|
||||
virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override;
|
||||
};
|
||||
|
||||
UCLASS()
|
||||
class UGMS_Factory_AnimLayerSetting_Overlay_SequenceStack : public UGMS_Factory
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UGMS_Factory_AnimLayerSetting_Overlay_SequenceStack(const FObjectInitializer& ObjectInitializer);
|
||||
virtual UObject* FactoryCreateNew(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override;
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "AnimGraphNode_Base.h"
|
||||
#include "Nodes/GMS_AnimNode_CurvesBlend.h"
|
||||
#include "UObject/Object.h"
|
||||
#include "GMS_AnimGraphNode_CurvesBlend.generated.h"
|
||||
|
||||
|
||||
UCLASS()
|
||||
class GENERICMOVEMENTEDITOR_API UGMS_AnimGraphNode_CurvesBlend : public UAnimGraphNode_Base
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Settings")
|
||||
FGMS_AnimNode_CurvesBlend Node;
|
||||
|
||||
public:
|
||||
virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
|
||||
|
||||
virtual FText GetTooltipText() const override;
|
||||
|
||||
virtual FString GetNodeCategory() const override;
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "AnimGraphNode_BlendListBase.h"
|
||||
#include "Nodes/GMS_AnimNode_GameplayTagsBlend.h"
|
||||
#include "GMS_AnimGraphNode_GameplayTagsBlend.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class GENERICMOVEMENTEDITOR_API UGMS_AnimGraphNode_GameplayTagsBlend : public UAnimGraphNode_BlendListBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Settings")
|
||||
FGMS_AnimNode_GameplayTagsBlend Node;
|
||||
|
||||
public:
|
||||
UGMS_AnimGraphNode_GameplayTagsBlend();
|
||||
|
||||
virtual void PostEditChangeProperty(FPropertyChangedEvent &PropertyChangedEvent) override;
|
||||
|
||||
virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
|
||||
|
||||
virtual FText GetTooltipText() const override;
|
||||
|
||||
virtual void ReallocatePinsDuringReconstruction(TArray<UEdGraphPin *> &PreviousPins) override;
|
||||
|
||||
virtual FString GetNodeCategory() const override;
|
||||
|
||||
virtual void CustomizePinData(UEdGraphPin *Pin, FName SourcePropertyName, int32 PinIndex) const override;
|
||||
|
||||
protected:
|
||||
static void GetBlendPinProperties(const UEdGraphPin *Pin, bool &bBlendPosePin, bool &bBlendTimePin);
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/ObjectMacros.h"
|
||||
#include "AnimGraphNode_BlendListBase.h"
|
||||
#include "Nodes/GMS_AnimNode_LayeredBoneBlend.h"
|
||||
#include "GMS_AnimGraphNode_LayeredBoneBlend.generated.h"
|
||||
|
||||
UCLASS(MinimalAPI)
|
||||
class UGMS_AnimGraphNode_LayeredBoneBlend : public UAnimGraphNode_BlendListBase
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
UPROPERTY(EditAnywhere, Category=Settings)
|
||||
FGMS_AnimNode_LayeredBoneBlend Node;
|
||||
|
||||
// Adds a new pose pin
|
||||
//@TODO: Generalize this behavior (returning a list of actions/delegates maybe?)
|
||||
GENERICMOVEMENTEDITOR_API virtual void AddPinToBlendByFilter();
|
||||
GENERICMOVEMENTEDITOR_API virtual void RemovePinFromBlendByFilter(UEdGraphPin* Pin);
|
||||
|
||||
// UObject interface
|
||||
// End of UObject interface
|
||||
|
||||
// UEdGraphNode interface
|
||||
virtual FLinearColor GetNodeTitleColor() const override;
|
||||
virtual FText GetTooltipText() const override;
|
||||
virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
|
||||
// End of UEdGraphNode interface
|
||||
|
||||
// UK2Node interface
|
||||
virtual void GetNodeContextMenuActions(class UToolMenu* Menu, class UGraphNodeContextMenuContext* Context) const override;
|
||||
// End of UK2Node interface
|
||||
|
||||
// UAnimGraphNode_Base interface
|
||||
virtual FString GetNodeCategory() const override;
|
||||
// End of UAnimGraphNode_Base interface
|
||||
|
||||
|
||||
// Gives each visual node a chance to validate that they are still valid in the context of the compiled class, giving a last shot at error or warning generation after primary compilation is finished
|
||||
virtual void ValidateAnimNodeDuringCompilation(class USkeleton* ForSkeleton, class FCompilerResultsLog& MessageLog) override;
|
||||
// End of UAnimGraphNode_Base interface
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
#include "AnimGraphNode_SkeletalControlBase.h"
|
||||
#include "Nodes/GMS_AnimNode_OrientationWarping.h"
|
||||
#include "GMS_AnimGraphNode_OrientationWarping.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class UGMS_AnimGraphNode_OrientationWarping : public UAnimGraphNode_SkeletalControlBase
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="Settings")
|
||||
FGMS_AnimNode_OrientationWarping Node;
|
||||
|
||||
public:
|
||||
// UEdGraphNode interface
|
||||
virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
|
||||
virtual FLinearColor GetNodeTitleColor() const override;
|
||||
virtual FText GetTooltipText() const override;
|
||||
virtual FString GetNodeCategory() const override;
|
||||
virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
|
||||
virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override;
|
||||
virtual void GetInputLinkAttributes(FNodeAttributeArray& OutAttributes) const override;
|
||||
virtual void GetOutputLinkAttributes(FNodeAttributeArray& OutAttributes) const override;
|
||||
virtual void ValidateAnimNodeDuringCompilation(USkeleton* ForSkeleton, FCompilerResultsLog& MessageLog) override;
|
||||
// End of UEdGraphNode interface
|
||||
|
||||
protected:
|
||||
// UAnimGraphNode_Base interface
|
||||
virtual void CustomizePinData(UEdGraphPin* Pin, FName SourcePropertyName, int32 ArrayIndex) const override;
|
||||
// End of UAnimGraphNode_Base interface
|
||||
|
||||
// UAnimGraphNode_SkeletalControlBase interface
|
||||
virtual FText GetControllerDescription() const override;
|
||||
virtual const FAnimNode_SkeletalControlBase* GetNode() const override { return &Node; }
|
||||
// End of UAnimGraphNode_SkeletalControlBase interface
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class GenericMovementSystem : ModuleRules
|
||||
{
|
||||
public GenericMovementSystem(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
CppCompileWarningSettings.NonInlinedGenCppWarningLevel = WarningLevel.Warning;
|
||||
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new[]
|
||||
{
|
||||
"GameplayTags",
|
||||
"AnimationWarpingRuntime",
|
||||
"Chooser", "PoseSearch", "Mover"
|
||||
// ... add other public dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new[]
|
||||
{
|
||||
"Core",
|
||||
"CoreUObject",
|
||||
"NetCore",
|
||||
"ModularGameplay",
|
||||
"EngineSettings",
|
||||
"Engine",
|
||||
"AnimGraphRuntime",
|
||||
"BlendStack",
|
||||
"AnimationLocomotionLibraryRuntime",
|
||||
"Niagara",
|
||||
"DeveloperSettings"
|
||||
// ... add private dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
if (Target.Type == TargetRules.TargetType.Editor) PrivateDependencyModuleNames.AddRange(new[] { "MessageLog" });
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,898 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#include "GMS_MovementSystemComponent.h"
|
||||
#include "GameFramework/Pawn.h"
|
||||
#include "Engine/World.h"
|
||||
#include "TimerManager.h"
|
||||
#include "GameplayTagAssetInterface.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "GameFramework/Pawn.h"
|
||||
#include "Animation/AnimInstance.h"
|
||||
#include "Misc/DataValidation.h"
|
||||
#include "Net/Core/PushModel/PushModel.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
#include "Settings/GMS_SettingObjectLibrary.h"
|
||||
#include "Utility/GMS_Log.h"
|
||||
#include "Utility/GMS_Math.h"
|
||||
#include "Utility/GMS_Vector.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_MovementSystemComponent)
|
||||
|
||||
UGMS_MovementSystemComponent::UGMS_MovementSystemComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
|
||||
{
|
||||
PrimaryComponentTick.bCanEverTick = true;
|
||||
bWantsInitializeComponent = true;
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::PostLoad()
|
||||
{
|
||||
Super::PostLoad();
|
||||
#if WITH_EDITOR
|
||||
PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
||||
if (!MovementDefinitions.IsEmpty())
|
||||
{
|
||||
MovementDefinition = MovementDefinitions.Last();
|
||||
MovementDefinitions.Empty();
|
||||
}
|
||||
PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
||||
#endif
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::InitializeComponent()
|
||||
{
|
||||
Super::InitializeComponent();
|
||||
|
||||
OwnerPawn = Cast<APawn>(GetOwner());
|
||||
|
||||
check(OwnerPawn)
|
||||
|
||||
if (OwnerPawn)
|
||||
{
|
||||
// Set some default values here to ensure that the animation instance and the
|
||||
// camera component can read the most up-to-date values during their initialization.
|
||||
SetReplicatedViewRotation(OwnerPawn->GetViewRotation().GetNormalized(), false);
|
||||
|
||||
ViewState.Rotation = ReplicatedViewRotation;
|
||||
ViewState.PreviousYawAngle = UE_REAL_TO_FLOAT(OwnerPawn->GetViewRotation().Yaw);
|
||||
|
||||
const auto YawAngle{UE_REAL_TO_FLOAT(OwnerPawn->GetActorRotation().Yaw)};
|
||||
|
||||
LocomotionState.InputYawAngle = YawAngle;
|
||||
LocomotionState.VelocityYawAngle = YawAngle;
|
||||
}
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
if (GetOwner()->GetClass()->ImplementsInterface(UGameplayTagAssetInterface::StaticClass()))
|
||||
{
|
||||
SetGameplayTagsProvider(GetOwner());
|
||||
}
|
||||
else
|
||||
{
|
||||
TArray<UActorComponent*> Components = GetOwner()->GetComponentsByInterface(UGameplayTagAssetInterface::StaticClass());
|
||||
if (Components.IsValidIndex(0))
|
||||
{
|
||||
SetGameplayTagsProvider(Components[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
|
||||
FDoRepLifetimeParams Parameters;
|
||||
Parameters.bIsPushBased = true;
|
||||
|
||||
Parameters.Condition = COND_SkipOwner;
|
||||
|
||||
DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, LocomotionMode, Parameters)
|
||||
DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, OverlayMode, Parameters)
|
||||
DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, MovementSet, Parameters)
|
||||
DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, MovementDefinition, Parameters)
|
||||
|
||||
DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, ReplicatedViewRotation, Parameters)
|
||||
DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, DesiredVelocityYawAngle, Parameters)
|
||||
DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, OwnedTags, Parameters)
|
||||
}
|
||||
|
||||
UGMS_MovementSystemComponent* UGMS_MovementSystemComponent::GetMovementSystemComponent(const AActor* Actor)
|
||||
{
|
||||
return Actor != nullptr ? Actor->FindComponentByClass<UGMS_MovementSystemComponent>() : nullptr;
|
||||
}
|
||||
|
||||
bool UGMS_MovementSystemComponent::K2_FindMovementComponent(const AActor* Actor, UGMS_MovementSystemComponent*& Instance)
|
||||
{
|
||||
if (Actor == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Instance = GetMovementSystemComponent(Actor);
|
||||
return Instance != nullptr;
|
||||
}
|
||||
|
||||
bool UGMS_MovementSystemComponent::K2_FindMovementComponentExt(const AActor* Actor, TSubclassOf<UGMS_MovementSystemComponent> DesiredClass, UGMS_MovementSystemComponent*& Instance)
|
||||
{
|
||||
if (DesiredClass)
|
||||
{
|
||||
Instance = GetMovementSystemComponent(Actor);
|
||||
return Instance != nullptr && Instance->GetClass()->IsChildOf(DesiredClass);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
FGameplayTagContainer UGMS_MovementSystemComponent::GetGameplayTags() const
|
||||
{
|
||||
if (IGameplayTagAssetInterface* TagAssetInterface = Cast<IGameplayTagAssetInterface>(GameplayTagsProvider))
|
||||
{
|
||||
FGameplayTagContainer RetTags;
|
||||
TagAssetInterface->GetOwnedGameplayTags(RetTags);
|
||||
|
||||
if (!OwnedTags.IsEmpty())
|
||||
{
|
||||
RetTags.AppendTags(OwnedTags);
|
||||
}
|
||||
|
||||
RetTags.AddTagFast(GetMovementState());
|
||||
RetTags.AddTagFast(GetRotationMode());
|
||||
|
||||
return RetTags;
|
||||
}
|
||||
return OwnedTags;
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::SetGameplayTagsProvider(UObject* Provider)
|
||||
{
|
||||
if (!IsValid(Provider))
|
||||
{
|
||||
GMS_CLOG(Warning, "Passed invalid GameplayTagsProvider.");
|
||||
return;
|
||||
}
|
||||
if (IGameplayTagAssetInterface* TagAssetInterface = Cast<IGameplayTagAssetInterface>(Provider))
|
||||
{
|
||||
GameplayTagsProvider = Provider;
|
||||
}
|
||||
else
|
||||
{
|
||||
GMS_CLOG(Warning, "Passed in GameplayTagsProvider(%s) Doesn't implement GameplayTagAssetInterface, it can't provide gameplay tags.", *Provider->GetName());
|
||||
}
|
||||
}
|
||||
#pragma region GameplayTags
|
||||
void UGMS_MovementSystemComponent::AddGameplayTag(FGameplayTag TagToAdd)
|
||||
{
|
||||
AddGameplayTag(TagToAdd, true);
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::RemoveGameplay(FGameplayTag TagToRemove)
|
||||
{
|
||||
RemoveGameplayTag(TagToRemove, true);
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::SetGameplayTags(FGameplayTagContainer TagsToSet)
|
||||
{
|
||||
SetGameplayTags(TagsToSet, true);
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::AddGameplayTag(const FGameplayTag& TagToAdd, bool bSendRpc)
|
||||
{
|
||||
if (GetOwner()->GetLocalRole() <= ROLE_SimulatedProxy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OwnedTags.AddTag(TagToAdd);
|
||||
|
||||
MARK_PROPERTY_DIRTY_FROM_NAME(ThisClass, OwnedTags, this)
|
||||
|
||||
if (bSendRpc)
|
||||
{
|
||||
if (GetOwner()->GetLocalRole() >= ROLE_Authority)
|
||||
{
|
||||
ClientAddGameplayTag(TagToAdd);
|
||||
}
|
||||
else
|
||||
{
|
||||
ServerAddGameplayTag(TagToAdd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::RemoveGameplayTag(const FGameplayTag& TagToRemove, bool bSendRpc)
|
||||
{
|
||||
if (GetOwner()->GetLocalRole() <= ROLE_SimulatedProxy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OwnedTags.RemoveTag(TagToRemove);
|
||||
|
||||
MARK_PROPERTY_DIRTY_FROM_NAME(ThisClass, OwnedTags, this)
|
||||
|
||||
if (bSendRpc)
|
||||
{
|
||||
if (GetOwner()->GetLocalRole() >= ROLE_Authority)
|
||||
{
|
||||
ClientRemoveGameplayTag(TagToRemove);
|
||||
}
|
||||
else
|
||||
{
|
||||
ServerRemoveGameplayTag(TagToRemove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::SetGameplayTags(const FGameplayTagContainer& TagsToSet, bool bSendRpc)
|
||||
{
|
||||
if (GetOwner()->GetLocalRole() <= ROLE_SimulatedProxy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OwnedTags = TagsToSet;
|
||||
|
||||
MARK_PROPERTY_DIRTY_FROM_NAME(ThisClass, OwnedTags, this)
|
||||
|
||||
if (bSendRpc)
|
||||
{
|
||||
if (GetOwner()->GetLocalRole() >= ROLE_Authority)
|
||||
{
|
||||
ClientSetGameplayTags(TagsToSet);
|
||||
}
|
||||
else
|
||||
{
|
||||
ServerSetGameplayTags(TagsToSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::ClientAddGameplayTag_Implementation(const FGameplayTag& TagToAdd)
|
||||
{
|
||||
AddGameplayTag(TagToAdd, false);
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::ServerAddGameplayTag_Implementation(const FGameplayTag& TagToAdd)
|
||||
{
|
||||
AddGameplayTag(TagToAdd, false);
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::ClientRemoveGameplayTag_Implementation(const FGameplayTag& TagToRemove)
|
||||
{
|
||||
RemoveGameplayTag(TagToRemove, false);
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::ServerRemoveGameplayTag_Implementation(const FGameplayTag& TagToRemove)
|
||||
{
|
||||
RemoveGameplayTag(TagToRemove, false);
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::ClientSetGameplayTags_Implementation(const FGameplayTagContainer& TagsToSet)
|
||||
{
|
||||
SetGameplayTags(TagsToSet, false);
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::ServerSetGameplayTags_Implementation(const FGameplayTagContainer& TagsToSet)
|
||||
{
|
||||
SetGameplayTags(TagsToSet, false);
|
||||
}
|
||||
#pragma endregion GameplayTags
|
||||
|
||||
#pragma region Locomotion
|
||||
|
||||
const FGMS_LocomotionState& UGMS_MovementSystemComponent::GetLocomotionState() const
|
||||
{
|
||||
return LocomotionState;
|
||||
}
|
||||
|
||||
TScriptInterface<IPoseSearchTrajectoryPredictorInterface> UGMS_MovementSystemComponent::GetTrajectoryPredictor() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool UGMS_MovementSystemComponent::IsCrouching() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
float UGMS_MovementSystemComponent::GetMaxSpeed() const
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
float UGMS_MovementSystemComponent::GetMaxAcceleration() const
|
||||
{
|
||||
return 1000.0f;
|
||||
}
|
||||
|
||||
bool UGMS_MovementSystemComponent::IsMovingOnGround() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::SetDesiredVelocityYawAngle(float NewDesiredVelocityYawAngle)
|
||||
{
|
||||
COMPARE_ASSIGN_AND_MARK_PROPERTY_DIRTY(ThisClass, DesiredVelocityYawAngle, NewDesiredVelocityYawAngle, this);
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::ServerSetDesiredVelocityYawAngle_Implementation(float NewDesiredVelocityYawAngle)
|
||||
{
|
||||
SetDesiredVelocityYawAngle(NewDesiredVelocityYawAngle);
|
||||
}
|
||||
|
||||
|
||||
const FGameplayTag& UGMS_MovementSystemComponent::GetLocomotionMode() const
|
||||
{
|
||||
return LocomotionMode;
|
||||
}
|
||||
|
||||
const FGMS_MovementBaseState& UGMS_MovementSystemComponent::GetMovementBase() const
|
||||
{
|
||||
return MovementBase;
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::SetLocomotionMode(const FGameplayTag& NewLocomotionMode)
|
||||
{
|
||||
if (LocomotionMode != NewLocomotionMode)
|
||||
{
|
||||
const auto PreviousLocomotionMode{LocomotionMode};
|
||||
|
||||
LocomotionMode = NewLocomotionMode;
|
||||
|
||||
MARK_PROPERTY_DIRTY_FROM_NAME(ThisClass, LocomotionMode, this)
|
||||
|
||||
OnLocomotionModeChanged(PreviousLocomotionMode);
|
||||
}
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::OnReplicated_LocomotionMode(const FGameplayTag& PreviousLocomotionMode)
|
||||
{
|
||||
OnLocomotionModeChanged(PreviousLocomotionMode);
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::OnLocomotionModeChanged_Implementation(const FGameplayTag& PreviousLocomotionMode)
|
||||
{
|
||||
GMS_CLOG(Verbose, "locomotion mode changed from %s to %s", *PreviousLocomotionMode.ToString(), *LocomotionMode.ToString());
|
||||
OnLocomotionModeChangedEvent.Broadcast(PreviousLocomotionMode);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
void UGMS_MovementSystemComponent::RefreshMovementBase()
|
||||
{
|
||||
}
|
||||
|
||||
#pragma region MovementSet
|
||||
|
||||
const FGameplayTag& UGMS_MovementSystemComponent::GetMovementSet() const
|
||||
{
|
||||
return MovementSet;
|
||||
}
|
||||
|
||||
const FGMS_MovementSetSetting& UGMS_MovementSystemComponent::GetMovementSetSetting() const
|
||||
{
|
||||
return MovementSetSetting;
|
||||
}
|
||||
|
||||
const FGMS_MovementStateSetting& UGMS_MovementSystemComponent::GetMovementStateSetting() const
|
||||
{
|
||||
return MovementStateSetting;
|
||||
}
|
||||
|
||||
const UGMS_MovementControlSetting_Default* UGMS_MovementSystemComponent::GetControlSetting() const
|
||||
{
|
||||
return ControlSetting;
|
||||
}
|
||||
|
||||
int32 UGMS_MovementSystemComponent::GetNumOfMovementStateSettings() const
|
||||
{
|
||||
return ControlSetting->MovementStates.Num();
|
||||
}
|
||||
|
||||
TSoftObjectPtr<const UGMS_MovementDefinition> UGMS_MovementSystemComponent::GetMovementDefinition() const
|
||||
{
|
||||
return MovementDefinition;
|
||||
}
|
||||
|
||||
TSoftObjectPtr<const UGMS_MovementDefinition> UGMS_MovementSystemComponent::GetPrevMovementDefinition() const
|
||||
{
|
||||
return PrevMovementDefinition;
|
||||
}
|
||||
|
||||
const UGMS_MovementDefinition* UGMS_MovementSystemComponent::GetLoadedMovementDefinition() const
|
||||
{
|
||||
return MovementDefinition.IsValid() ? MovementDefinition.Get() : nullptr;
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::SetMovementSet(const FGameplayTag& NewMovementSet)
|
||||
{
|
||||
SetMovementSet(NewMovementSet, true);
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::SetMovementDefinition(TSoftObjectPtr<UGMS_MovementDefinition> NewDefinition)
|
||||
{
|
||||
if (NewDefinition.IsNull())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (NewDefinition != MovementDefinition)
|
||||
{
|
||||
auto LoadedDefinition = NewDefinition.LoadSynchronous();
|
||||
|
||||
if (LoadedDefinition->MovementSets.Contains(MovementSet))
|
||||
{
|
||||
InternalSetMovementDefinition(NewDefinition, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::LocalSetMovementDefinition(TSoftObjectPtr<UGMS_MovementDefinition> NewDefinition)
|
||||
{
|
||||
InternalSetMovementDefinition(NewDefinition, false);
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::PushAvailableMovementDefinition(TSoftObjectPtr<UGMS_MovementDefinition> NewDefinition, bool bPopCurrent)
|
||||
{
|
||||
InternalSetMovementDefinition(NewDefinition, true);
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::PopAvailableMovementDefinition()
|
||||
{
|
||||
// PopMovementDefinition(true);
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::InternalSetMovementDefinition(const TSoftObjectPtr<UGMS_MovementDefinition> NewDefinition, bool bSendRpc)
|
||||
{
|
||||
if (!NewDefinition.IsNull() && NewDefinition != MovementDefinition)
|
||||
{
|
||||
PrevMovementDefinition = MovementDefinition;
|
||||
MovementDefinition = NewDefinition;
|
||||
MARK_PROPERTY_DIRTY_FROM_NAME(ThisClass, MovementDefinition, this)
|
||||
|
||||
OnMovementSetChanged(MovementSet);
|
||||
|
||||
if (bSendRpc)
|
||||
{
|
||||
if (GetOwner()->GetLocalRole() >= ROLE_Authority)
|
||||
{
|
||||
ClientSetMovementDefinition(NewDefinition);
|
||||
}
|
||||
else
|
||||
{
|
||||
ServerSetMovementDefinition(NewDefinition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::ClientSetMovementDefinition_Implementation(const TSoftObjectPtr<UGMS_MovementDefinition>& NewDefinition)
|
||||
{
|
||||
InternalSetMovementDefinition(NewDefinition, false);
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::ServerSetMovementDefinition_Implementation(const TSoftObjectPtr<UGMS_MovementDefinition>& NewDefinition)
|
||||
{
|
||||
InternalSetMovementDefinition(NewDefinition, false);
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::OnReplicated_MovementDefinition()
|
||||
{
|
||||
OnMovementSetChanged(MovementSet);
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::SetMovementSet(const FGameplayTag& NewMovementSet, bool bSendRpc)
|
||||
{
|
||||
if (MovementSet == NewMovementSet || GetOwner()->GetLocalRole() <= ROLE_SimulatedProxy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto PreviousMovementSet{MovementSet};
|
||||
|
||||
MovementSet = NewMovementSet;
|
||||
|
||||
MARK_PROPERTY_DIRTY_FROM_NAME(ThisClass, MovementSet, this)
|
||||
|
||||
OnMovementSetChanged(PreviousMovementSet);
|
||||
|
||||
if (bSendRpc)
|
||||
{
|
||||
if (GetOwner()->GetLocalRole() >= ROLE_Authority)
|
||||
{
|
||||
ClientSetMovementSet(MovementSet);
|
||||
}
|
||||
else
|
||||
{
|
||||
ServerSetMovementSet(MovementSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::ClientSetMovementSet_Implementation(const FGameplayTag& NewMovementSet)
|
||||
{
|
||||
SetMovementSet(NewMovementSet, false);
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::ServerSetMovementSet_Implementation(const FGameplayTag& NewMovementSet)
|
||||
{
|
||||
SetMovementSet(NewMovementSet, false);
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::OnReplicated_MovementSet(const FGameplayTag& PreviousMovementSet)
|
||||
{
|
||||
OnMovementSetChanged(PreviousMovementSet);
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::RefreshMovementSetSetting()
|
||||
{
|
||||
const UGMS_MovementDefinition* LoadedDefinition = MovementDefinition.LoadSynchronous();
|
||||
|
||||
if (!LoadedDefinition)
|
||||
{
|
||||
GMS_CLOG(Warning, "Missing valid movement definition!")
|
||||
return;
|
||||
}
|
||||
|
||||
if (!LoadedDefinition->MovementSets.Contains(MovementSet))
|
||||
{
|
||||
GMS_CLOG(Warning, "No movement set(%s) found in movement definition(%s)!", *MovementSet.ToString(), *GetNameSafe(LoadedDefinition))
|
||||
return;
|
||||
}
|
||||
MovementSetSetting = MovementDefinition->MovementSets[MovementSet];
|
||||
RefreshControlSetting();
|
||||
|
||||
// bool bFoundMovementSet{false};
|
||||
// for (int32 i = MovementDefinitions.Num() - 1; i >= 0; i--)
|
||||
// {
|
||||
// if (MovementDefinitions[i].IsNull())
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
// if (!MovementDefinitions[i].IsValid())
|
||||
// {
|
||||
// MovementDefinitions[i].LoadSynchronous();
|
||||
// }
|
||||
// if (MovementDefinitions[i]->MovementSets.Contains(MovementSet))
|
||||
// {
|
||||
// MovementDefinition = MovementDefinitions[i].Get();
|
||||
// MovementSetSetting = MovementDefinition->MovementSets[MovementSet];
|
||||
// bFoundMovementSet = true;
|
||||
// RefreshControlSetting();
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// if (!bFoundMovementSet)
|
||||
// {
|
||||
// GMS_CLOG(Error, "No movement set(%s) found in movement definitions!", *MovementSet.ToString())
|
||||
// }
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::RefreshControlSetting()
|
||||
{
|
||||
const UGMS_MovementControlSetting_Default* NewSetting = MovementSetSetting.bControlSettingPerOverlayMode && MovementSetSetting.ControlSettings.Contains(OverlayMode)
|
||||
? MovementSetSetting.ControlSettings[OverlayMode]
|
||||
: MovementSetSetting.ControlSetting;
|
||||
|
||||
if (NewSetting != nullptr && !NewSetting->MovementStates.IsEmpty())
|
||||
{
|
||||
ControlSetting = NewSetting;
|
||||
RefreshMovementStateSetting();
|
||||
ApplyMovementSetting();
|
||||
}
|
||||
else
|
||||
{
|
||||
ControlSetting = nullptr;
|
||||
GMS_CLOG(Error, "Empty MovementState settings are found in the movement set(%s) of definition(%s), which is not allowed!", *MovementSet.ToString(),
|
||||
*MovementDefinition->GetName())
|
||||
}
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::OnMovementSetChanged_Implementation(const FGameplayTag& PreviousMovementSet)
|
||||
{
|
||||
RefreshMovementSetSetting();
|
||||
OnMovementSetChangedEvent.Broadcast(PreviousMovementSet);
|
||||
}
|
||||
|
||||
const FGameplayTag& UGMS_MovementSystemComponent::GetDesiredMovementState() const
|
||||
{
|
||||
return FGameplayTag::EmptyTag;
|
||||
}
|
||||
|
||||
|
||||
void UGMS_MovementSystemComponent::RefreshMovementStateSetting()
|
||||
{
|
||||
if (!IsValid(ControlSetting))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FGMS_MovementStateSetting NewStateSetting;
|
||||
if (!ControlSetting->GetStateByTag(GetMovementState(), NewStateSetting))
|
||||
{
|
||||
checkf(!ControlSetting->MovementStates.IsEmpty(), TEXT("Found empty MovementState Settings on %s!"), *ControlSetting->GetName())
|
||||
NewStateSetting = ControlSetting->MovementStates.Last();
|
||||
SetDesiredMovement(NewStateSetting.Tag);
|
||||
GMS_CLOG(Verbose, "No MovementState setting for current movement state(%s), Change desired last one(%s) in list.", *GetMovementState().ToString(),
|
||||
*NewStateSetting.Tag.ToString());
|
||||
}
|
||||
|
||||
MovementStateSetting = NewStateSetting;
|
||||
|
||||
if (bRespectAllowedRotationModesSettings)
|
||||
{
|
||||
if (!MovementStateSetting.AllowedRotationModes.Contains(GetDesiredRotationMode()))
|
||||
{
|
||||
FGameplayTag AdjustedRotationMode = MovementStateSetting.AllowedRotationModes.Last();
|
||||
GMS_CLOG(Warning, "current movement state(%s) doesn't allow current desired rotation mode(%s), adjusted to:%s", *GetMovementState().ToString(), *GetDesiredRotationMode().ToString(),
|
||||
*AdjustedRotationMode.ToString());
|
||||
SetDesiredRotationMode(AdjustedRotationMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::ApplyMovementSetting()
|
||||
{
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region MovementState
|
||||
void UGMS_MovementSystemComponent::SetDesiredMovement(const FGameplayTag& NewDesiredMovement)
|
||||
{
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::CycleDesiredMovementState(bool bForward)
|
||||
{
|
||||
if (GetNumOfMovementStateSettings() == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
int32 Index = ControlSetting->MovementStates.IndexOfByKey(GetMovementState());
|
||||
|
||||
if (Index == INDEX_NONE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (bForward && ControlSetting->MovementStates.IsValidIndex(Index + 1))
|
||||
{
|
||||
SetDesiredMovement(ControlSetting->MovementStates[Index + 1].Tag);
|
||||
}
|
||||
|
||||
if (!bForward && ControlSetting->MovementStates.IsValidIndex(Index - 1))
|
||||
{
|
||||
SetDesiredMovement(ControlSetting->MovementStates[Index - 1].Tag);
|
||||
}
|
||||
}
|
||||
|
||||
const FGameplayTag& UGMS_MovementSystemComponent::GetMovementState() const
|
||||
{
|
||||
return FGameplayTag::EmptyTag;
|
||||
}
|
||||
|
||||
int32 UGMS_MovementSystemComponent::GetSpeedLevel() const
|
||||
{
|
||||
return MovementStateSetting.SpeedLevel;
|
||||
}
|
||||
|
||||
float UGMS_MovementSystemComponent::GetMappedMovementSpeedLevel(float Speed) const
|
||||
{
|
||||
if (!ControlSetting)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
float SpeedLevelAmount = ControlSetting->MovementStates.Last().SpeedLevel;
|
||||
for (int32 i = ControlSetting->MovementStates.Num() - 2; i >= 0; i--)
|
||||
{
|
||||
const FGMS_MovementStateSetting& Max = ControlSetting->MovementStates[i + 1];
|
||||
const FGMS_MovementStateSetting& Min = ControlSetting->MovementStates[i];
|
||||
// In current range.
|
||||
if (Min.Speed > 0 && Speed >= Min.Speed && Speed <= Max.Speed)
|
||||
{
|
||||
SpeedLevelAmount = FMath::GetMappedRangeValueClamped(FVector2f{Min.Speed, Max.Speed}, {static_cast<float>(Min.SpeedLevel), static_cast<float>(Max.SpeedLevel)}, Speed);
|
||||
}
|
||||
}
|
||||
GMS_CLOG(VeryVerbose, "Mapped speed(%f) to speed level(%f)", Speed, SpeedLevelAmount)
|
||||
return SpeedLevelAmount;
|
||||
}
|
||||
|
||||
|
||||
void UGMS_MovementSystemComponent::OnMovementStateChanged_Implementation(const FGameplayTag& PreviousMovementState)
|
||||
{
|
||||
RefreshMovementStateSetting();
|
||||
ApplyMovementSetting();
|
||||
OnMovementStateChangedEvent.Broadcast(PreviousMovementState);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Input
|
||||
|
||||
FVector UGMS_MovementSystemComponent::GetMovementIntent() const
|
||||
{
|
||||
checkf(0, TEXT("Not implemented"));
|
||||
return FVector::ZeroVector;
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::RefreshInput(float DeltaTime)
|
||||
{
|
||||
LocomotionState.bHasInput = GetMovementIntent().SizeSquared() > UE_KINDA_SMALL_NUMBER;
|
||||
|
||||
if (LocomotionState.bHasInput)
|
||||
{
|
||||
LocomotionState.InputYawAngle = UE_REAL_TO_FLOAT(UGMS_Vector::DirectionToAngleXY(GetMovementIntent()));
|
||||
}
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::TurnAtRate(float Direction)
|
||||
{
|
||||
if (Direction != 0 && GetRotationMode() == GMS_RotationModeTags::VelocityDirection &&
|
||||
OwnerPawn->GetLocalRole() >= ROLE_AutonomousProxy)
|
||||
{
|
||||
if (const auto Setting = ControlSetting->VelocityDirectionSetting.GetPtr<FGMS_VelocityDirectionSetting_RateBased>())
|
||||
{
|
||||
const float TurnRate =
|
||||
IsValid(Setting->TurnRateSpeedCurve)
|
||||
? Setting->TurnRateSpeedCurve->GetFloatValue(FMath::Max(1.0f, GetMappedMovementSpeedLevel(UE_REAL_TO_FLOAT(LocomotionState.Velocity.Size2D()))))
|
||||
: Setting->TurnRate;
|
||||
|
||||
float YawDelta = Direction * TurnRate * GetWorld()->GetDeltaSeconds();
|
||||
if (FMath::Abs(YawDelta) > UE_SMALL_NUMBER)
|
||||
{
|
||||
float TargetYawAngle = FMath::UnwindDegrees(OwnerPawn->GetActorRotation().Yaw + YawDelta);
|
||||
|
||||
// 或者,方法2:使用 RotateAngleAxis(如果需要保持四元数精度)
|
||||
// FQuat DeltaRotation = FQuat(FVector::UpVector, FMath::DegreesToRadians(YawDelta));
|
||||
// FQuat NewRotation = DeltaRotation * LocomotionState.RotationQuaternion;
|
||||
// NewDesiredVelocityYawAngle = NewRotation.Rotator().Yaw;
|
||||
|
||||
// FRotator RotationDelta = FRotator(0,Direction * GetVelocityDirectionSetting().TurningRate * DeltaTime,0);
|
||||
//
|
||||
// NewDesiredVelocityYawAngle = (RotationDelta.Quaternion() * LocomotionState.RotationQuaternion).Rotator().Yaw;
|
||||
|
||||
SetDesiredVelocityYawAngle(TargetYawAngle);
|
||||
if (OwnerPawn->GetLocalRole() < ROLE_Authority)
|
||||
{
|
||||
ServerSetDesiredVelocityYawAngle(TargetYawAngle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region ViewSystem
|
||||
|
||||
const FGMS_ViewState& UGMS_MovementSystemComponent::GetViewState() const
|
||||
{
|
||||
return ViewState;
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::SetReplicatedViewRotation(const FRotator& NewViewRotation, bool bSendRpc)
|
||||
{
|
||||
if (!ReplicatedViewRotation.Equals(NewViewRotation))
|
||||
{
|
||||
ReplicatedViewRotation = NewViewRotation;
|
||||
|
||||
MARK_PROPERTY_DIRTY_FROM_NAME(ThisClass, ReplicatedViewRotation, this)
|
||||
|
||||
if (bSendRpc && GetOwner()->GetLocalRole() == ROLE_AutonomousProxy)
|
||||
{
|
||||
ServerSetReplicatedViewRotation(ReplicatedViewRotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::ServerSetReplicatedViewRotation_Implementation(const FRotator& NewViewRotation)
|
||||
{
|
||||
SetReplicatedViewRotation(NewViewRotation, false);
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::OnReplicated_ReplicatedViewRotation()
|
||||
{
|
||||
ViewState.Rotation = MovementBase.bHasRelativeRotation ? (MovementBase.Rotation * ReplicatedViewRotation.Quaternion()).Rotator() : ReplicatedViewRotation;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Rotation Mode
|
||||
|
||||
const FGameplayTag& UGMS_MovementSystemComponent::GetDesiredRotationMode() const
|
||||
{
|
||||
return FGameplayTag::EmptyTag;
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::SetDesiredRotationMode(const FGameplayTag& NewDesiredRotationMode)
|
||||
{
|
||||
}
|
||||
|
||||
const FGameplayTag& UGMS_MovementSystemComponent::GetRotationMode() const
|
||||
{
|
||||
return FGameplayTag::EmptyTag;
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::OnRotationModeChanged_Implementation(const FGameplayTag& PreviousRotationMode)
|
||||
{
|
||||
ApplyMovementSetting();
|
||||
OnRotationModeChangedEvent.Broadcast(PreviousRotationMode);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region OverlayMode
|
||||
const FGameplayTag& UGMS_MovementSystemComponent::GetOverlayMode() const
|
||||
{
|
||||
return OverlayMode;
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::SetOverlayMode(const FGameplayTag& NewOverlayMode)
|
||||
{
|
||||
SetOverlayMode(NewOverlayMode, true);
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::SetOverlayMode(const FGameplayTag& NewOverlayMode, bool bSendRpc)
|
||||
{
|
||||
if (OverlayMode == NewOverlayMode || OwnerPawn->GetLocalRole() <= ROLE_SimulatedProxy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto PreviousOverlayMode{OverlayMode};
|
||||
|
||||
OverlayMode = NewOverlayMode;
|
||||
|
||||
MARK_PROPERTY_DIRTY_FROM_NAME(ThisClass, OverlayMode, this)
|
||||
|
||||
OnOverlayModeChanged(PreviousOverlayMode);
|
||||
|
||||
if (bSendRpc)
|
||||
{
|
||||
if (OwnerPawn->GetLocalRole() >= ROLE_Authority)
|
||||
{
|
||||
ClientSetOverlayMode(OverlayMode);
|
||||
}
|
||||
else
|
||||
{
|
||||
ServerSetOverlayMode(OverlayMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::ClientSetOverlayMode_Implementation(const FGameplayTag& NewOverlayMode)
|
||||
{
|
||||
SetOverlayMode(NewOverlayMode, false);
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::ServerSetOverlayMode_Implementation(const FGameplayTag& NewOverlayMode)
|
||||
{
|
||||
SetOverlayMode(NewOverlayMode, false);
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::OnReplicated_OverlayMode(const FGameplayTag& PreviousOverlayMode)
|
||||
{
|
||||
OnOverlayModeChanged(PreviousOverlayMode);
|
||||
}
|
||||
|
||||
void UGMS_MovementSystemComponent::OnOverlayModeChanged_Implementation(const FGameplayTag& PreviousOverlayMode)
|
||||
{
|
||||
if (MovementSetSetting.bControlSettingPerOverlayMode)
|
||||
{
|
||||
RefreshControlSetting();
|
||||
}
|
||||
OnOverlayModeChangedEvent.Broadcast(PreviousOverlayMode);
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
|
||||
#if WITH_EDITOR
|
||||
EDataValidationResult UGMS_MovementSystemComponent::IsDataValid(class FDataValidationContext& Context) const
|
||||
{
|
||||
if (IsTemplate() && AnimGraphSetting == nullptr)
|
||||
{
|
||||
Context.AddError(FText::FromString("AnimGraphSetting is required!"));
|
||||
return EDataValidationResult::Invalid;
|
||||
}
|
||||
return Super::IsDataValid(Context);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,753 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "GMS_MoverMovementSystemComponent.h"
|
||||
#include "PoseSearch/PoseSearchTrajectoryPredictor.h"
|
||||
#include "Runtime/Launch/Resources/Version.h"
|
||||
#include "GameFramework/Pawn.h"
|
||||
#include "Components/SkeletalMeshComponent.h"
|
||||
#include "MoverComponent.h"
|
||||
#include "MoverPoseSearchTrajectoryPredictor.h"
|
||||
#include "BoneControllers/AnimNode_OffsetRootBone.h"
|
||||
#include "Components/CapsuleComponent.h"
|
||||
#include "DefaultMovementSet/CharacterMoverComponent.h"
|
||||
#include "DefaultMovementSet/NavMoverComponent.h"
|
||||
#include "DefaultMovementSet/Settings/CommonLegacyMovementSettings.h"
|
||||
#include "Locomotions/GMS_MainAnimInstance.h"
|
||||
#include "MoveLibrary/MovementMixer.h"
|
||||
#include "Mover/GMS_MoverStructLibrary.h"
|
||||
#include "Mover/Modifers/GMS_MovementStateModifer.h"
|
||||
#include "Net/UnrealNetwork.h"
|
||||
#include "Net/Core/PushModel/PushModel.h"
|
||||
#include "Settings/GMS_SettingObjectLibrary.h"
|
||||
#include "Utility/GMS_Constants.h"
|
||||
#include "Utility/GMS_Log.h"
|
||||
#include "Utility/GMS_Math.h"
|
||||
#include "Utility/GMS_Utility.h"
|
||||
#include "Utility/GMS_Vector.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_MoverMovementSystemComponent)
|
||||
|
||||
|
||||
UGMS_MoverMovementSystemComponent::UGMS_MoverMovementSystemComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
|
||||
{
|
||||
SetIsReplicatedByDefault(true);
|
||||
PrimaryComponentTick.bCanEverTick = true;
|
||||
bWantsInitializeComponent = true;
|
||||
bReplicateUsingRegisteredSubObjectList = true;
|
||||
|
||||
MovementModeToTagMapping = {
|
||||
{TEXT("None"), GMS_MovementModeTags::None},
|
||||
{TEXT("Walking"), GMS_MovementModeTags::Grounded},
|
||||
{TEXT("NavWalking"), GMS_MovementModeTags::Grounded},
|
||||
{TEXT("Falling"), GMS_MovementModeTags::InAir},
|
||||
{TEXT("Swimming"), GMS_MovementModeTags::Swimming},
|
||||
{TEXT("Flying"), GMS_MovementModeTags::Flying},
|
||||
};
|
||||
}
|
||||
|
||||
void UGMS_MoverMovementSystemComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
|
||||
{
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
|
||||
FDoRepLifetimeParams Parameters;
|
||||
Parameters.bIsPushBased = true;
|
||||
|
||||
Parameters.Condition = COND_SkipOwner;
|
||||
|
||||
DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, MovementState, Parameters)
|
||||
DOREPLIFETIME_WITH_PARAMS_FAST(ThisClass, RotationMode, Parameters)
|
||||
}
|
||||
|
||||
void UGMS_MoverMovementSystemComponent::InitializeComponent()
|
||||
{
|
||||
Super::InitializeComponent();
|
||||
|
||||
MovementState = DesiredMovementState;
|
||||
RotationMode = DesiredRotationMode;
|
||||
MoverComponent = OwnerPawn->FindComponentByClass<UMoverComponent>();
|
||||
if (MoverComponent)
|
||||
{
|
||||
TrajectoryPredictor = NewObject<UMoverTrajectoryPredictor>(this, UMoverTrajectoryPredictor::StaticClass());
|
||||
TrajectoryPredictor->Setup(MoverComponent);
|
||||
MoverComponent->InputProducer = this;
|
||||
if (!MoverComponent->MovementMixer)
|
||||
{
|
||||
// Prevent crash by early create this object on initialzie component.
|
||||
MoverComponent->MovementMixer = NewObject<UMovementMixer>(this, TEXT("Default Movement Mixer"));
|
||||
}
|
||||
// Make sure this component are ticking after the mover component so this component can access the most up-to-date mover state.
|
||||
AddTickPrerequisiteComponent(MoverComponent);
|
||||
}
|
||||
|
||||
NavMoverComponent = OwnerPawn->FindComponentByClass<UNavMoverComponent>();
|
||||
|
||||
MeshComponent = OwnerPawn->FindComponentByClass<USkeletalMeshComponent>();
|
||||
if (MeshComponent)
|
||||
{
|
||||
AnimationInstance = MeshComponent->GetAnimInstance();
|
||||
// Make sure the mesh and animation blueprint are ticking after the character so they can access the most up-to-date character state.
|
||||
MeshComponent->AddTickPrerequisiteComponent(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Called when the game starts
|
||||
void UGMS_MoverMovementSystemComponent::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
//This callback fires only on predicting client and server, not simulated pawn.
|
||||
MoverComponent->OnMovementModeChanged.AddDynamic(this, &ThisClass::OnMoverMovementModeChanged);
|
||||
|
||||
RefreshMovementSetSetting();
|
||||
|
||||
MoverComponent->OnPreSimulationTick.AddDynamic(this, &ThisClass::OnMoverPreSimulationTick);
|
||||
}
|
||||
|
||||
void UGMS_MoverMovementSystemComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
||||
{
|
||||
if (IsValid(MoverComponent))
|
||||
{
|
||||
MoverComponent->OnMovementModeChanged.RemoveDynamic(this, &ThisClass::OnMoverMovementModeChanged);
|
||||
}
|
||||
Super::EndPlay(EndPlayReason);
|
||||
}
|
||||
|
||||
|
||||
// Called every frame
|
||||
void UGMS_MoverMovementSystemComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
|
||||
{
|
||||
DECLARE_SCOPE_CYCLE_COUNTER(TEXT("UGMS_MoverMovementSystemComponent::TickComponent"), STAT_GMS_MovementSystem_Tick, STATGROUP_GMS)
|
||||
TRACE_CPUPROFILER_EVENT_SCOPE_STR(__FUNCTION__)
|
||||
|
||||
if (!GetMovementDefinition().IsValid() || !IsValid(AnimationInstance) || !IsValid(ControlSetting))
|
||||
{
|
||||
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
||||
return;
|
||||
}
|
||||
|
||||
RefreshMovementBase();
|
||||
|
||||
RefreshInput(DeltaTime);
|
||||
|
||||
RefreshLocomotionEarly();
|
||||
|
||||
RefreshView(DeltaTime);
|
||||
|
||||
RefreshLocomotion(DeltaTime);
|
||||
|
||||
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
|
||||
|
||||
RefreshLocomotionLate(DeltaTime);
|
||||
}
|
||||
|
||||
void UGMS_MoverMovementSystemComponent::OnMoverPreSimulationTick(const FMoverTimeStep& TimeStep, const FMoverInputCmdContext& InputCmd)
|
||||
{
|
||||
const FCharacterDefaultInputs* CharacterInputs = InputCmd.InputCollection.FindDataByType<FCharacterDefaultInputs>();
|
||||
const FGMS_MoverMovementControlInputs* ControlInputs = InputCmd.InputCollection.FindDataByType<FGMS_MoverMovementControlInputs>();
|
||||
|
||||
if (ControlInputs)
|
||||
{
|
||||
// update movement state, settings. before actually do it.
|
||||
ApplyMovementState(ControlInputs->DesiredMovementState);
|
||||
|
||||
ApplyRotationMode(ControlInputs->DesiredRotationMode);
|
||||
}
|
||||
}
|
||||
|
||||
void UGMS_MoverMovementSystemComponent::OnMoverMovementModeChanged(const FName& PreviousMovementModeName, const FName& NewMovementModeName)
|
||||
{
|
||||
// Use the mover movement mode to set the locomotion mode to the right value.
|
||||
|
||||
if (NewMovementModeName != NAME_None)
|
||||
{
|
||||
if (MovementModeToTagMapping.Contains(NewMovementModeName) && MovementModeToTagMapping[NewMovementModeName].IsValid())
|
||||
{
|
||||
if (LocomotionMode != MovementModeToTagMapping[NewMovementModeName])
|
||||
{
|
||||
SetLocomotionMode(MovementModeToTagMapping[NewMovementModeName]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GMS_CLOG(Error, "No locomotion mode mapping for MovementMode:%s", *NewMovementModeName.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGMS_MoverMovementSystemComponent::ApplyMovementSetting()
|
||||
{
|
||||
if (IsValid(MoverComponent) && IsValid(ControlSetting))
|
||||
{
|
||||
if (const FGMS_MovementStateSetting* TempMS = ControlSetting->GetMovementStateSetting(DesiredMovementState, true))
|
||||
{
|
||||
if (UCommonLegacyMovementSettings* LegacyMovementSettings = MoverComponent->FindSharedSettings_Mutable<UCommonLegacyMovementSettings>())
|
||||
{
|
||||
LegacyMovementSettings->MaxSpeed = TempMS->Speed;
|
||||
LegacyMovementSettings->Acceleration = TempMS->Acceleration;
|
||||
LegacyMovementSettings->Deceleration = TempMS->BrakingDeceleration;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UGMS_MoverMovementSystemComponent::RefreshView(float DeltaTime)
|
||||
{
|
||||
ViewState.PreviousYawAngle = UE_REAL_TO_FLOAT(ViewState.Rotation.Yaw);
|
||||
|
||||
if (OwnerPawn->IsLocallyControlled() || (OwnerPawn->GetLocalRole() >= ROLE_Authority && IsValid(OwnerPawn->GetController())))
|
||||
{
|
||||
// The character movement component already sends the view rotation to the
|
||||
// server if movement is replicated, so we don't have to do this ourselves.
|
||||
SetReplicatedViewRotation(OwnerPawn->GetViewRotation().GetNormalized(), true);
|
||||
}
|
||||
|
||||
ViewState.Rotation = ReplicatedViewRotation;
|
||||
// Set the yaw speed by comparing the current and previous view yaw angle, divided by
|
||||
// delta seconds. This represents the speed the camera is rotating from left to right.
|
||||
if (DeltaTime > UE_SMALL_NUMBER)
|
||||
{
|
||||
ViewState.YawSpeed = FMath::Abs(UE_REAL_TO_FLOAT(ViewState.Rotation.Yaw - ViewState.PreviousYawAngle)) / DeltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
void UGMS_MoverMovementSystemComponent::ServerSetReplicatedViewRotation_Implementation(const FRotator& NewViewRotation)
|
||||
{
|
||||
Super::ServerSetReplicatedViewRotation_Implementation(NewViewRotation);
|
||||
|
||||
// Mover doesn't send control rotation to server, so we do it.
|
||||
if (OwnerPawn->GetController() && !OwnerPawn->GetController()->GetControlRotation().Equals(NewViewRotation))
|
||||
{
|
||||
OwnerPawn->GetController()->SetControlRotation(NewViewRotation);
|
||||
}
|
||||
}
|
||||
|
||||
TScriptInterface<IPoseSearchTrajectoryPredictorInterface> UGMS_MoverMovementSystemComponent::GetTrajectoryPredictor() const
|
||||
{
|
||||
return TrajectoryPredictor;
|
||||
}
|
||||
|
||||
bool UGMS_MoverMovementSystemComponent::IsCrouching() const
|
||||
{
|
||||
if (UCharacterMoverComponent* CharacterMover = Cast<UCharacterMoverComponent>(MoverComponent))
|
||||
{
|
||||
return CharacterMover->IsCrouching();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
float UGMS_MoverMovementSystemComponent::GetMaxSpeed() const
|
||||
{
|
||||
const UCommonLegacyMovementSettings* CommonLegacySettings = MoverComponent->FindSharedSettings<UCommonLegacyMovementSettings>();
|
||||
|
||||
return CommonLegacySettings->MaxSpeed;
|
||||
}
|
||||
|
||||
float UGMS_MoverMovementSystemComponent::GetScaledCapsuleRadius() const
|
||||
{
|
||||
if (UCapsuleComponent* Capsule = Cast<UCapsuleComponent>(MoverComponent->GetUpdatedComponent()))
|
||||
{
|
||||
return Capsule->GetScaledCapsuleRadius();
|
||||
}
|
||||
return GetDefault<UCapsuleComponent>()->GetUnscaledCapsuleRadius();
|
||||
}
|
||||
|
||||
float UGMS_MoverMovementSystemComponent::GetScaledCapsuleHalfHeight() const
|
||||
{
|
||||
if (UCapsuleComponent* Capsule = Cast<UCapsuleComponent>(MoverComponent->GetUpdatedComponent()))
|
||||
{
|
||||
return Capsule->GetScaledCapsuleHalfHeight();
|
||||
}
|
||||
return GetDefault<UCapsuleComponent>()->GetUnscaledCapsuleHalfHeight();
|
||||
}
|
||||
|
||||
float UGMS_MoverMovementSystemComponent::GetMaxAcceleration() const
|
||||
{
|
||||
const UCommonLegacyMovementSettings* CommonLegacySettings = MoverComponent->FindSharedSettings<UCommonLegacyMovementSettings>();
|
||||
|
||||
return CommonLegacySettings->Acceleration;
|
||||
}
|
||||
|
||||
float UGMS_MoverMovementSystemComponent::GetMaxBrakingDeceleration() const
|
||||
{
|
||||
const UCommonLegacyMovementSettings* CommonLegacySettings = MoverComponent->FindSharedSettings<UCommonLegacyMovementSettings>();
|
||||
|
||||
return CommonLegacySettings->Deceleration;
|
||||
}
|
||||
|
||||
float UGMS_MoverMovementSystemComponent::GetWalkableFloorZ() const
|
||||
{
|
||||
const UCommonLegacyMovementSettings* CommonLegacySettings = MoverComponent->FindSharedSettings<UCommonLegacyMovementSettings>();
|
||||
|
||||
return CommonLegacySettings->MaxWalkSlopeCosine;
|
||||
}
|
||||
|
||||
float UGMS_MoverMovementSystemComponent::GetGravityZ() const
|
||||
{
|
||||
return MoverComponent->GetGravityAcceleration().Z;
|
||||
}
|
||||
|
||||
USkeletalMeshComponent* UGMS_MoverMovementSystemComponent::GetMesh() const
|
||||
{
|
||||
return MeshComponent;
|
||||
}
|
||||
|
||||
bool UGMS_MoverMovementSystemComponent::IsMovingOnGround() const
|
||||
{
|
||||
return IsValid(MoverComponent) ? MoverComponent->HasGameplayTag(Mover_IsOnGround, true) : false;
|
||||
}
|
||||
|
||||
void UGMS_MoverMovementSystemComponent::RefreshLocomotionEarly()
|
||||
{
|
||||
}
|
||||
|
||||
void UGMS_MoverMovementSystemComponent::RefreshLocomotion(float DeltaTime)
|
||||
{
|
||||
LocomotionState.Velocity = MoverComponent->GetVelocity();
|
||||
|
||||
// Determine if the character is moving by getting its speed. The speed equals the length
|
||||
// of the horizontal velocity, so it does not take vertical movement into account. If the
|
||||
// character is moving, update the last velocity rotation. This value is saved because it might
|
||||
// be useful to know the last orientation of a movement even after the character has stopped.
|
||||
|
||||
LocomotionState.Speed = UE_REAL_TO_FLOAT(LocomotionState.Velocity.Size2D());
|
||||
|
||||
static constexpr auto HasSpeedThreshold{1.0f};
|
||||
|
||||
LocomotionState.bHasVelocity = LocomotionState.Speed >= HasSpeedThreshold;
|
||||
|
||||
if (LocomotionState.bHasVelocity)
|
||||
{
|
||||
LocomotionState.VelocityYawAngle = UE_REAL_TO_FLOAT(UGMS_Vector::DirectionToAngleXY(LocomotionState.Velocity));
|
||||
}
|
||||
|
||||
// Character is moving if has speed and current acceleration, or if the speed is greater than the moving speed threshold.
|
||||
|
||||
LocomotionState.bMoving = (LocomotionState.bHasInput && LocomotionState.bHasVelocity) ||
|
||||
LocomotionState.Speed > ControlSetting->MovingSpeedThreshold;
|
||||
}
|
||||
|
||||
void UGMS_MoverMovementSystemComponent::RefreshDynamicMovementState()
|
||||
{
|
||||
}
|
||||
|
||||
void UGMS_MoverMovementSystemComponent::RefreshLocomotionLate(float DeltaTime)
|
||||
{
|
||||
if (!LocomotionMode.IsValid())
|
||||
{
|
||||
RefreshTargetYawAngleUsingActorRotation();
|
||||
}
|
||||
}
|
||||
|
||||
void UGMS_MoverMovementSystemComponent::RefreshTargetYawAngleUsingActorRotation()
|
||||
{
|
||||
const auto YawAngle{UE_REAL_TO_FLOAT(OwnerPawn->GetActorRotation().Yaw)};
|
||||
|
||||
SetTargetYawAngle(YawAngle);
|
||||
}
|
||||
|
||||
void UGMS_MoverMovementSystemComponent::SetTargetYawAngle(const float TargetYawAngle)
|
||||
{
|
||||
LocomotionState.TargetYawAngle = FRotator3f::NormalizeAxis(TargetYawAngle);
|
||||
|
||||
RefreshViewRelativeTargetYawAngle();
|
||||
|
||||
LocomotionState.SmoothTargetYawAngle = LocomotionState.TargetYawAngle;
|
||||
}
|
||||
|
||||
void UGMS_MoverMovementSystemComponent::RefreshViewRelativeTargetYawAngle()
|
||||
{
|
||||
LocomotionState.ViewRelativeTargetYawAngle = FRotator3f::NormalizeAxis(UE_REAL_TO_FLOAT(
|
||||
ViewState.Rotation.Yaw - LocomotionState.TargetYawAngle));
|
||||
}
|
||||
|
||||
FGMS_PredictGroundMovementPivotLocationParams UGMS_MoverMovementSystemComponent::GetPredictGroundMovementPivotLocationParams() const
|
||||
{
|
||||
FGMS_PredictGroundMovementPivotLocationParams Params;
|
||||
|
||||
if (MoverComponent)
|
||||
{
|
||||
if (const UCommonLegacyMovementSettings* CommonLegacySettings = MoverComponent->FindSharedSettings<UCommonLegacyMovementSettings>())
|
||||
{
|
||||
Params.Acceleration = MoverComponent->GetMovementIntent() * CommonLegacySettings->Acceleration;
|
||||
Params.Velocity = MoverComponent->GetVelocity();
|
||||
Params.GroundFriction = CommonLegacySettings->GroundFriction;
|
||||
}
|
||||
}
|
||||
return Params;
|
||||
}
|
||||
|
||||
FGMS_PredictGroundMovementStopLocationParams UGMS_MoverMovementSystemComponent::GetPredictGroundMovementStopLocationParams() const
|
||||
{
|
||||
FGMS_PredictGroundMovementStopLocationParams Params;
|
||||
if (MoverComponent)
|
||||
{
|
||||
if (const UCommonLegacyMovementSettings* CommonLegacySettings = MoverComponent->FindSharedSettings<UCommonLegacyMovementSettings>())
|
||||
{
|
||||
Params.Velocity = MoverComponent->GetVelocity();
|
||||
Params.bUseSeparateBrakingFriction = true;
|
||||
Params.BrakingFriction = CommonLegacySettings->BrakingFriction;
|
||||
Params.GroundFriction = CommonLegacySettings->GroundFriction;
|
||||
Params.BrakingFrictionFactor = CommonLegacySettings->BrakingFrictionFactor;
|
||||
Params.BrakingDecelerationWalking = CommonLegacySettings->Deceleration;
|
||||
}
|
||||
}
|
||||
return Params;
|
||||
}
|
||||
|
||||
void UGMS_MoverMovementSystemComponent::RefreshMovementBase()
|
||||
{
|
||||
UPrimitiveComponent* BasePrimitive = MoverComponent->GetMovementBase();
|
||||
FName BaseBoneName = MoverComponent->GetMovementBaseBoneName();
|
||||
if (BasePrimitive != MovementBase.Primitive || BaseBoneName != MovementBase.BoneName)
|
||||
{
|
||||
MovementBase.Primitive = BasePrimitive;
|
||||
MovementBase.BoneName = BaseBoneName;
|
||||
MovementBase.bBaseChanged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
MovementBase.bBaseChanged = false;
|
||||
}
|
||||
|
||||
|
||||
MovementBase.bHasRelativeLocation = UBasedMovementUtils::IsADynamicBase(BasePrimitive);
|
||||
MovementBase.bHasRelativeRotation = MovementBase.bHasRelativeLocation && bUseBaseRelativeMovement;
|
||||
|
||||
const auto PreviousRotation{MovementBase.Rotation};
|
||||
|
||||
UBasedMovementUtils::GetMovementBaseTransform(BasePrimitive, BaseBoneName,
|
||||
MovementBase.Location, MovementBase.Rotation);
|
||||
|
||||
MovementBase.DeltaRotation = MovementBase.bHasRelativeLocation && !MovementBase.bBaseChanged
|
||||
? (MovementBase.Rotation * PreviousRotation.Inverse()).Rotator()
|
||||
: FRotator::ZeroRotator;
|
||||
}
|
||||
|
||||
|
||||
void UGMS_MoverMovementSystemComponent::ProduceInput_Implementation(int32 SimTimeMs, FMoverInputCmdContext& InputCmdResult)
|
||||
{
|
||||
InputCmdResult = OnProduceInput(static_cast<float>(SimTimeMs), InputCmdResult);
|
||||
}
|
||||
|
||||
#pragma region MovementState
|
||||
|
||||
const FGameplayTag& UGMS_MoverMovementSystemComponent::GetDesiredMovementState() const
|
||||
{
|
||||
return DesiredMovementState;
|
||||
}
|
||||
|
||||
void UGMS_MoverMovementSystemComponent::SetDesiredMovement(const FGameplayTag& NewDesiredMovement)
|
||||
{
|
||||
DesiredMovementState = NewDesiredMovement;
|
||||
}
|
||||
|
||||
|
||||
const FGameplayTag& UGMS_MoverMovementSystemComponent::GetMovementState() const
|
||||
{
|
||||
return MovementState;
|
||||
}
|
||||
|
||||
void UGMS_MoverMovementSystemComponent::ApplyMovementState(const FGameplayTag& NewMovementState)
|
||||
{
|
||||
if (MovementState == NewMovementState || GetOwner()->GetLocalRole() < ROLE_AutonomousProxy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FGameplayTag Prev = MovementState;
|
||||
|
||||
MovementState = NewMovementState;
|
||||
|
||||
MARK_PROPERTY_DIRTY_FROM_NAME(ThisClass, MovementState, this)
|
||||
OnMovementStateChanged(Prev);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region RotationMode
|
||||
const FGameplayTag& UGMS_MoverMovementSystemComponent::GetDesiredRotationMode() const
|
||||
{
|
||||
return DesiredRotationMode;
|
||||
}
|
||||
|
||||
void UGMS_MoverMovementSystemComponent::SetDesiredRotationMode(const FGameplayTag& NewDesiredRotationMode)
|
||||
{
|
||||
DesiredRotationMode = NewDesiredRotationMode;
|
||||
}
|
||||
|
||||
const FGameplayTag& UGMS_MoverMovementSystemComponent::GetRotationMode() const
|
||||
{
|
||||
return RotationMode;
|
||||
}
|
||||
|
||||
void UGMS_MoverMovementSystemComponent::ApplyRotationMode(const FGameplayTag& NewRotationMode)
|
||||
{
|
||||
if (RotationMode == NewRotationMode || GetOwner()->GetLocalRole() < ROLE_AutonomousProxy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FGameplayTag Prev = RotationMode;
|
||||
|
||||
RotationMode = NewRotationMode;
|
||||
|
||||
MARK_PROPERTY_DIRTY_FROM_NAME(ThisClass, RotationMode, this)
|
||||
OnRotationModeChanged(Prev);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
FVector UGMS_MoverMovementSystemComponent::GetMovementIntent() const
|
||||
{
|
||||
if (const FCharacterDefaultInputs* CharacterInputs = MoverComponent->GetLastInputCmd().InputCollection.FindDataByType<FCharacterDefaultInputs>())
|
||||
{
|
||||
return CharacterInputs->GetMoveInput_WorldSpace();
|
||||
}
|
||||
return MoverComponent->GetMovementIntent();
|
||||
}
|
||||
|
||||
FVector UGMS_MoverMovementSystemComponent::AdjustOrientationIntent(float DeltaSeconds, const FVector& OrientationIntent) const
|
||||
{
|
||||
FVector Intent = OrientationIntent;
|
||||
if (GetRotationMode() == GMS_RotationModeTags::VelocityDirection)
|
||||
{
|
||||
if (const FGMS_VelocityDirectionSetting_RateBased* Setting = GetControlSetting()->VelocityDirectionSetting.GetPtr<FGMS_VelocityDirectionSetting_RateBased>())
|
||||
{
|
||||
float YawDelta = CachedTurnInput.Yaw * DeltaSeconds * Setting->TurnRate;
|
||||
Intent.Z += YawDelta;
|
||||
}
|
||||
}
|
||||
|
||||
if (GetRotationMode() == GMS_RotationModeTags::ViewDirection)
|
||||
{
|
||||
// Use curve to drive actor rotation only if no root bone rotation. 仅在没有使用根骨旋转时,采用曲线驱动Actor旋转。
|
||||
if (UGMS_MainAnimInstance* AnimInst = Cast<UGMS_MainAnimInstance>(MainAnimInstance))
|
||||
{
|
||||
if (AnimInst->GetOffsetRootBoneRotationMode() == EOffsetRootBoneMode::Release)
|
||||
{
|
||||
const float CurveValue = AnimationInstance->GetCurveValue(UGMS_Constants::RotationYawSpeedCurveName());
|
||||
const float DeltaYawAngle{CurveValue * DeltaSeconds};
|
||||
|
||||
if (FMath::Abs(DeltaYawAngle) > UE_SMALL_NUMBER)
|
||||
{
|
||||
Intent = Intent.RotateAngleAxis(DeltaYawAngle, FVector::ZAxisVector);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Intent;
|
||||
}
|
||||
|
||||
FMoverInputCmdContext UGMS_MoverMovementSystemComponent::OnProduceInput_Implementation(float DeltaMs, FMoverInputCmdContext InputCmdResult)
|
||||
{
|
||||
check(OwnerPawn)
|
||||
|
||||
FCharacterDefaultInputs& CharacterInputs = InputCmdResult.InputCollection.FindOrAddMutableDataByType<FCharacterDefaultInputs>();
|
||||
|
||||
FGMS_MoverMovementControlInputs& ControlInputs = InputCmdResult.InputCollection.FindOrAddMutableDataByType<FGMS_MoverMovementControlInputs>();
|
||||
|
||||
ControlInputs.DesiredMovementSet = MovementSet;
|
||||
ControlInputs.DesiredRotationMode = DesiredRotationMode;
|
||||
ControlInputs.DesiredMovementState = DesiredMovementState;
|
||||
|
||||
if (OwnerPawn->Controller == nullptr)
|
||||
{
|
||||
if (OwnerPawn->GetLocalRole() == ROLE_Authority && OwnerPawn->GetRemoteRole() == ROLE_SimulatedProxy)
|
||||
{
|
||||
static const FCharacterDefaultInputs DoNothingInput;
|
||||
// If we get here, that means this pawn is not currently possessed and we're choosing to provide default do-nothing input
|
||||
CharacterInputs = DoNothingInput;
|
||||
}
|
||||
|
||||
// We don't have a local controller so we can't run the code below. This is ok. Simulated proxies will just use previous input when extrapolating
|
||||
return InputCmdResult;
|
||||
}
|
||||
|
||||
CharacterInputs.ControlRotation = FRotator::ZeroRotator;
|
||||
|
||||
// APlayerController* PC = Cast<APlayerController>(OwnerPawn->Controller);
|
||||
// if (PC)
|
||||
// {
|
||||
// CharacterInputs.ControlRotation = PC->GetControlRotation();
|
||||
// }
|
||||
CharacterInputs.ControlRotation = ViewState.Rotation;
|
||||
|
||||
bool bRequestedNavMovement = false;
|
||||
if (NavMoverComponent)
|
||||
{
|
||||
bRequestedNavMovement = NavMoverComponent->ConsumeNavMovementData(CachedMoveInputIntent, CachedMoveInputVelocity);
|
||||
}
|
||||
|
||||
// setup move input based on velocity/raw input.
|
||||
{
|
||||
// Favor velocity input
|
||||
bool bUsingInputIntentForMove = CachedMoveInputVelocity.IsZero();
|
||||
|
||||
if (bUsingInputIntentForMove)
|
||||
{
|
||||
const FVector FinalDirectionalIntent = CharacterInputs.ControlRotation.RotateVector(CachedMoveInputIntent);
|
||||
// FRotator Rotator = CharacterInputs.ControlRotation;
|
||||
// FVector FinalDirectionalIntent;
|
||||
// if (const UCharacterMoverComponent* MoverComp = Cast<UCharacterMoverComponent>(MoverComponent))
|
||||
// {
|
||||
// if (MoverComp->IsOnGround() || MoverComp->IsFalling())
|
||||
// {
|
||||
// const FVector RotationProjectedOntoUpDirection = FVector::VectorPlaneProject(Rotator.Vector(), MoverComp->GetUpDirection()).GetSafeNormal();
|
||||
// Rotator = RotationProjectedOntoUpDirection.Rotation();
|
||||
// }
|
||||
// FinalDirectionalIntent = Rotator.RotateVector(CachedMoveInputIntent);
|
||||
// }
|
||||
CharacterInputs.SetMoveInput(EMoveInputType::DirectionalIntent, FinalDirectionalIntent);
|
||||
}
|
||||
else
|
||||
{
|
||||
CharacterInputs.SetMoveInput(EMoveInputType::Velocity, CachedMoveInputVelocity);
|
||||
}
|
||||
|
||||
// Normally cached input is cleared by OnMoveCompleted input event but that won't be called if movement came from nav movement
|
||||
if (bRequestedNavMovement)
|
||||
{
|
||||
CachedMoveInputIntent = FVector::ZeroVector;
|
||||
CachedMoveInputVelocity = FVector::ZeroVector;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static float RotationMagMin(1e-3);
|
||||
|
||||
const bool bHasAffirmativeMoveInput = (CharacterInputs.GetMoveInput().Size() >= RotationMagMin);
|
||||
|
||||
// Figure out intended orientation
|
||||
CharacterInputs.OrientationIntent = FVector::ZeroVector;
|
||||
|
||||
// setup orientation.
|
||||
{
|
||||
const bool bVelocityDirection = GetRotationMode() == GMS_RotationModeTags::VelocityDirection;
|
||||
const bool bViewDirection = !bVelocityDirection;
|
||||
if (!bHasAffirmativeMoveInput && bVelocityDirection)
|
||||
{
|
||||
if (const FGMS_VelocityDirectionSetting_RateBased* Setting = GetControlSetting()->VelocityDirectionSetting.GetPtr<FGMS_VelocityDirectionSetting_RateBased>())
|
||||
{
|
||||
const float DeltaSeconds = DeltaMs * 0.001f;
|
||||
CharacterInputs.OrientationIntent = AdjustOrientationIntent(DeltaSeconds, MoverComponent->GetTargetOrientation().Vector());
|
||||
}
|
||||
else if (bMaintainLastInputOrientation)
|
||||
{
|
||||
// There is no movement intent, so use the last-known affirmative move input
|
||||
CharacterInputs.OrientationIntent = LastAffirmativeMoveInput;
|
||||
}
|
||||
}
|
||||
if (bHasAffirmativeMoveInput && bVelocityDirection)
|
||||
{
|
||||
if (const FGMS_VelocityDirectionSetting_RateBased* Setting = GetControlSetting()->VelocityDirectionSetting.GetPtr<FGMS_VelocityDirectionSetting_RateBased>())
|
||||
{
|
||||
const float DeltaSeconds = DeltaMs * 0.001f;
|
||||
CharacterInputs.OrientationIntent = AdjustOrientationIntent(DeltaSeconds, MoverComponent->GetTargetOrientation().Vector());
|
||||
}
|
||||
else
|
||||
{
|
||||
// set the intent to the actors movement direction
|
||||
CharacterInputs.OrientationIntent = CharacterInputs.GetMoveInput().GetSafeNormal();
|
||||
}
|
||||
}
|
||||
if (!bHasAffirmativeMoveInput && bViewDirection)
|
||||
{
|
||||
if (GetControlSetting()->ViewDirectionSetting.Get().bEnableRotationWhenNotMoving)
|
||||
{
|
||||
// set intent to the the control rotation - often a player's camera rotation
|
||||
CharacterInputs.OrientationIntent = CharacterInputs.ControlRotation.Vector().GetSafeNormal();
|
||||
}
|
||||
else
|
||||
{
|
||||
const float DeltaSeconds = DeltaMs * 0.001f;
|
||||
CharacterInputs.OrientationIntent = AdjustOrientationIntent(DeltaSeconds, MoverComponent->GetTargetOrientation().Vector());
|
||||
}
|
||||
}
|
||||
if (bHasAffirmativeMoveInput && bViewDirection)
|
||||
{
|
||||
// set intent to the control rotation - often a player's camera rotation
|
||||
CharacterInputs.OrientationIntent = CharacterInputs.ControlRotation.Vector().GetSafeNormal();
|
||||
}
|
||||
}
|
||||
|
||||
if (bHasAffirmativeMoveInput)
|
||||
{
|
||||
LastAffirmativeMoveInput = CharacterInputs.GetMoveInput();
|
||||
}
|
||||
|
||||
if (bShouldRemainVertical)
|
||||
{
|
||||
// canceling out any z intent if the actor is supposed to remain vertical
|
||||
CharacterInputs.OrientationIntent = CharacterInputs.OrientationIntent.GetSafeNormal2D();
|
||||
}
|
||||
|
||||
CharacterInputs.bIsJumpPressed = bIsJumpPressed;
|
||||
CharacterInputs.bIsJumpJustPressed = bIsJumpJustPressed;
|
||||
|
||||
if (bShouldToggleFlying)
|
||||
{
|
||||
if (!bIsFlyingActive)
|
||||
{
|
||||
CharacterInputs.SuggestedMovementMode = DefaultModeNames::Flying;
|
||||
}
|
||||
else
|
||||
{
|
||||
CharacterInputs.SuggestedMovementMode = DefaultModeNames::Falling;
|
||||
}
|
||||
|
||||
bIsFlyingActive = !bIsFlyingActive;
|
||||
}
|
||||
else
|
||||
{
|
||||
CharacterInputs.SuggestedMovementMode = NAME_None;
|
||||
}
|
||||
|
||||
// Convert inputs to be relative to the current movement base (depending on options and state)
|
||||
CharacterInputs.bUsingMovementBase = false;
|
||||
|
||||
if (bUseBaseRelativeMovement)
|
||||
{
|
||||
if (const UCharacterMoverComponent* MoverComp = OwnerPawn->GetComponentByClass<UCharacterMoverComponent>())
|
||||
{
|
||||
if (UPrimitiveComponent* MovementBasePtr = MoverComp->GetMovementBase())
|
||||
{
|
||||
FName MovementBaseBoneName = MoverComp->GetMovementBaseBoneName();
|
||||
|
||||
FVector RelativeMoveInput, RelativeOrientDir;
|
||||
|
||||
UBasedMovementUtils::TransformWorldDirectionToBased(MovementBasePtr, MovementBaseBoneName, CharacterInputs.GetMoveInput(), RelativeMoveInput);
|
||||
UBasedMovementUtils::TransformWorldDirectionToBased(MovementBasePtr, MovementBaseBoneName, CharacterInputs.OrientationIntent, RelativeOrientDir);
|
||||
|
||||
CharacterInputs.SetMoveInput(CharacterInputs.GetMoveInputType(), RelativeMoveInput);
|
||||
CharacterInputs.OrientationIntent = RelativeOrientDir;
|
||||
|
||||
CharacterInputs.bUsingMovementBase = true;
|
||||
CharacterInputs.MovementBase = MovementBasePtr;
|
||||
CharacterInputs.MovementBaseBoneName = MovementBaseBoneName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear/consume temporal movement inputs. We are not consuming others in the event that the game world is ticking at a lower rate than the Mover simulation.
|
||||
// In that case, we want most input to carry over between simulation frames.
|
||||
{
|
||||
bIsJumpJustPressed = false;
|
||||
bShouldToggleFlying = false;
|
||||
}
|
||||
|
||||
return InputCmdResult;
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
EDataValidationResult UGMS_MoverMovementSystemComponent::IsDataValid(class FDataValidationContext& Context) const
|
||||
{
|
||||
// if (IsTemplate() && InputProducerClass.IsNull())
|
||||
// {
|
||||
// Context.AddError(FText::FromString("Input Producer Class is required!"));
|
||||
// return EDataValidationResult::Invalid;
|
||||
// }
|
||||
return Super::IsDataValid(Context);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#include "GenericMovementSystem.h"
|
||||
|
||||
#include "Utility/GMS_Log.h"
|
||||
#if ALLOW_CONSOLE
|
||||
#include "Engine/Console.h"
|
||||
#endif
|
||||
|
||||
#if WITH_EDITOR
|
||||
#include "MessageLogModule.h"
|
||||
#endif
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FGenericMovementSystemModule"
|
||||
|
||||
void FGenericMovementSystemModule::StartupModule()
|
||||
{
|
||||
#if ALLOW_CONSOLE
|
||||
UConsole::RegisterConsoleAutoCompleteEntries.AddRaw(this, &FGenericMovementSystemModule::Console_OnRegisterAutoCompleteEntries);
|
||||
#endif
|
||||
|
||||
#if WITH_EDITOR
|
||||
// Register dedicated message log category for GMS.
|
||||
auto& MessageLog{FModuleManager::LoadModuleChecked<FMessageLogModule>(FName{TEXTVIEW("MessageLog")})};
|
||||
|
||||
FMessageLogInitializationOptions MessageLogOptions;
|
||||
MessageLogOptions.bShowFilters = true;
|
||||
MessageLogOptions.bAllowClear = true;
|
||||
MessageLogOptions.bDiscardDuplicates = true;
|
||||
|
||||
MessageLog.RegisterLogListing(GMSLog::MessageLogName, LOCTEXT("MessageLogLabel", "GMS"), MessageLogOptions);
|
||||
#endif
|
||||
}
|
||||
|
||||
void FGenericMovementSystemModule::ShutdownModule()
|
||||
{
|
||||
// This function may be called during shutdown to clean up your module. For modules that support dynamic reloading,
|
||||
// we call this function before unloading the module.
|
||||
}
|
||||
|
||||
|
||||
#if ALLOW_CONSOLE
|
||||
void FGenericMovementSystemModule::Console_OnRegisterAutoCompleteEntries(TArray<FAutoCompleteCommand>& AutoCompleteCommands)
|
||||
{
|
||||
const auto CommandColor{GetDefault<UConsoleSettings>()->AutoCompleteCommandColor};
|
||||
|
||||
auto* Command{&AutoCompleteCommands.AddDefaulted_GetRef()};
|
||||
Command->Command = FString{TEXTVIEW("Stat GMS")};
|
||||
Command->Desc = FString{TEXTVIEW("Displays GMS performance statistics.")};
|
||||
Command->Color = CommandColor;
|
||||
|
||||
|
||||
// Visual debugging will continue in 1.6
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(FGenericMovementSystemModule, GenericMovementSystem)
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,478 @@
|
||||
// // Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
//
|
||||
//
|
||||
// #include "Movement/GMS_CharacterMovementComponent.h"
|
||||
//
|
||||
// #include "GMS_CharacterMovementSystemComponent.h"
|
||||
// #include "GMS_MovementSystemComponent.h"
|
||||
// #include "GameFramework/Character.h"
|
||||
// #include "Locomotions/GMS_MainAnimInstance.h"
|
||||
// #include "Settings/GMS_SettingObjectLibrary.h"
|
||||
// #include "Utility/GMS_Constants.h"
|
||||
// #include "Utility/GMS_Log.h"
|
||||
// #include "Utility/GMS_Math.h"
|
||||
//
|
||||
// #include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_CharacterMovementComponent)
|
||||
//
|
||||
// UGMS_CharacterMovementComponent::UGMS_CharacterMovementComponent(const FObjectInitializer& ObjectInitializer): Super(ObjectInitializer)
|
||||
// {
|
||||
// }
|
||||
//
|
||||
// void UGMS_CharacterMovementComponent::InitializeComponent()
|
||||
// {
|
||||
// Super::InitializeComponent();
|
||||
// if (CharacterOwner)
|
||||
// {
|
||||
// UGMS_CharacterMovementSystemComponent* NewMovementSystem = CharacterOwner->FindComponentByClass<UGMS_CharacterMovementSystemComponent>();
|
||||
// if (NewMovementSystem == nullptr)
|
||||
// {
|
||||
// GMS_CLOG(Warning, "Requires GMS Character Movement System Component to function!")
|
||||
// return;
|
||||
// }
|
||||
// MovementSystem = NewMovementSystem;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// void UGMS_CharacterMovementComponent::SetUpdatedComponent(USceneComponent* NewUpdatedComponent)
|
||||
// {
|
||||
// Super::SetUpdatedComponent(NewUpdatedComponent);
|
||||
// if (CharacterOwner)
|
||||
// {
|
||||
// UGMS_CharacterMovementSystemComponent* NewMovementSystem = CharacterOwner->FindComponentByClass<UGMS_CharacterMovementSystemComponent>();
|
||||
// if (NewMovementSystem == nullptr)
|
||||
// {
|
||||
// GMS_CLOG(Warning, "Requires GMS Character Movement System Component to function!")
|
||||
// return;
|
||||
// }
|
||||
// MovementSystem = NewMovementSystem;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// bool UGMS_CharacterMovementComponent::HasValidData() const
|
||||
// {
|
||||
// return Super::HasValidData() && IsValid(MovementSystem);
|
||||
// }
|
||||
//
|
||||
// void UGMS_CharacterMovementComponent::TickCharacterPose(float DeltaTime)
|
||||
// {
|
||||
// Super::TickCharacterPose(DeltaTime);
|
||||
// }
|
||||
//
|
||||
// void UGMS_CharacterMovementComponent::PhysicsRotation(float DeltaTime)
|
||||
// {
|
||||
// if (bUseNativeRotation)
|
||||
// {
|
||||
// Super::PhysicsRotation(DeltaTime);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// GMS_PhysicsRotation(DeltaTime);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// void UGMS_CharacterMovementComponent::GMS_TurnToDesiredRotation(const FRotator& CurrentRotation, FRotator DesiredRotation, FRotator DeltaRot)
|
||||
// {
|
||||
// const bool bWantsToBeVertical = ShouldRemainVertical();
|
||||
//
|
||||
// if (bWantsToBeVertical)
|
||||
// {
|
||||
// if (HasCustomGravity())
|
||||
// {
|
||||
// FRotator GravityRelativeDesiredRotation = (GetWorldToGravityTransform() * DesiredRotation.Quaternion()).Rotator();
|
||||
// GravityRelativeDesiredRotation.Pitch = 0.f;
|
||||
// GravityRelativeDesiredRotation.Yaw = FRotator::NormalizeAxis(GravityRelativeDesiredRotation.Yaw);
|
||||
// GravityRelativeDesiredRotation.Roll = 0.f;
|
||||
// DesiredRotation = (GetWorldToGravityTransform() * GravityRelativeDesiredRotation.Quaternion()).Rotator();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// DesiredRotation.Pitch = 0.f;
|
||||
// DesiredRotation.Yaw = FRotator::NormalizeAxis(DesiredRotation.Yaw);
|
||||
// DesiredRotation.Roll = 0.f;
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// DesiredRotation.Normalize();
|
||||
// }
|
||||
//
|
||||
// // Accumulate a desired new rotation.
|
||||
// constexpr float AngleTolerance = 1e-3f;
|
||||
//
|
||||
// if (!CurrentRotation.Equals(DesiredRotation, AngleTolerance))
|
||||
// {
|
||||
// // If we'd be prevented from becoming vertical, override the non-yaw rotation rates to allow the character to snap upright
|
||||
//
|
||||
// static bool PreventNonVerticalOrientationBlock = true;
|
||||
// if (PreventNonVerticalOrientationBlock && bWantsToBeVertical)
|
||||
// {
|
||||
// if (FMath::IsNearlyZero(DeltaRot.Pitch))
|
||||
// {
|
||||
// DeltaRot.Pitch = 360.0;
|
||||
// }
|
||||
// if (FMath::IsNearlyZero(DeltaRot.Roll))
|
||||
// {
|
||||
// DeltaRot.Roll = 360.0;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (HasCustomGravity())
|
||||
// {
|
||||
// FRotator GravityRelativeCurrentRotation = (GetWorldToGravityTransform() * CurrentRotation.Quaternion()).Rotator();
|
||||
// FRotator GravityRelativeDesiredRotation = (GetWorldToGravityTransform() * DesiredRotation.Quaternion()).Rotator();
|
||||
//
|
||||
// // PITCH
|
||||
// if (!FMath::IsNearlyEqual(GravityRelativeCurrentRotation.Pitch, GravityRelativeDesiredRotation.Pitch, AngleTolerance))
|
||||
// {
|
||||
// // GravityRelativeDesiredRotation.Pitch = UGMS_Math::ExponentialDecayAngle(GravityRelativeCurrentRotation.Pitch,
|
||||
// // GravityRelativeDesiredRotation.Pitch, DeltaTime, DeltaRot.Pitch);
|
||||
// GravityRelativeDesiredRotation.Pitch = FMath::FixedTurn(GravityRelativeCurrentRotation.Pitch, GravityRelativeDesiredRotation.Pitch, DeltaRot.Pitch);
|
||||
// }
|
||||
//
|
||||
// // YAW
|
||||
// if (!FMath::IsNearlyEqual(GravityRelativeCurrentRotation.Yaw, GravityRelativeDesiredRotation.Yaw, AngleTolerance))
|
||||
// {
|
||||
// // GravityRelativeDesiredRotation.Yaw = UGMS_Math::ExponentialDecayAngle(GravityRelativeCurrentRotation.Yaw,
|
||||
// // GravityRelativeDesiredRotation.Yaw, DeltaTime, DeltaRot.Yaw);
|
||||
// GravityRelativeDesiredRotation.Yaw = FMath::FixedTurn(GravityRelativeCurrentRotation.Yaw, GravityRelativeDesiredRotation.Yaw, DeltaRot.Yaw);
|
||||
// }
|
||||
//
|
||||
// // ROLL
|
||||
// if (!FMath::IsNearlyEqual(GravityRelativeCurrentRotation.Roll, GravityRelativeDesiredRotation.Roll, AngleTolerance))
|
||||
// {
|
||||
// // GravityRelativeDesiredRotation.Roll = UGMS_Math::ExponentialDecayAngle(GravityRelativeCurrentRotation.Roll,
|
||||
// // GravityRelativeDesiredRotation.Roll, DeltaTime, DeltaRot.Roll);
|
||||
// GravityRelativeDesiredRotation.Roll = FMath::FixedTurn(GravityRelativeCurrentRotation.Roll, GravityRelativeDesiredRotation.Roll, DeltaRot.Roll);
|
||||
// }
|
||||
//
|
||||
// DesiredRotation = (GetWorldToGravityTransform() * GravityRelativeDesiredRotation.Quaternion()).Rotator();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// // PITCH
|
||||
// if (!FMath::IsNearlyEqual(CurrentRotation.Pitch, DesiredRotation.Pitch, AngleTolerance))
|
||||
// {
|
||||
// // DesiredRotation.Pitch = UGMS_Math::ExponentialDecayAngle(CurrentRotation.Pitch, DesiredRotation.Pitch, DeltaTime, DeltaRot.Pitch);
|
||||
// DesiredRotation.Pitch = FMath::FixedTurn(CurrentRotation.Pitch, DesiredRotation.Pitch, DeltaRot.Pitch);
|
||||
// }
|
||||
//
|
||||
// // YAW
|
||||
// if (!FMath::IsNearlyEqual(CurrentRotation.Yaw, DesiredRotation.Yaw, AngleTolerance))
|
||||
// {
|
||||
// // DesiredRotation.Yaw = UGMS_Math::ExponentialDecayAngle(CurrentRotation.Yaw, DesiredRotation.Yaw, DeltaTime, DeltaRot.Yaw);
|
||||
// DesiredRotation.Yaw = FMath::FixedTurn(CurrentRotation.Yaw, DesiredRotation.Yaw, DeltaRot.Yaw);
|
||||
// }
|
||||
//
|
||||
// // ROLL
|
||||
// if (!FMath::IsNearlyEqual(CurrentRotation.Roll, DesiredRotation.Roll, AngleTolerance))
|
||||
// {
|
||||
// // DesiredRotation.Roll = UGMS_Math::ExponentialDecayAngle(CurrentRotation.Roll, DesiredRotation.Roll, DeltaTime, DeltaRot.Yaw);
|
||||
// DesiredRotation.Roll = FMath::FixedTurn(CurrentRotation.Roll, DesiredRotation.Roll, DeltaRot.Roll);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Set the new rotation.
|
||||
// DesiredRotation.DiagnosticCheckNaN(TEXT("CharacterMovementComponent::PhysicsRotation(): DesiredRotation"));
|
||||
// MoveUpdatedComponent(FVector::ZeroVector, DesiredRotation, /*bSweep*/ false);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// void UGMS_CharacterMovementComponent::GMS_TurnToDesiredRotationWithRotationRate(const FRotator& CurrentRotation, FRotator DesiredRotation, FRotator DeltaRot)
|
||||
// {
|
||||
// const bool bWantsToBeVertical = ShouldRemainVertical();
|
||||
//
|
||||
// if (bWantsToBeVertical)
|
||||
// {
|
||||
// if (HasCustomGravity())
|
||||
// {
|
||||
// FRotator GravityRelativeDesiredRotation = (GetWorldToGravityTransform() * DesiredRotation.Quaternion()).Rotator();
|
||||
// GravityRelativeDesiredRotation.Pitch = 0.f;
|
||||
// GravityRelativeDesiredRotation.Yaw = FRotator::NormalizeAxis(GravityRelativeDesiredRotation.Yaw);
|
||||
// GravityRelativeDesiredRotation.Roll = 0.f;
|
||||
// DesiredRotation = (GetWorldToGravityTransform() * GravityRelativeDesiredRotation.Quaternion()).Rotator();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// DesiredRotation.Pitch = 0.f;
|
||||
// DesiredRotation.Yaw = FRotator::NormalizeAxis(DesiredRotation.Yaw);
|
||||
// DesiredRotation.Roll = 0.f;
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// DesiredRotation.Normalize();
|
||||
// }
|
||||
//
|
||||
// // Accumulate a desired new rotation.
|
||||
// constexpr float AngleTolerance = 1e-3f;
|
||||
//
|
||||
// if (!CurrentRotation.Equals(DesiredRotation, AngleTolerance))
|
||||
// {
|
||||
// // If we'd be prevented from becoming vertical, override the non-yaw rotation rates to allow the character to snap upright
|
||||
//
|
||||
// static bool PreventNonVerticalOrientationBlock = true;
|
||||
// if (PreventNonVerticalOrientationBlock && bWantsToBeVertical)
|
||||
// {
|
||||
// if (FMath::IsNearlyZero(DeltaRot.Pitch))
|
||||
// {
|
||||
// DeltaRot.Pitch = 360.0;
|
||||
// }
|
||||
// if (FMath::IsNearlyZero(DeltaRot.Roll))
|
||||
// {
|
||||
// DeltaRot.Roll = 360.0;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (HasCustomGravity())
|
||||
// {
|
||||
// FRotator GravityRelativeCurrentRotation = (GetWorldToGravityTransform() * CurrentRotation.Quaternion()).Rotator();
|
||||
// FRotator GravityRelativeDesiredRotation = (GetWorldToGravityTransform() * DesiredRotation.Quaternion()).Rotator();
|
||||
//
|
||||
// // PITCH
|
||||
// if (!FMath::IsNearlyEqual(GravityRelativeCurrentRotation.Pitch, GravityRelativeDesiredRotation.Pitch, AngleTolerance))
|
||||
// {
|
||||
// GravityRelativeDesiredRotation.Pitch = FMath::FixedTurn(GravityRelativeCurrentRotation.Pitch, GravityRelativeDesiredRotation.Pitch, DeltaRot.Pitch);
|
||||
// }
|
||||
//
|
||||
// // YAW
|
||||
// if (!FMath::IsNearlyEqual(GravityRelativeCurrentRotation.Yaw, GravityRelativeDesiredRotation.Yaw, AngleTolerance))
|
||||
// {
|
||||
// GravityRelativeDesiredRotation.Yaw = FMath::FixedTurn(GravityRelativeCurrentRotation.Yaw, GravityRelativeDesiredRotation.Yaw, DeltaRot.Yaw);
|
||||
// }
|
||||
//
|
||||
// // ROLL
|
||||
// if (!FMath::IsNearlyEqual(GravityRelativeCurrentRotation.Roll, GravityRelativeDesiredRotation.Roll, AngleTolerance))
|
||||
// {
|
||||
// GravityRelativeDesiredRotation.Roll = FMath::FixedTurn(GravityRelativeCurrentRotation.Roll, GravityRelativeDesiredRotation.Roll, DeltaRot.Roll);
|
||||
// }
|
||||
//
|
||||
// DesiredRotation = (GetWorldToGravityTransform() * GravityRelativeDesiredRotation.Quaternion()).Rotator();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// // PITCH
|
||||
// if (!FMath::IsNearlyEqual(CurrentRotation.Pitch, DesiredRotation.Pitch, AngleTolerance))
|
||||
// {
|
||||
// DesiredRotation.Pitch = FMath::FixedTurn(CurrentRotation.Pitch, DesiredRotation.Pitch, DeltaRot.Pitch);
|
||||
// }
|
||||
//
|
||||
// // YAW
|
||||
// if (!FMath::IsNearlyEqual(CurrentRotation.Yaw, DesiredRotation.Yaw, AngleTolerance))
|
||||
// {
|
||||
// DesiredRotation.Yaw = FMath::FixedTurn(CurrentRotation.Yaw, DesiredRotation.Yaw, DeltaRot.Yaw);
|
||||
// }
|
||||
//
|
||||
// // ROLL
|
||||
// if (!FMath::IsNearlyEqual(CurrentRotation.Roll, DesiredRotation.Roll, AngleTolerance))
|
||||
// {
|
||||
// DesiredRotation.Roll = FMath::FixedTurn(CurrentRotation.Roll, DesiredRotation.Roll, DeltaRot.Roll);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Set the new rotation.
|
||||
// DesiredRotation.DiagnosticCheckNaN(TEXT("CharacterMovementComponent::PhysicsRotation(): DesiredRotation"));
|
||||
// MoveUpdatedComponent(FVector::ZeroVector, DesiredRotation, /*bSweep*/ false);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// void UGMS_CharacterMovementComponent::GMS_PhysicsRotation_Implementation(float DeltaTime)
|
||||
// {
|
||||
// if (!(bOrientRotationToMovement || bUseControllerDesiredRotation))
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// if (!HasValidData() || (!CharacterOwner->Controller && !bRunPhysicsWithNoController))
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// FRotator CurrentRotation = UpdatedComponent->GetComponentRotation(); // Normalized
|
||||
// CurrentRotation.DiagnosticCheckNaN(TEXT("CharacterMovementComponent::PhysicsRotation(): CurrentRotation"));
|
||||
//
|
||||
// FRotator DeltaRot = GetDeltaRotation(DeltaTime);
|
||||
// DeltaRot.DiagnosticCheckNaN(TEXT("CharacterMovementComponent::PhysicsRotation(): GetDeltaRotation"));
|
||||
//
|
||||
// FRotator DesiredRotation = CurrentRotation;
|
||||
// if (bOrientRotationToMovement)
|
||||
// {
|
||||
// DesiredRotation = GMS_ComputeOrientToDesiredMovementRotation(CurrentRotation, DeltaTime, DeltaRot);
|
||||
// }
|
||||
// else if (CharacterOwner->Controller && bUseControllerDesiredRotation)
|
||||
// {
|
||||
// DesiredRotation = CharacterOwner->Controller->GetDesiredRotation();
|
||||
// // DesiredRotation = MovementSystem->GetViewState().Rotation;
|
||||
// }
|
||||
// else if (!CharacterOwner->Controller && bRunPhysicsWithNoController && bUseControllerDesiredRotation)
|
||||
// {
|
||||
// if (AController* ControllerOwner = Cast<AController>(CharacterOwner->GetOwner()))
|
||||
// {
|
||||
// DesiredRotation = ControllerOwner->GetDesiredRotation();
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// const bool bWantsToBeVertical = ShouldRemainVertical();
|
||||
//
|
||||
// if (bWantsToBeVertical)
|
||||
// {
|
||||
// if (HasCustomGravity())
|
||||
// {
|
||||
// FRotator GravityRelativeDesiredRotation = (GetGravityToWorldTransform() * DesiredRotation.Quaternion()).Rotator();
|
||||
// GravityRelativeDesiredRotation.Pitch = 0.f;
|
||||
// GravityRelativeDesiredRotation.Yaw = FRotator::NormalizeAxis(GravityRelativeDesiredRotation.Yaw);
|
||||
// GravityRelativeDesiredRotation.Roll = 0.f;
|
||||
// DesiredRotation = (GetWorldToGravityTransform() * GravityRelativeDesiredRotation.Quaternion()).Rotator();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// DesiredRotation.Pitch = 0.f;
|
||||
// DesiredRotation.Yaw = FRotator::NormalizeAxis(DesiredRotation.Yaw);
|
||||
// DesiredRotation.Roll = 0.f;
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// DesiredRotation.Normalize();
|
||||
// }
|
||||
//
|
||||
// // Accumulate a desired new rotation.
|
||||
// constexpr float AngleTolerance = 1e-3f;
|
||||
//
|
||||
// if (!CurrentRotation.Equals(DesiredRotation, AngleTolerance))
|
||||
// {
|
||||
// // If we'd be prevented from becoming vertical, override the non-yaw rotation rates to allow the character to snap upright
|
||||
// if (true && bWantsToBeVertical)
|
||||
// {
|
||||
// if (FMath::IsNearlyZero(DeltaRot.Pitch))
|
||||
// {
|
||||
// DeltaRot.Pitch = 360.0;
|
||||
// }
|
||||
// if (FMath::IsNearlyZero(DeltaRot.Roll))
|
||||
// {
|
||||
// DeltaRot.Roll = 360.0;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (HasCustomGravity())
|
||||
// {
|
||||
// FRotator GravityRelativeCurrentRotation = (GetGravityToWorldTransform() * CurrentRotation.Quaternion()).Rotator();
|
||||
// FRotator GravityRelativeDesiredRotation = (GetGravityToWorldTransform() * DesiredRotation.Quaternion()).Rotator();
|
||||
//
|
||||
// // PITCH
|
||||
// if (!FMath::IsNearlyEqual(GravityRelativeCurrentRotation.Pitch, GravityRelativeDesiredRotation.Pitch, AngleTolerance))
|
||||
// {
|
||||
// GravityRelativeDesiredRotation.Pitch = FMath::FixedTurn(GravityRelativeCurrentRotation.Pitch, GravityRelativeDesiredRotation.Pitch, DeltaRot.Pitch);
|
||||
// }
|
||||
//
|
||||
// // YAW
|
||||
// if (!FMath::IsNearlyEqual(GravityRelativeCurrentRotation.Yaw, GravityRelativeDesiredRotation.Yaw, AngleTolerance))
|
||||
// {
|
||||
// GravityRelativeDesiredRotation.Yaw = FMath::FixedTurn(GravityRelativeCurrentRotation.Yaw, GravityRelativeDesiredRotation.Yaw, DeltaRot.Yaw);
|
||||
// }
|
||||
//
|
||||
// // ROLL
|
||||
// if (!FMath::IsNearlyEqual(GravityRelativeCurrentRotation.Roll, GravityRelativeDesiredRotation.Roll, AngleTolerance))
|
||||
// {
|
||||
// GravityRelativeDesiredRotation.Roll = FMath::FixedTurn(GravityRelativeCurrentRotation.Roll, GravityRelativeDesiredRotation.Roll, DeltaRot.Roll);
|
||||
// }
|
||||
//
|
||||
// DesiredRotation = (GetWorldToGravityTransform() * GravityRelativeDesiredRotation.Quaternion()).Rotator();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// // PITCH
|
||||
// if (!FMath::IsNearlyEqual(CurrentRotation.Pitch, DesiredRotation.Pitch, AngleTolerance))
|
||||
// {
|
||||
// DesiredRotation.Pitch = FMath::FixedTurn(CurrentRotation.Pitch, DesiredRotation.Pitch, DeltaRot.Pitch);
|
||||
// }
|
||||
//
|
||||
// // YAW
|
||||
// if (!FMath::IsNearlyEqual(CurrentRotation.Yaw, DesiredRotation.Yaw, AngleTolerance))
|
||||
// {
|
||||
// DesiredRotation.Yaw = FMath::FixedTurn(CurrentRotation.Yaw, DesiredRotation.Yaw, DeltaRot.Yaw);
|
||||
// }
|
||||
//
|
||||
// // ROLL
|
||||
// if (!FMath::IsNearlyEqual(CurrentRotation.Roll, DesiredRotation.Roll, AngleTolerance))
|
||||
// {
|
||||
// DesiredRotation.Roll = FMath::FixedTurn(CurrentRotation.Roll, DesiredRotation.Roll, DeltaRot.Roll);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Set the new rotation.
|
||||
// DesiredRotation.DiagnosticCheckNaN(TEXT("CharacterMovementComponent::PhysicsRotation(): DesiredRotation"));
|
||||
// MoveUpdatedComponent(FVector::ZeroVector, DesiredRotation, /*bSweep*/ false);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// FRotator UGMS_CharacterMovementComponent::GMS_ComputeOrientToDesiredViewRotation_Implementation(const FRotator& CurrentRotation, float DeltaTime, FRotator& DeltaRotation) const
|
||||
// {
|
||||
// check(MovementSystem->GetControlSetting());
|
||||
//
|
||||
// bool bMoving = MovementSystem->GetLocomotionState().bMoving;
|
||||
//
|
||||
// DeltaRotation.Yaw = DeltaRotation.Pitch = DeltaRotation.Roll = MovementSystem->GetControlSetting()->ViewDirectionSetting.Get().RotationInterpolationSpeed;
|
||||
//
|
||||
// float DeltaYawAngle{0.0f};
|
||||
// if (!bMoving && HasAnimationRotationDeltaYawAngle(DeltaTime, DeltaYawAngle))
|
||||
// {
|
||||
// auto NewRotation{CurrentRotation};
|
||||
// NewRotation.Yaw += DeltaYawAngle;
|
||||
// DeltaRotation.Yaw = -1;
|
||||
// return NewRotation;
|
||||
// }
|
||||
//
|
||||
// FRotator ControllerDesiredRotation = CharacterOwner->Controller->GetDesiredRotation();
|
||||
//
|
||||
// if (const FGMS_ViewDirectionSetting_Default* Setting = MovementSystem->GetControlSetting()->ViewDirectionSetting.GetPtr<FGMS_ViewDirectionSetting_Default>())
|
||||
// {
|
||||
// if (bMoving || Setting->bEnableRotationWhenNotMoving)
|
||||
// {
|
||||
// auto NewRotation{CurrentRotation};
|
||||
// NewRotation.Yaw = ControllerDesiredRotation.Yaw;
|
||||
// return NewRotation;
|
||||
// }
|
||||
// return CurrentRotation;
|
||||
// }
|
||||
//
|
||||
// if (const FGMS_ViewDirectionSetting_Aiming* Setting = MovementSystem->GetControlSetting()->ViewDirectionSetting.GetPtr<FGMS_ViewDirectionSetting_Aiming>())
|
||||
// {
|
||||
// if (!bMoving && Setting->bEnableRotationWhenNotMoving)
|
||||
// {
|
||||
// auto NewRotation{CurrentRotation};
|
||||
// NewRotation.Yaw = ControllerDesiredRotation.Yaw;
|
||||
// return NewRotation;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return CurrentRotation;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// bool UGMS_CharacterMovementComponent::HasAnimationRotationDeltaYawAngle_Implementation(float DeltaTime, float& OutDeltaYawAngle) const
|
||||
// {
|
||||
// UAnimInstance* AnimInstance = CharacterOwner->GetMesh()->GetAnimInstance();
|
||||
// if (UGMS_MainAnimInstance* AnimInst = Cast<UGMS_MainAnimInstance>(AnimInstance))
|
||||
// {
|
||||
// if (AnimInst->GetOffsetRootBoneRotationMode() != EOffsetRootBoneMode::Release)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// const float CurveValue = AnimInstance->GetCurveValue(UGMS_Constants::RotationYawSpeedCurveName());
|
||||
//
|
||||
// OutDeltaYawAngle = CurveValue * DeltaTime;
|
||||
//
|
||||
// return FMath::Abs(OutDeltaYawAngle) > UE_SMALL_NUMBER;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// FRotator UGMS_CharacterMovementComponent::GMS_ComputeOrientToDesiredMovementRotation_Implementation(const FRotator& CurrentRotation, float DeltaTime, FRotator& DeltaRotation) const
|
||||
// {
|
||||
// return Super::ComputeOrientToMovementRotation(CurrentRotation, DeltaTime, DeltaRotation);
|
||||
// }
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Mover/Flying/GMS_FlyingMode.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_FlyingMode)
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Mover/GMS_MoverSettingObjectLibrary.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_MoverSettingObjectLibrary)
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Mover/GMS_MoverStructLibrary.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_MoverStructLibrary)
|
||||
@@ -0,0 +1,150 @@
|
||||
// // Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
//
|
||||
//
|
||||
// #include "Mover/Modifers/GMS_MovementStateModifer.h"
|
||||
//
|
||||
// #include "MoverComponent.h"
|
||||
// #include "DefaultMovementSet/Settings/CommonLegacyMovementSettings.h"
|
||||
// #include "MoveLibrary/MovementUtils.h"
|
||||
// #include "Mover/GMS_MoverStructLibrary.h"
|
||||
//
|
||||
//
|
||||
// FGMS_MovementStateModifier::FGMS_MovementStateModifier()
|
||||
// {
|
||||
// DurationMs = -1.0f;
|
||||
// }
|
||||
//
|
||||
// void FGMS_MovementStateModifier::OnStart(UMoverComponent* MoverComp, const FMoverTimeStep& TimeStep, const FMoverSyncState& SyncState, const FMoverAuxStateContext& AuxState)
|
||||
// {
|
||||
// const FGMS_MoverMovementControlInputs* Inputs = SyncState.SyncStateCollection.FindMutableDataByType<FGMS_MoverMovementControlInputs>();
|
||||
//
|
||||
// if (UStanceSettings* StanceSettings = MoverComp->FindSharedSettings_Mutable<UStanceSettings>())
|
||||
// {
|
||||
// if (const UCapsuleComponent* CapsuleComponent = Cast<UCapsuleComponent>(MoverComp->GetUpdatedComponent()))
|
||||
// {
|
||||
// float OldHalfHeight = CapsuleComponent->GetScaledCapsuleHalfHeight();
|
||||
// float NewHalfHeight = 0;
|
||||
// float NewEyeHeight = 0;
|
||||
//
|
||||
// switch (ActiveStance)
|
||||
// {
|
||||
// default:
|
||||
// case EStanceMode::Crouch:
|
||||
// NewHalfHeight = StanceSettings->CrouchHalfHeight;
|
||||
// NewEyeHeight = StanceSettings->CrouchedEyeHeight;
|
||||
// break;
|
||||
//
|
||||
// // Prone isn't currently implemented
|
||||
// case EStanceMode::Prone:
|
||||
// UE_LOG(LogMover, Warning, TEXT("Stance got into prone stance - That stance is not currently implemented."));
|
||||
// // TODO: returning here so we don't apply any bad state to actor in case prone was set. Eventually, the return should be removed once prone is implemented properly
|
||||
// DurationMs = 0;
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// ApplyMovementSettings(MoverComp);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// void FGMS_MovementStateModifier::OnEnd(UMoverComponent* MoverComp, const FMoverTimeStep& TimeStep, const FMoverSyncState& SyncState, const FMoverAuxStateContext& AuxState)
|
||||
// {
|
||||
// const AActor* OwnerCDO = Cast<AActor>(MoverComp->GetOwner()->GetClass()->GetDefaultObject());
|
||||
//
|
||||
// if (UCapsuleComponent* CapsuleComponent = Cast<UCapsuleComponent>(MoverComp->GetUpdatedComponent()))
|
||||
// {
|
||||
// if (const UCapsuleComponent* OriginalCapsule = UMovementUtils::GetOriginalComponentType<UCapsuleComponent>(MoverComp->GetOwner()))
|
||||
// {
|
||||
// if (const APawn* OwnerCDOAsPawn = Cast<APawn>(OwnerCDO))
|
||||
// {
|
||||
// AdjustCapsule(MoverComp, CapsuleComponent->GetScaledCapsuleHalfHeight(), OriginalCapsule->GetScaledCapsuleHalfHeight(), OwnerCDOAsPawn->BaseEyeHeight);
|
||||
// RevertMovementSettings(MoverComp);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// void FGMS_MovementStateModifier::OnPreMovement(UMoverComponent* MoverComp, const FMoverTimeStep& TimeStep)
|
||||
// {
|
||||
// // TODO: Check for different inputs/state here and manage swapping between stances - use AdjustCapsule and Apply/Revert movement settings.
|
||||
//
|
||||
// // TODO: Prone isn't currently implemented - so we're just going to cancel the modifier if we got into that state
|
||||
// if (ActiveStance == EStanceMode::Prone)
|
||||
// {
|
||||
// UE_LOG(LogMover, Warning, TEXT("Stance got into prone stance - That stance is not currently implemented."));
|
||||
// DurationMs = 0;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// void FGMS_MovementStateModifier::OnPostMovement(UMoverComponent* MoverComp, const FMoverTimeStep& TimeStep, const FMoverSyncState& SyncState, const FMoverAuxStateContext& AuxState)
|
||||
// {
|
||||
// FMovementModifierBase::OnPostMovement(MoverComp, TimeStep, SyncState, AuxState);
|
||||
// }
|
||||
//
|
||||
// FMovementModifierBase* FGMS_MovementStateModifier::Clone() const
|
||||
// {
|
||||
// FGMS_MovementStateModifier* CopyPtr = new FGMS_MovementStateModifier(*this);
|
||||
// return CopyPtr;
|
||||
// }
|
||||
//
|
||||
// void FGMS_MovementStateModifier::NetSerialize(FArchive& Ar)
|
||||
// {
|
||||
// Super::NetSerialize(Ar);
|
||||
// }
|
||||
//
|
||||
// UScriptStruct* FGMS_MovementStateModifier::GetScriptStruct() const
|
||||
// {
|
||||
// return StaticStruct();
|
||||
// }
|
||||
//
|
||||
// FString FGMS_MovementStateModifier::ToSimpleString() const
|
||||
// {
|
||||
// return FString::Printf(TEXT("Stance Modifier"));
|
||||
// }
|
||||
//
|
||||
// void FGMS_MovementStateModifier::AddReferencedObjects(FReferenceCollector& Collector)
|
||||
// {
|
||||
// Super::AddReferencedObjects(Collector);
|
||||
// }
|
||||
//
|
||||
//
|
||||
// void FGMS_MovementStateModifier::ApplyMovementSettings(UMoverComponent* MoverComp)
|
||||
// {
|
||||
// switch (ActiveStance)
|
||||
// {
|
||||
// default:
|
||||
// case EStanceMode::Crouch:
|
||||
// if (UStanceSettings* StanceSettings = MoverComp->FindSharedSettings_Mutable<UStanceSettings>())
|
||||
// {
|
||||
// // Update relevant movement settings
|
||||
// if (UCommonLegacyMovementSettings* MovementSettings = MoverComp->FindSharedSettings_Mutable<UCommonLegacyMovementSettings>())
|
||||
// {
|
||||
// MovementSettings->Acceleration = StanceSettings->CrouchingMaxAcceleration;
|
||||
// MovementSettings->MaxSpeed = StanceSettings->CrouchingMaxSpeed;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// break;
|
||||
//
|
||||
// // Prone isn't currently implemented properly so we're just doing nothing for now
|
||||
// case EStanceMode::Prone:
|
||||
// UE_LOG(LogMover, Warning, TEXT("Stance got into prone stance - That mode is not currently implemented fully."));
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// void FGMS_MovementStateModifier::RevertMovementSettings(UMoverComponent* MoverComp)
|
||||
// {
|
||||
// if (const UMoverComponent* CDOMoverComp = UMovementUtils::GetOriginalComponentType<UMoverComponent>(MoverComp->GetOwner()))
|
||||
// {
|
||||
// const UCommonLegacyMovementSettings* OriginalMovementSettings = CDOMoverComp->FindSharedSettings<UCommonLegacyMovementSettings>();
|
||||
// UCommonLegacyMovementSettings* MovementSettings = MoverComp->FindSharedSettings_Mutable<UCommonLegacyMovementSettings>();
|
||||
//
|
||||
// // Revert movement settings back to original settings
|
||||
// if (MovementSettings && OriginalMovementSettings)
|
||||
// {
|
||||
// MovementSettings->Acceleration = OriginalMovementSettings->Acceleration;
|
||||
// MovementSettings->MaxSpeed = OriginalMovementSettings->MaxSpeed;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Mover/Walking/GMS_WalkingMode.h"
|
||||
|
||||
#include "Mover/GMS_MoverSettingObjectLibrary.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_WalkingMode)
|
||||
|
||||
UGMS_WalkingMode::UGMS_WalkingMode(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
SharedSettingsClasses.Add(UGMS_MoverGroundedMovementSettings::StaticClass());
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Mover/Zipline/GMS_ZiplineInterface.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_ZiplineInterface)
|
||||
|
||||
// Add default functionality here for any IGMS_ZiplineInterface functions that are not pure virtual.
|
||||
@@ -0,0 +1,93 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Mover/Zipline/GMS_ZiplineModeTransition.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "DefaultMovementSet/CharacterMoverComponent.h"
|
||||
#include "Kismet/KismetSystemLibrary.h"
|
||||
#include "Mover/GMS_MoverStructLibrary.h"
|
||||
#include "Mover/Zipline/GMS_ZiplineInterface.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_ZiplineModeTransition)
|
||||
|
||||
// UGMS_ZiplineStartTransition //////////////////////////////
|
||||
|
||||
UGMS_ZiplineStartTransition::UGMS_ZiplineStartTransition(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
}
|
||||
|
||||
#if ENGINE_MINOR_VERSION >=6
|
||||
FTransitionEvalResult UGMS_ZiplineStartTransition::Evaluate_Implementation(const FSimulationTickParams& Params) const
|
||||
#else
|
||||
FTransitionEvalResult UGMS_ZiplineStartTransition::OnEvaluate(const FSimulationTickParams& Params) const
|
||||
#endif
|
||||
{
|
||||
FTransitionEvalResult EvalResult = FTransitionEvalResult::NoTransition;
|
||||
|
||||
UCharacterMoverComponent* MoverComp = Cast<UCharacterMoverComponent>(Params.MovingComps.MoverComponent.Get());
|
||||
|
||||
const FMoverSyncState& SyncState = Params.StartState.SyncState;
|
||||
|
||||
if (MoverComp && MoverComp->IsAirborne() && SyncState.MovementMode != ZipliningModeName)
|
||||
{
|
||||
if (const FGMS_MoverTagInputs* AbilityInputs = Params.StartState.InputCmd.InputCollection.FindDataByType<FGMS_MoverTagInputs>())
|
||||
{
|
||||
if (ZipliningInputTag.IsValid() && AbilityInputs->Tags.HasTagExact(ZipliningInputTag))
|
||||
{
|
||||
TArray<AActor*> OverlappingActors;
|
||||
MoverComp->GetOwner()->GetOverlappingActors(OUT OverlappingActors);
|
||||
|
||||
for (AActor* CandidateActor : OverlappingActors)
|
||||
{
|
||||
bool bIsZipline = UKismetSystemLibrary::DoesImplementInterface(CandidateActor, UGMS_ZiplineInterface::StaticClass());
|
||||
|
||||
if (bIsZipline)
|
||||
{
|
||||
EvalResult.NextMode = ZipliningModeName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return EvalResult;
|
||||
}
|
||||
|
||||
|
||||
// UGMS_ZiplineEndTransition //////////////////////////////
|
||||
|
||||
UGMS_ZiplineEndTransition::UGMS_ZiplineEndTransition(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
#if ENGINE_MINOR_VERSION >=6
|
||||
FTransitionEvalResult UGMS_ZiplineEndTransition::Evaluate_Implementation(const FSimulationTickParams& Params) const
|
||||
#else
|
||||
FTransitionEvalResult UGMS_ZiplineEndTransition::OnEvaluate(const FSimulationTickParams& Params) const
|
||||
#endif
|
||||
{
|
||||
FTransitionEvalResult EvalResult = FTransitionEvalResult::NoTransition;
|
||||
|
||||
if (const FCharacterDefaultInputs* DefaultInputs = Params.StartState.InputCmd.InputCollection.FindDataByType<FCharacterDefaultInputs>())
|
||||
{
|
||||
if (DefaultInputs->bIsJumpJustPressed)
|
||||
{
|
||||
EvalResult.NextMode = AutoExitToMode;
|
||||
}
|
||||
}
|
||||
|
||||
return EvalResult;
|
||||
}
|
||||
|
||||
#if ENGINE_MINOR_VERSION >=6
|
||||
void UGMS_ZiplineEndTransition::Trigger_Implementation(const FSimulationTickParams& Params)
|
||||
#else
|
||||
void UGMS_ZiplineEndTransition::OnTrigger(const FSimulationTickParams& Params)
|
||||
#endif
|
||||
{
|
||||
//TODO: create a small jump, using current directionality
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Mover/Zipline/GMS_ZipliningMode.h"
|
||||
|
||||
#include "MoverComponent.h"
|
||||
#include "DefaultMovementSet/Settings/CommonLegacyMovementSettings.h"
|
||||
#include "Kismet/KismetSystemLibrary.h"
|
||||
#include "MoveLibrary/MovementUtils.h"
|
||||
#include "Mover/Zipline/GMS_ZiplineInterface.h"
|
||||
#include "Mover/Zipline/GMS_ZiplineModeTransition.h"
|
||||
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_ZipliningMode)
|
||||
|
||||
|
||||
// FGMS_ZipliningState //////////////////////////////
|
||||
|
||||
FMoverDataStructBase* FGMS_ZipliningState::Clone() const
|
||||
{
|
||||
FGMS_ZipliningState* CopyPtr = new FGMS_ZipliningState(*this);
|
||||
return CopyPtr;
|
||||
}
|
||||
|
||||
bool FGMS_ZipliningState::NetSerialize(FArchive& Ar, UPackageMap* Map, bool& bOutSuccess)
|
||||
{
|
||||
Super::NetSerialize(Ar, Map, bOutSuccess);
|
||||
|
||||
Ar << ZiplineActor;
|
||||
Ar.SerializeBits(&bIsMovingAtoB, 1);
|
||||
|
||||
bOutSuccess = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void FGMS_ZipliningState::ToString(FAnsiStringBuilderBase& Out) const
|
||||
{
|
||||
Super::ToString(Out);
|
||||
|
||||
Out.Appendf("ZiplineActor: %s\n", *GetNameSafe(ZiplineActor));
|
||||
Out.Appendf("IsMovingAtoB: %d\n", bIsMovingAtoB);
|
||||
}
|
||||
|
||||
bool FGMS_ZipliningState::ShouldReconcile(const FMoverDataStructBase& AuthorityState) const
|
||||
{
|
||||
const FGMS_ZipliningState* AuthorityZiplineState = static_cast<const FGMS_ZipliningState*>(&AuthorityState);
|
||||
|
||||
return (ZiplineActor != AuthorityZiplineState->ZiplineActor) ||
|
||||
(bIsMovingAtoB != AuthorityZiplineState->bIsMovingAtoB);
|
||||
}
|
||||
|
||||
void FGMS_ZipliningState::Interpolate(const FMoverDataStructBase& From, const FMoverDataStructBase& To, float Pct)
|
||||
{
|
||||
const FGMS_ZipliningState* FromState = static_cast<const FGMS_ZipliningState*>(&From);
|
||||
const FGMS_ZipliningState* ToState = static_cast<const FGMS_ZipliningState*>(&To);
|
||||
|
||||
ZiplineActor = ToState->ZiplineActor;
|
||||
bIsMovingAtoB = ToState->bIsMovingAtoB;
|
||||
}
|
||||
|
||||
|
||||
// UGMS_ZipliningMode //////////////////////////////
|
||||
|
||||
UGMS_ZipliningMode::UGMS_ZipliningMode(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
{
|
||||
Transitions.Add(CreateDefaultSubobject<UGMS_ZiplineEndTransition>(TEXT("ZiplineEndTransition")));
|
||||
}
|
||||
|
||||
#if ENGINE_MINOR_VERSION >=6
|
||||
void UGMS_ZipliningMode::GenerateMove_Implementation(const FMoverTickStartData& StartState, const FMoverTimeStep& TimeStep, FProposedMove& OutProposedMove) const
|
||||
#else
|
||||
void UGMS_ZipliningMode::OnGenerateMove(const FMoverTickStartData& StartState, const FMoverTimeStep& TimeStep, FProposedMove& OutProposedMove) const
|
||||
#endif
|
||||
{
|
||||
UMoverComponent* MoverComp = GetMoverComponent();
|
||||
|
||||
// Ziplining is just following a path from A to B, so all movement is handled in OnSimulationTick
|
||||
OutProposedMove = FProposedMove();
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if ENGINE_MINOR_VERSION >=6
|
||||
void UGMS_ZipliningMode::SimulationTick_Implementation(const FSimulationTickParams& Params, FMoverTickEndData& OutputState)
|
||||
#else
|
||||
void UGMS_ZipliningMode::OnSimulationTick(const FSimulationTickParams& Params, FMoverTickEndData& OutputState)
|
||||
#endif
|
||||
{
|
||||
// Are we continuing a move or starting fresh?
|
||||
const FGMS_ZipliningState* StartingZipState = Params.StartState.SyncState.SyncStateCollection.FindDataByType<FGMS_ZipliningState>();
|
||||
|
||||
FMoverDefaultSyncState& OutputSyncState = OutputState.SyncState.SyncStateCollection.FindOrAddMutableDataByType<FMoverDefaultSyncState>();
|
||||
FGMS_ZipliningState& OutZipState = OutputState.SyncState.SyncStateCollection.FindOrAddMutableDataByType<FGMS_ZipliningState>();
|
||||
|
||||
USceneComponent* UpdatedComponent = Params.MovingComps.UpdatedComponent.Get();
|
||||
UMoverComponent* MoverComp = Params.MovingComps.MoverComponent.Get();
|
||||
AActor* MoverActor = MoverComp->GetOwner();
|
||||
|
||||
USceneComponent* StartPoint = nullptr;
|
||||
USceneComponent* EndPoint = nullptr;
|
||||
FVector ZipDirection;
|
||||
FVector FlatFacingDir;
|
||||
|
||||
const float DeltaSeconds = Params.TimeStep.StepMs * 0.001f;
|
||||
|
||||
FVector ActorOrigin;
|
||||
FVector BoxExtent;
|
||||
MoverActor->GetActorBounds(true, OUT ActorOrigin, OUT BoxExtent);
|
||||
const FVector ActorToZiplineOffset = MoverComp->GetUpDirection() * BoxExtent.Z;
|
||||
|
||||
if (!StartingZipState)
|
||||
{
|
||||
// There is no existing zipline state... so let's find the target
|
||||
// A) teleport to the closest starting point, set the zip direction
|
||||
// B) choose the appropriate facing direction
|
||||
// C) choose the appropriate initial velocity
|
||||
TArray<AActor*> OverlappingActors;
|
||||
MoverComp->GetOwner()->GetOverlappingActors(OUT OverlappingActors);
|
||||
|
||||
for (AActor* CandidateActor : OverlappingActors)
|
||||
{
|
||||
bool bIsZipline = UKismetSystemLibrary::DoesImplementInterface(CandidateActor, UGMS_ZiplineInterface::StaticClass());
|
||||
|
||||
if (bIsZipline)
|
||||
{
|
||||
const FVector MoverLoc = UpdatedComponent->GetComponentLocation();
|
||||
USceneComponent* ZipPointA = IGMS_ZiplineInterface::Execute_GetStartComponent(CandidateActor);
|
||||
USceneComponent* ZipPointB = IGMS_ZiplineInterface::Execute_GetEndComponent(CandidateActor);
|
||||
|
||||
if (FVector::DistSquared(ZipPointA->GetComponentLocation(), MoverLoc) < FVector::DistSquared(ZipPointB->GetComponentLocation(), MoverLoc))
|
||||
{
|
||||
OutZipState.bIsMovingAtoB = true;
|
||||
StartPoint = ZipPointA;
|
||||
EndPoint = ZipPointB;
|
||||
}
|
||||
else
|
||||
{
|
||||
OutZipState.bIsMovingAtoB = false;
|
||||
StartPoint = ZipPointB;
|
||||
EndPoint = ZipPointA;
|
||||
}
|
||||
|
||||
ZipDirection = (EndPoint->GetComponentLocation() - StartPoint->GetComponentLocation()).GetSafeNormal();
|
||||
|
||||
const FVector WarpLocation = StartPoint->GetComponentLocation() - ActorToZiplineOffset;
|
||||
|
||||
FlatFacingDir = FVector::VectorPlaneProject(ZipDirection, MoverComp->GetUpDirection()).GetSafeNormal();
|
||||
|
||||
OutZipState.ZiplineActor = CandidateActor;
|
||||
|
||||
UpdatedComponent->GetOwner()->TeleportTo(WarpLocation, FlatFacingDir.ToOrientationRotator());
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we were unable to find a valid target zipline, refund all the time and let the actor fall
|
||||
if (!StartPoint || !EndPoint)
|
||||
{
|
||||
FName DefaultAirMode = DefaultModeNames::Falling;
|
||||
if (UCommonLegacyMovementSettings* LegacySettings = MoverComp->FindSharedSettings_Mutable<UCommonLegacyMovementSettings>())
|
||||
{
|
||||
DefaultAirMode = LegacySettings->AirMovementModeName;
|
||||
}
|
||||
|
||||
OutputState.MovementEndState.NextModeName = DefaultModeNames::Falling;
|
||||
OutputState.MovementEndState.RemainingMs = Params.TimeStep.StepMs;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
check(StartingZipState->ZiplineActor);
|
||||
OutZipState = *StartingZipState;
|
||||
|
||||
USceneComponent* ZipPointA = IGMS_ZiplineInterface::Execute_GetStartComponent(StartingZipState->ZiplineActor);
|
||||
USceneComponent* ZipPointB = IGMS_ZiplineInterface::Execute_GetEndComponent(StartingZipState->ZiplineActor);
|
||||
|
||||
if (StartingZipState->bIsMovingAtoB)
|
||||
{
|
||||
StartPoint = ZipPointA;
|
||||
EndPoint = ZipPointB;
|
||||
}
|
||||
else
|
||||
{
|
||||
StartPoint = ZipPointB;
|
||||
EndPoint = ZipPointA;
|
||||
}
|
||||
|
||||
ZipDirection = (EndPoint->GetComponentLocation() - StartPoint->GetComponentLocation()).GetSafeNormal();
|
||||
FlatFacingDir = FVector::VectorPlaneProject(ZipDirection, MoverComp->GetUpDirection()).GetSafeNormal();
|
||||
}
|
||||
|
||||
|
||||
// Now let's slide along the zipline
|
||||
const FVector StepStartPos = UpdatedComponent->GetComponentLocation() + ActorToZiplineOffset;
|
||||
const FVector DesiredEndPos = StepStartPos + (ZipDirection * MaxSpeed * DeltaSeconds); // TODO: Make speed more dynamic
|
||||
|
||||
FVector ActualEndPos = FMath::ClosestPointOnSegment(DesiredEndPos,
|
||||
StartPoint->GetComponentLocation(),
|
||||
EndPoint->GetComponentLocation());
|
||||
|
||||
bool bWillReachEndPosition = (ActualEndPos - EndPoint->GetComponentLocation()).IsNearlyZero();
|
||||
|
||||
FVector MoveDelta = ActualEndPos - StepStartPos;
|
||||
|
||||
|
||||
FMovementRecord MoveRecord;
|
||||
MoveRecord.SetDeltaSeconds(DeltaSeconds);
|
||||
|
||||
|
||||
if (!MoveDelta.IsNearlyZero())
|
||||
{
|
||||
FHitResult Hit(1.f);
|
||||
|
||||
UMovementUtils::TrySafeMoveUpdatedComponent(Params.MovingComps, MoveDelta, FlatFacingDir.ToOrientationQuat(), true, Hit, ETeleportType::None, MoveRecord);
|
||||
}
|
||||
|
||||
|
||||
const FVector FinalLocation = UpdatedComponent->GetComponentLocation();
|
||||
const FVector FinalVelocity = MoveRecord.GetRelevantVelocity();
|
||||
|
||||
OutputSyncState.SetTransforms_WorldSpace(FinalLocation,
|
||||
UpdatedComponent->GetComponentRotation(),
|
||||
FinalVelocity,
|
||||
nullptr); // no movement base
|
||||
|
||||
UpdatedComponent->ComponentVelocity = FinalVelocity;
|
||||
|
||||
|
||||
if (bWillReachEndPosition)
|
||||
{
|
||||
FName DefaultAirMode = DefaultModeNames::Falling;
|
||||
if (UCommonLegacyMovementSettings* LegacySettings = MoverComp->FindSharedSettings_Mutable<UCommonLegacyMovementSettings>())
|
||||
{
|
||||
DefaultAirMode = LegacySettings->AirMovementModeName;
|
||||
}
|
||||
|
||||
OutputState.MovementEndState.NextModeName = DefaultAirMode;
|
||||
// TODO: If we reach the end position early, we should refund the remaining time
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Nodes/GMS_AnimNode_CurvesBlend.h"
|
||||
|
||||
#include "Animation/AnimTrace.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_AnimNode_CurvesBlend)
|
||||
|
||||
void FGMS_AnimNode_CurvesBlend::Initialize_AnyThread(const FAnimationInitializeContext& Context)
|
||||
{
|
||||
DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC()
|
||||
|
||||
Super::Initialize_AnyThread(Context);
|
||||
|
||||
SourcePose.Initialize(Context);
|
||||
CurvesPose.Initialize(Context);
|
||||
}
|
||||
|
||||
void FGMS_AnimNode_CurvesBlend::CacheBones_AnyThread(const FAnimationCacheBonesContext& Context)
|
||||
{
|
||||
DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC()
|
||||
|
||||
Super::CacheBones_AnyThread(Context);
|
||||
|
||||
SourcePose.CacheBones(Context);
|
||||
CurvesPose.CacheBones(Context);
|
||||
}
|
||||
|
||||
void FGMS_AnimNode_CurvesBlend::Update_AnyThread(const FAnimationUpdateContext& Context)
|
||||
{
|
||||
DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC()
|
||||
|
||||
Super::Update_AnyThread(Context);
|
||||
|
||||
GetEvaluateGraphExposedInputs().Execute(Context);
|
||||
|
||||
SourcePose.Update(Context);
|
||||
|
||||
const auto CurrentBlendAmount{GetBlendAmount()};
|
||||
if (FAnimWeight::IsRelevant(CurrentBlendAmount))
|
||||
{
|
||||
CurvesPose.Update(Context);
|
||||
}
|
||||
|
||||
TRACE_ANIM_NODE_VALUE(Context, TEXT("Blend Amount"), CurrentBlendAmount);
|
||||
|
||||
TRACE_ANIM_NODE_VALUE(Context, TEXT("Blend Mode"), *StaticEnum<EGMS_CurvesBlendMode>()->GetNameStringByValue(static_cast<int64>(GetBlendMode())));
|
||||
}
|
||||
|
||||
void FGMS_AnimNode_CurvesBlend::Evaluate_AnyThread(FPoseContext& Output)
|
||||
{
|
||||
DECLARE_SCOPE_HIERARCHICAL_COUNTER_FUNC()
|
||||
ANIM_MT_SCOPE_CYCLE_COUNTER_VERBOSE(CurvesBlend, !IsInGameThread());
|
||||
|
||||
Super::Evaluate_AnyThread(Output);
|
||||
|
||||
SourcePose.Evaluate(Output);
|
||||
|
||||
const auto CurrentBlendAmount{GetBlendAmount()};
|
||||
if (!FAnimWeight::IsRelevant(CurrentBlendAmount))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto CurvesPoseContext{Output};
|
||||
CurvesPose.Evaluate(CurvesPoseContext);
|
||||
|
||||
switch (GetBlendMode())
|
||||
{
|
||||
case EGMS_CurvesBlendMode::BlendByAmount:
|
||||
Output.Curve.Accumulate(CurvesPoseContext.Curve, CurrentBlendAmount);
|
||||
break;
|
||||
|
||||
case EGMS_CurvesBlendMode::Combine:
|
||||
Output.Curve.Combine(CurvesPoseContext.Curve);
|
||||
break;
|
||||
|
||||
case EGMS_CurvesBlendMode::CombinePreserved:
|
||||
Output.Curve.CombinePreserved(CurvesPoseContext.Curve);
|
||||
break;
|
||||
|
||||
case EGMS_CurvesBlendMode::UseMaxValue:
|
||||
Output.Curve.UseMaxValue(CurvesPoseContext.Curve);
|
||||
break;
|
||||
|
||||
case EGMS_CurvesBlendMode::UseMinValue:
|
||||
Output.Curve.UseMinValue(CurvesPoseContext.Curve);
|
||||
break;
|
||||
|
||||
case EGMS_CurvesBlendMode::Override:
|
||||
Output.Curve.Override(CurvesPoseContext.Curve);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void FGMS_AnimNode_CurvesBlend::GatherDebugData(FNodeDebugData& DebugData)
|
||||
{
|
||||
DECLARE_SCOPE_HIERARCHICAL_COUNTER_ANIMNODE(GatherDebugData)
|
||||
|
||||
TStringBuilder<256> DebugItemBuilder{InPlace, DebugData.GetNodeName(this), TEXTVIEW(": Blend Amount: ")};
|
||||
|
||||
DebugItemBuilder.Appendf(TEXT("%.2f"), GetBlendAmount());
|
||||
|
||||
DebugData.AddDebugItem(FString{DebugItemBuilder});
|
||||
SourcePose.GatherDebugData(DebugData.BranchFlow(1.0f));
|
||||
CurvesPose.GatherDebugData(DebugData.BranchFlow(GetBlendAmount()));
|
||||
}
|
||||
|
||||
float FGMS_AnimNode_CurvesBlend::GetBlendAmount() const
|
||||
{
|
||||
return GET_ANIM_NODE_DATA(float, BlendAmount);
|
||||
}
|
||||
|
||||
EGMS_CurvesBlendMode FGMS_AnimNode_CurvesBlend::GetBlendMode() const
|
||||
{
|
||||
return GET_ANIM_NODE_DATA(EGMS_CurvesBlendMode, BlendMode);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Nodes/GMS_AnimNode_GameplayTagsBlend.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_AnimNode_GameplayTagsBlend)
|
||||
|
||||
int32 FGMS_AnimNode_GameplayTagsBlend::GetActiveChildIndex()
|
||||
{
|
||||
const auto& CurrentActiveTag{GetActiveTag()};
|
||||
|
||||
return CurrentActiveTag.IsValid()
|
||||
? GetTags().Find(CurrentActiveTag) + 1
|
||||
: 0;
|
||||
}
|
||||
|
||||
const FGameplayTag& FGMS_AnimNode_GameplayTagsBlend::GetActiveTag() const
|
||||
{
|
||||
return GET_ANIM_NODE_DATA(FGameplayTag, ActiveTag);
|
||||
}
|
||||
|
||||
const TArray<FGameplayTag>& FGMS_AnimNode_GameplayTagsBlend::GetTags() const
|
||||
{
|
||||
return GET_ANIM_NODE_DATA(TArray<FGameplayTag>, Tags);
|
||||
}
|
||||
|
||||
#if WITH_EDITOR
|
||||
void FGMS_AnimNode_GameplayTagsBlend::RefreshPoses()
|
||||
{
|
||||
const auto Difference{BlendPose.Num() - GetTags().Num() - 1};
|
||||
if (Difference == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Difference > 0)
|
||||
{
|
||||
for (auto i{Difference}; i > 0; i--)
|
||||
{
|
||||
RemovePose(BlendPose.Num() - 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto i{Difference}; i < 0; i++)
|
||||
{
|
||||
AddPose();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,296 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#include "Nodes/GMS_AnimNode_LayeredBoneBlend.h"
|
||||
#include "AnimationRuntime.h"
|
||||
#include "Animation/AnimInstanceProxy.h"
|
||||
#include "Animation/AnimTrace.h"
|
||||
#include "Animation/AnimCurveTypes.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_AnimNode_LayeredBoneBlend)
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
// FGMS_AnimNode_LayeredBoneBlend
|
||||
|
||||
void FGMS_AnimNode_LayeredBoneBlend::Initialize_AnyThread(const FAnimationInitializeContext& Context)
|
||||
{
|
||||
DECLARE_SCOPE_HIERARCHICAL_COUNTER_ANIMNODE(Initialize_AnyThread)
|
||||
FAnimNode_Base::Initialize_AnyThread(Context);
|
||||
|
||||
const int NumPoses = BlendPoses.Num();
|
||||
checkSlow(BlendWeights.Num() == NumPoses);
|
||||
|
||||
// initialize children
|
||||
BasePose.Initialize(Context);
|
||||
|
||||
if (NumPoses > 0)
|
||||
{
|
||||
for (int32 ChildIndex = 0; ChildIndex < NumPoses; ++ChildIndex)
|
||||
{
|
||||
BlendPoses[ChildIndex].Initialize(Context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FGMS_AnimNode_LayeredBoneBlend::RebuildPerBoneBlendWeights(const USkeleton* InSkeleton)
|
||||
{
|
||||
if (InSkeleton)
|
||||
{
|
||||
if (ExternalLayerSetup.BranchFilters.IsEmpty())
|
||||
{
|
||||
FAnimationRuntime::CreateMaskWeights(PerBoneBlendWeights, LayerSetup, InSkeleton);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const FBranchFilter& BranchFilter : ExternalLayerSetup.BranchFilters)
|
||||
{
|
||||
LayerSetup[0].BranchFilters.Add(BranchFilter);
|
||||
}
|
||||
FAnimationRuntime::CreateMaskWeights(PerBoneBlendWeights, LayerSetup, InSkeleton);
|
||||
}
|
||||
|
||||
SkeletonGuid = InSkeleton->GetGuid();
|
||||
VirtualBoneGuid = InSkeleton->GetVirtualBoneGuid();
|
||||
}
|
||||
}
|
||||
|
||||
bool FGMS_AnimNode_LayeredBoneBlend::ArePerBoneBlendWeightsValid(const USkeleton* InSkeleton) const
|
||||
{
|
||||
return (InSkeleton != nullptr && InSkeleton->GetGuid() == SkeletonGuid && InSkeleton->GetVirtualBoneGuid() == VirtualBoneGuid);
|
||||
}
|
||||
|
||||
void FGMS_AnimNode_LayeredBoneBlend::UpdateCachedBoneData(const FBoneContainer& RequiredBones, const USkeleton* Skeleton)
|
||||
{
|
||||
if (LayerSetup.IsValidIndex(0) && LayerSetup[0].BranchFilters.IsEmpty())
|
||||
{
|
||||
RebuildPerBoneBlendWeights(Skeleton);
|
||||
}
|
||||
|
||||
// if(RequiredBones.GetSerialNumber() == RequiredBonesSerialNumber)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
|
||||
if (!ArePerBoneBlendWeightsValid(Skeleton))
|
||||
{
|
||||
RebuildPerBoneBlendWeights(Skeleton);
|
||||
}
|
||||
|
||||
// build desired bone weights
|
||||
const TArray<FBoneIndexType>& RequiredBoneIndices = RequiredBones.GetBoneIndicesArray();
|
||||
const int32 NumRequiredBones = RequiredBoneIndices.Num();
|
||||
DesiredBoneBlendWeights.SetNumZeroed(NumRequiredBones);
|
||||
for (int32 RequiredBoneIndex=0; RequiredBoneIndex<NumRequiredBones; RequiredBoneIndex++)
|
||||
{
|
||||
const int32 SkeletonBoneIndex = RequiredBones.GetSkeletonIndex(FCompactPoseBoneIndex(RequiredBoneIndex));
|
||||
if (ensure(SkeletonBoneIndex != INDEX_NONE))
|
||||
{
|
||||
DesiredBoneBlendWeights[RequiredBoneIndex] = PerBoneBlendWeights[SkeletonBoneIndex];
|
||||
}
|
||||
}
|
||||
|
||||
CurrentBoneBlendWeights.Reset(DesiredBoneBlendWeights.Num());
|
||||
CurrentBoneBlendWeights.AddZeroed(DesiredBoneBlendWeights.Num());
|
||||
|
||||
//Reinitialize bone blend weights now that we have cleared them
|
||||
FAnimationRuntime::UpdateDesiredBoneWeight(DesiredBoneBlendWeights, CurrentBoneBlendWeights, BlendWeights);
|
||||
|
||||
// Build curve source indices
|
||||
{
|
||||
// Get the original Reserve value
|
||||
int32 OriginalReserve = CurvePoseSourceIndices.Max();
|
||||
CurvePoseSourceIndices.Empty();
|
||||
|
||||
Skeleton->ForEachCurveMetaData([this, &RequiredBones](const FName& InCurveName, const FCurveMetaData& InMetaData)
|
||||
{
|
||||
for (const FBoneReference& LinkedBone : InMetaData.LinkedBones)
|
||||
{
|
||||
FCompactPoseBoneIndex CompactPoseIndex = LinkedBone.GetCompactPoseIndex(RequiredBones);
|
||||
if (CompactPoseIndex != INDEX_NONE)
|
||||
{
|
||||
if (DesiredBoneBlendWeights[CompactPoseIndex.GetInt()].BlendWeight > 0.f)
|
||||
{
|
||||
CurvePoseSourceIndices.Add(InCurveName, DesiredBoneBlendWeights[CompactPoseIndex.GetInt()].SourceIndex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Shrink afterwards to exactly what was used if the Reserve increased, to save memory. Eventually the reserve will
|
||||
// stabilize at the maximum number of nodes actually used in practice for this specific anim node.
|
||||
if (CurvePoseSourceIndices.Num() > OriginalReserve)
|
||||
{
|
||||
CurvePoseSourceIndices.Shrink();
|
||||
}
|
||||
}
|
||||
|
||||
RequiredBonesSerialNumber = RequiredBones.GetSerialNumber();
|
||||
LayerSetup[0].BranchFilters.Reset();
|
||||
}
|
||||
|
||||
void FGMS_AnimNode_LayeredBoneBlend::CacheBones_AnyThread(const FAnimationCacheBonesContext& Context)
|
||||
{
|
||||
DECLARE_SCOPE_HIERARCHICAL_COUNTER_ANIMNODE(CacheBones_AnyThread)
|
||||
BasePose.CacheBones(Context);
|
||||
int32 NumPoses = BlendPoses.Num();
|
||||
for(int32 ChildIndex=0; ChildIndex<NumPoses; ChildIndex++)
|
||||
{
|
||||
BlendPoses[ChildIndex].CacheBones(Context);
|
||||
}
|
||||
|
||||
UpdateCachedBoneData(Context.AnimInstanceProxy->GetRequiredBones(), Context.AnimInstanceProxy->GetSkeleton());
|
||||
}
|
||||
|
||||
void FGMS_AnimNode_LayeredBoneBlend::Update_AnyThread(const FAnimationUpdateContext& Context)
|
||||
{
|
||||
DECLARE_SCOPE_HIERARCHICAL_COUNTER_ANIMNODE(Update_AnyThread)
|
||||
bHasRelevantPoses = false;
|
||||
int32 RootMotionBlendPose = -1;
|
||||
float RootMotionWeight = 0.f;
|
||||
const float RootMotionClearWeight = bBlendRootMotionBasedOnRootBone ? 0.f : 1.f;
|
||||
|
||||
if (IsLODEnabled(Context.AnimInstanceProxy))
|
||||
{
|
||||
GetEvaluateGraphExposedInputs().Execute(Context);
|
||||
|
||||
for (int32 ChildIndex = 0; ChildIndex < BlendPoses.Num(); ++ChildIndex)
|
||||
{
|
||||
const float ChildWeight = BlendWeights[ChildIndex];
|
||||
if (FAnimWeight::IsRelevant(ChildWeight))
|
||||
{
|
||||
if (bHasRelevantPoses == false)
|
||||
{
|
||||
// Update cached data now we know we might be valid
|
||||
UpdateCachedBoneData(Context.AnimInstanceProxy->GetRequiredBones(), Context.AnimInstanceProxy->GetSkeleton());
|
||||
|
||||
// Update weights
|
||||
FAnimationRuntime::UpdateDesiredBoneWeight(DesiredBoneBlendWeights, CurrentBoneBlendWeights, BlendWeights);
|
||||
bHasRelevantPoses = true;
|
||||
|
||||
if(bBlendRootMotionBasedOnRootBone && !CurrentBoneBlendWeights.IsEmpty())
|
||||
{
|
||||
const float NewRootMotionWeight = CurrentBoneBlendWeights[0].BlendWeight;
|
||||
if(NewRootMotionWeight > ZERO_ANIMWEIGHT_THRESH)
|
||||
{
|
||||
RootMotionWeight = NewRootMotionWeight;
|
||||
RootMotionBlendPose = CurrentBoneBlendWeights[0].SourceIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const float ThisPoseRootMotionWeight = (ChildIndex == RootMotionBlendPose) ? RootMotionWeight : RootMotionClearWeight;
|
||||
BlendPoses[ChildIndex].Update(Context.FractionalWeightAndRootMotion(ChildWeight, ThisPoseRootMotionWeight));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// initialize children
|
||||
const float BaseRootMotionWeight = 1.f - RootMotionWeight;
|
||||
|
||||
if (BaseRootMotionWeight < ZERO_ANIMWEIGHT_THRESH)
|
||||
{
|
||||
BasePose.Update(Context.FractionalWeightAndRootMotion(1.f, BaseRootMotionWeight));
|
||||
}
|
||||
else
|
||||
{
|
||||
BasePose.Update(Context);
|
||||
}
|
||||
|
||||
TRACE_ANIM_NODE_VALUE(Context, TEXT("Num Poses"), BlendPoses.Num());
|
||||
}
|
||||
|
||||
void FGMS_AnimNode_LayeredBoneBlend::Evaluate_AnyThread(FPoseContext& Output)
|
||||
{
|
||||
DECLARE_SCOPE_HIERARCHICAL_COUNTER_ANIMNODE(Evaluate_AnyThread)
|
||||
ANIM_MT_SCOPE_CYCLE_COUNTER(BlendPosesInGraph, !IsInGameThread());
|
||||
|
||||
const int NumPoses = BlendPoses.Num();
|
||||
if ((NumPoses == 0) || !bHasRelevantPoses)
|
||||
{
|
||||
BasePose.Evaluate(Output);
|
||||
}
|
||||
else
|
||||
{
|
||||
FPoseContext BasePoseContext(Output);
|
||||
|
||||
// evaluate children
|
||||
BasePose.Evaluate(BasePoseContext);
|
||||
|
||||
TArray<FCompactPose> TargetBlendPoses;
|
||||
TargetBlendPoses.SetNum(NumPoses);
|
||||
|
||||
TArray<FBlendedCurve> TargetBlendCurves;
|
||||
TargetBlendCurves.SetNum(NumPoses);
|
||||
|
||||
TArray<UE::Anim::FStackAttributeContainer> TargetBlendAttributes;
|
||||
TargetBlendAttributes.SetNum(NumPoses);
|
||||
|
||||
for (int32 ChildIndex = 0; ChildIndex < NumPoses; ++ChildIndex)
|
||||
{
|
||||
if (FAnimWeight::IsRelevant(BlendWeights[ChildIndex]))
|
||||
{
|
||||
FPoseContext CurrentPoseContext(Output);
|
||||
BlendPoses[ChildIndex].Evaluate(CurrentPoseContext);
|
||||
|
||||
TargetBlendPoses[ChildIndex].MoveBonesFrom(CurrentPoseContext.Pose);
|
||||
TargetBlendCurves[ChildIndex].MoveFrom(CurrentPoseContext.Curve);
|
||||
TargetBlendAttributes[ChildIndex].MoveFrom(CurrentPoseContext.CustomAttributes);
|
||||
}
|
||||
else
|
||||
{
|
||||
TargetBlendPoses[ChildIndex].ResetToRefPose(BasePoseContext.Pose.GetBoneContainer());
|
||||
TargetBlendCurves[ChildIndex].InitFrom(Output.Curve);
|
||||
}
|
||||
}
|
||||
|
||||
// filter to make sure it only includes curves that are linked to the correct bone filter
|
||||
UE::Anim::FNamedValueArrayUtils::RemoveByPredicate(BasePoseContext.Curve, CurvePoseSourceIndices,
|
||||
[](const UE::Anim::FCurveElement& InOutBasePoseElement, const UE::Anim::FCurveElementIndexed& InSourceIndexElement)
|
||||
{
|
||||
// if source index is set, remove base pose curve value
|
||||
return (InSourceIndexElement.Index != INDEX_NONE);
|
||||
});
|
||||
|
||||
// Filter child pose curves
|
||||
for (int32 ChildIndex = 0; ChildIndex < NumPoses; ++ChildIndex)
|
||||
{
|
||||
UE::Anim::FNamedValueArrayUtils::RemoveByPredicate(TargetBlendCurves[ChildIndex], CurvePoseSourceIndices,
|
||||
[ChildIndex](const UE::Anim::FCurveElement& InOutBasePoseElement, const UE::Anim::FCurveElementIndexed& InSourceIndexElement)
|
||||
{
|
||||
// if not source, remove it
|
||||
return (InSourceIndexElement.Index != INDEX_NONE) && (InSourceIndexElement.Index != ChildIndex);
|
||||
});
|
||||
}
|
||||
|
||||
FAnimationRuntime::EBlendPosesPerBoneFilterFlags BlendFlags = FAnimationRuntime::EBlendPosesPerBoneFilterFlags::None;
|
||||
if (bMeshSpaceRotationBlend)
|
||||
{
|
||||
BlendFlags |= FAnimationRuntime::EBlendPosesPerBoneFilterFlags::MeshSpaceRotation;
|
||||
}
|
||||
if (bMeshSpaceScaleBlend)
|
||||
{
|
||||
BlendFlags |= FAnimationRuntime::EBlendPosesPerBoneFilterFlags::MeshSpaceScale;
|
||||
}
|
||||
|
||||
FAnimationPoseData AnimationPoseData(Output);
|
||||
FAnimationRuntime::BlendPosesPerBoneFilter(BasePoseContext.Pose, TargetBlendPoses, BasePoseContext.Curve, TargetBlendCurves, BasePoseContext.CustomAttributes, TargetBlendAttributes, AnimationPoseData, CurrentBoneBlendWeights, BlendFlags, CurveBlendOption);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FGMS_AnimNode_LayeredBoneBlend::GatherDebugData(FNodeDebugData& DebugData)
|
||||
{
|
||||
DECLARE_SCOPE_HIERARCHICAL_COUNTER_ANIMNODE(GatherDebugData)
|
||||
const int NumPoses = BlendPoses.Num();
|
||||
|
||||
FString DebugLine = DebugData.GetNodeName(this);
|
||||
DebugLine += FString::Printf(TEXT("(Num Poses: %i)"), NumPoses);
|
||||
DebugData.AddDebugItem(DebugLine);
|
||||
|
||||
BasePose.GatherDebugData(DebugData.BranchFlow(1.f));
|
||||
|
||||
for (int32 ChildIndex = 0; ChildIndex < NumPoses; ++ChildIndex)
|
||||
{
|
||||
BlendPoses[ChildIndex].GatherDebugData(DebugData.BranchFlow(BlendWeights[ChildIndex]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,680 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#include "Nodes/GMS_AnimNode_OrientationWarping.h"
|
||||
#include "Animation/AnimInstanceProxy.h"
|
||||
#include "Animation/AnimNodeFunctionRef.h"
|
||||
#include "Animation/AnimRootMotionProvider.h"
|
||||
#include "BoneControllers/AnimNode_OffsetRootBone.h"
|
||||
#include "HAL/IConsoleManager.h"
|
||||
#include "Animation/AnimTrace.h"
|
||||
#include "Logging/LogVerbosity.h"
|
||||
#include "VisualLogger/VisualLogger.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_AnimNode_OrientationWarping)
|
||||
|
||||
DECLARE_CYCLE_STAT(TEXT("OrientationWarping Eval"), STAT_OrientationWarping_Eval, STATGROUP_Anim);
|
||||
|
||||
#if ENABLE_ANIM_DEBUG
|
||||
static TAutoConsoleVariable<int32> CVarAnimNodeOrientationWarpingDebug(TEXT("a.AnimNode.GenericOrientationWarping.Debug"), 0, TEXT("Turn on visualization debugging for Orientation Warping."));
|
||||
static TAutoConsoleVariable<int32> CVarAnimNodeOrientationWarpingVerbose(TEXT("a.AnimNode.GenericOrientationWarping.Verbose"), 0, TEXT("Turn on verbose graph debugging for Orientation Warping"));
|
||||
static TAutoConsoleVariable<int32> CVarAnimNodeOrientationWarpingEnable(TEXT("a.AnimNode.GenericOrientationWarping.Enable"), 1, TEXT("Toggle Orientation Warping"));
|
||||
#endif
|
||||
|
||||
namespace UE::Anim
|
||||
{
|
||||
static inline FVector GetAxisVector(const EAxis::Type& InAxis)
|
||||
{
|
||||
switch (InAxis)
|
||||
{
|
||||
case EAxis::X:
|
||||
return FVector::ForwardVector;
|
||||
case EAxis::Y:
|
||||
return FVector::RightVector;
|
||||
default:
|
||||
return FVector::UpVector;
|
||||
};
|
||||
}
|
||||
|
||||
static inline bool IsInvalidWarpingAngleDegrees(float Angle, float Tolerance)
|
||||
{
|
||||
Angle = FRotator::NormalizeAxis(Angle);
|
||||
return FMath::IsNearlyZero(Angle, Tolerance) || FMath::IsNearlyEqual(FMath::Abs(Angle), 180.f, Tolerance);
|
||||
}
|
||||
|
||||
static float SignedAngleRadBetweenNormals(const FVector& From, const FVector& To, const FVector& Axis)
|
||||
{
|
||||
const float FromDotTo = FVector::DotProduct(From, To);
|
||||
const float Angle = FMath::Acos(FromDotTo);
|
||||
const FVector Cross = FVector::CrossProduct(From, To);
|
||||
const float Dot = FVector::DotProduct(Cross, Axis);
|
||||
return Dot >= 0 ? Angle : -Angle;
|
||||
}
|
||||
}
|
||||
|
||||
void FGMS_AnimNode_OrientationWarping::GatherDebugData(FNodeDebugData& DebugData)
|
||||
{
|
||||
FString DebugLine = DebugData.GetNodeName(this);
|
||||
#if ENABLE_ANIM_DEBUG
|
||||
if (CVarAnimNodeOrientationWarpingVerbose.GetValueOnAnyThread() == 1)
|
||||
{
|
||||
if (Mode == EWarpingEvaluationMode::Manual)
|
||||
{
|
||||
DebugLine += TEXT("\n - Evaluation Mode: (Manual)");
|
||||
DebugLine += FString::Printf(TEXT("\n - Orientation Angle: (%.3fd)"), FMath::RadiansToDegrees(ActualOrientationAngleRad));
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugLine += TEXT("\n - Evaluation Mode: (Graph)");
|
||||
DebugLine += FString::Printf(TEXT("\n - Orientation Angle: (%.3fd)"), FMath::RadiansToDegrees(ActualOrientationAngleRad));
|
||||
// Locomotion angle is already in degrees.
|
||||
DebugLine += FString::Printf(TEXT("\n - Locomotion Angle: (%.3fd)"), LocomotionAngle);
|
||||
DebugLine += FString::Printf(TEXT("\n - Locomotion Delta Angle Threshold: (%.3fd)"), LocomotionAngleDeltaThreshold);
|
||||
#if WITH_EDITORONLY_DATA
|
||||
DebugLine += FString::Printf(TEXT("\n - Root Motion Delta Attribute Found: %s)"), (bFoundRootMotionAttribute) ? TEXT("true") : TEXT("false"));
|
||||
#endif
|
||||
}
|
||||
if (const UEnum* TypeEnum = FindObject<UEnum>(nullptr, TEXT("/Script/CoreUObject.EAxis")))
|
||||
{
|
||||
DebugLine += FString::Printf(TEXT("\n - Rotation Axis: (%s)"), *(TypeEnum->GetNameStringByIndex(static_cast<int32>(RotationAxis))));
|
||||
}
|
||||
DebugLine += FString::Printf(TEXT("\n - Rotation Interpolation Speed: (%.3fd)"), RotationInterpSpeed);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
const float ActualOrientationAngleDegrees = FMath::RadiansToDegrees(ActualOrientationAngleRad);
|
||||
DebugLine += FString::Printf(TEXT("(Orientation Angle: %.3fd)"), ActualOrientationAngleDegrees);
|
||||
}
|
||||
DebugData.AddDebugItem(DebugLine);
|
||||
ComponentPose.GatherDebugData(DebugData);
|
||||
}
|
||||
|
||||
void FGMS_AnimNode_OrientationWarping::Initialize_AnyThread(const FAnimationInitializeContext& Context)
|
||||
{
|
||||
FAnimNode_SkeletalControlBase::Initialize_AnyThread(Context);
|
||||
|
||||
Reset(Context);
|
||||
|
||||
//早一点拿到ExternalBoneReference.
|
||||
if (IsLODEnabled(Context.AnimInstanceProxy))
|
||||
{
|
||||
GetEvaluateGraphExposedInputs().Execute(Context);
|
||||
}
|
||||
}
|
||||
|
||||
void FGMS_AnimNode_OrientationWarping::UpdateInternal(const FAnimationUpdateContext& Context)
|
||||
{
|
||||
FAnimNode_SkeletalControlBase::UpdateInternal(Context);
|
||||
|
||||
// If we just became relevant and haven't been initialized yet, then reset.
|
||||
if (!bIsFirstUpdate && UpdateCounter.HasEverBeenUpdated() && !UpdateCounter.WasSynchronizedCounter(Context.AnimInstanceProxy->GetUpdateCounter()))
|
||||
{
|
||||
Reset(Context);
|
||||
}
|
||||
UpdateCounter.SynchronizeWith(Context.AnimInstanceProxy->GetUpdateCounter());
|
||||
BlendWeight = Context.GetFinalBlendWeight();
|
||||
|
||||
// if (WarpingSpace == EOrientationWarpingSpace::RootBoneTransform)
|
||||
// {
|
||||
// if (UE::AnimationWarping::FRootOffsetProvider* RootOffsetProvider = Context.GetMessage<UE::AnimationWarping::FRootOffsetProvider>())
|
||||
// {
|
||||
// WarpingSpaceTransform = RootOffsetProvider->GetRootTransform();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// WarpingSpaceTransform = Context.AnimInstanceProxy->GetComponentTransform();
|
||||
// }
|
||||
// }
|
||||
if (WarpingSpace == EOrientationWarpingSpace::ComponentTransform)
|
||||
{
|
||||
WarpingSpaceTransform = Context.AnimInstanceProxy->GetComponentTransform();
|
||||
}
|
||||
}
|
||||
|
||||
void FGMS_AnimNode_OrientationWarping::EvaluateSkeletalControl_AnyThread(FComponentSpacePoseContext& Output, TArray<FBoneTransform>& OutBoneTransforms)
|
||||
{
|
||||
SCOPE_CYCLE_COUNTER(STAT_OrientationWarping_Eval);
|
||||
check(OutBoneTransforms.Num() == 0);
|
||||
|
||||
float TargetOrientationAngleRad;
|
||||
|
||||
const float DeltaSeconds = Output.AnimInstanceProxy->GetDeltaSeconds();
|
||||
const float MaxAngleCorrectionRad = FMath::DegreesToRadians(MaxCorrectionDegrees);
|
||||
const FVector RotationAxisVector = UE::Anim::GetAxisVector(RotationAxis);
|
||||
FVector LocomotionForward = FVector::ZeroVector;
|
||||
|
||||
bool bGraphDrivenWarping = false;
|
||||
const UE::Anim::IAnimRootMotionProvider* RootMotionProvider = UE::Anim::IAnimRootMotionProvider::Get();
|
||||
|
||||
if (Mode == EWarpingEvaluationMode::Graph)
|
||||
{
|
||||
bGraphDrivenWarping = !!RootMotionProvider;
|
||||
ensureMsgf(bGraphDrivenWarping, TEXT("Graph driven Orientation Warping expected a valid root motion delta provider interface."));
|
||||
}
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
bFoundRootMotionAttribute = false;
|
||||
#endif
|
||||
|
||||
#if ENABLE_ANIM_DEBUG
|
||||
FTransform RootMotionTransformDelta = FTransform::Identity;
|
||||
float RootMotionDeltaAngleRad = 0.0;
|
||||
const float PreviousOrientationAngleRad = ActualOrientationAngleRad;
|
||||
#endif
|
||||
|
||||
// We will likely need to revisit LocomotionAngle participating as an input to orientation warping.
|
||||
// Without velocity information from the motion model (such as the capsule), LocomotionAngle isn't enough
|
||||
// information in isolation for all cases when deciding to warp.
|
||||
//
|
||||
// For example imagine that the motion model has stopped moving with zero velocity due to a
|
||||
// transition into a strafing stop. During that transition we may play an animation with non-zero
|
||||
// velocity for an arbitrary number of frames. In this scenario the concept of direction is meaningless
|
||||
// since we cannot orient the animation to match a zero velocity and consequently a zero direction,
|
||||
// since that would break the pose. For those frames, we would incorrectly over-orient the strafe.
|
||||
//
|
||||
// The solution may be instead to pass velocity with the actor base rotation, allowing us to retain
|
||||
// speed information about the motion. It may also allow us to do more complex orienting behavior
|
||||
// when multiple degrees of freedom can be considered.
|
||||
|
||||
if (WarpingSpace == EOrientationWarpingSpace::ComponentTransform)
|
||||
{
|
||||
WarpingSpaceTransform = Output.AnimInstanceProxy->GetComponentTransform();
|
||||
}
|
||||
|
||||
if (bGraphDrivenWarping)
|
||||
{
|
||||
#if !ENABLE_ANIM_DEBUG
|
||||
FTransform RootMotionTransformDelta = FTransform::Identity;
|
||||
#endif
|
||||
|
||||
bGraphDrivenWarping = RootMotionProvider->ExtractRootMotion(Output.CustomAttributes, RootMotionTransformDelta);
|
||||
|
||||
// Graph driven orientation warping will modify the incoming root motion to orient towards the intended locomotion angle
|
||||
if (bGraphDrivenWarping)
|
||||
{
|
||||
#if WITH_EDITORONLY_DATA
|
||||
// Graph driven Orientation Warping expects a root motion delta to be present in the attribute stream.
|
||||
bFoundRootMotionAttribute = true;
|
||||
#endif
|
||||
|
||||
// In UE, forward is defined as +x; consequently this is also true when sampling an actor's velocity. Historically the skeletal
|
||||
// mesh component forward will not match the actor, requiring us to correct the rotation before sampling the LocomotionForward.
|
||||
// In order to make orientation warping 'pure' in the future we will need to provide more context about the intent of
|
||||
// the actor vs the intent of the animation in their respective spaces. Specifically, we will need some form the following information:
|
||||
//
|
||||
// 1. Actor Forward
|
||||
// 2. Actor Velocity
|
||||
// 3. Skeletal Mesh Relative Rotation
|
||||
|
||||
if (LocomotionDirection.SquaredLength() > UE_SMALL_NUMBER)
|
||||
{
|
||||
// if we have a LocomotionDirection vector, transform into root bone local space
|
||||
LocomotionForward = WarpingSpaceTransform.InverseTransformVector(LocomotionDirection);
|
||||
LocomotionForward.Normalize();
|
||||
}
|
||||
else
|
||||
{
|
||||
LocomotionAngle = FRotator::NormalizeAxis(LocomotionAngle);
|
||||
// UE-184297 Avoid storing LocomotionAngle in radians in case haven't updated the pinned input, to avoid a DegToRad(RadianValue)
|
||||
const float LocomotionAngleRadians = FMath::DegreesToRadians(LocomotionAngle);
|
||||
const FQuat LocomotionRotation = FQuat(RotationAxisVector, LocomotionAngleRadians);
|
||||
const FTransform SkeletalMeshRelativeTransform = Output.AnimInstanceProxy->GetComponentRelativeTransform();
|
||||
const FQuat SkeletalMeshRelativeRotation = SkeletalMeshRelativeTransform.GetRotation();
|
||||
LocomotionForward = SkeletalMeshRelativeRotation.UnrotateVector(LocomotionRotation.GetForwardVector()).GetSafeNormal();
|
||||
}
|
||||
|
||||
// Flatten locomotion direction, along the rotation axis.
|
||||
LocomotionForward = (LocomotionForward - RotationAxisVector.Dot(LocomotionForward) * RotationAxisVector).GetSafeNormal();
|
||||
|
||||
// @todo: Graph mode using a "manual value" makes no sense. Restructure logic to address this in the future.
|
||||
if (bUseManualRootMotionVelocity)
|
||||
{
|
||||
RootMotionTransformDelta.SetTranslation(ManualRootMotionVelocity * DeltaSeconds);
|
||||
}
|
||||
|
||||
FVector RootMotionDeltaTranslation = RootMotionTransformDelta.GetTranslation();
|
||||
|
||||
// Flatten root motion translation, along the rotation axis.
|
||||
RootMotionDeltaTranslation = RootMotionDeltaTranslation - RotationAxisVector.Dot(RootMotionDeltaTranslation) * RotationAxisVector;
|
||||
|
||||
const float RootMotionDeltaSpeed = RootMotionDeltaTranslation.Size() / DeltaSeconds;
|
||||
if (RootMotionDeltaSpeed < MinRootMotionSpeedThreshold)
|
||||
{
|
||||
// If we're under the threshold, snap orientation angle to 0, and let interpolation handle the delta
|
||||
TargetOrientationAngleRad = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
const FVector PreviousRootMotionDeltaDirection = RootMotionDeltaDirection;
|
||||
// Hold previous direction if we can't calculate it from current move delta, because the root is no longer moving
|
||||
RootMotionDeltaDirection = RootMotionDeltaTranslation.GetSafeNormal(UE_SMALL_NUMBER, PreviousRootMotionDeltaDirection);
|
||||
TargetOrientationAngleRad = UE::Anim::SignedAngleRadBetweenNormals(RootMotionDeltaDirection, LocomotionForward, RotationAxisVector);
|
||||
|
||||
// Motion Matching may return an animation that deviates a lot from the movement direction (e.g movement direction going bwd and motion matching could return the fwd animation for a few frames)
|
||||
// When that happens, since we use the delta between root motion and movement direction, we would be over-rotating the lower body and breaking the pose during those frames
|
||||
// So, when that happens we use the inverse of the root motion direction to calculate our target rotation.
|
||||
// This feels a bit 'hacky' but its the only option I've found so far to mitigate the problem
|
||||
if (LocomotionAngleDeltaThreshold > 0.f)
|
||||
{
|
||||
if (FMath::Abs(FMath::RadiansToDegrees(TargetOrientationAngleRad)) > LocomotionAngleDeltaThreshold)
|
||||
{
|
||||
TargetOrientationAngleRad = FMath::UnwindRadians(TargetOrientationAngleRad + FMath::DegreesToRadians(180.0f));
|
||||
RootMotionDeltaDirection = -RootMotionDeltaDirection;
|
||||
}
|
||||
}
|
||||
|
||||
// Don't compensate interpolation by the root motion angle delta if the previous angle isn't valid.
|
||||
if (bCounterCompenstateInterpolationByRootMotion && !PreviousRootMotionDeltaDirection.IsNearlyZero(UE_SMALL_NUMBER))
|
||||
{
|
||||
#if !ENABLE_ANIM_DEBUG
|
||||
float RootMotionDeltaAngleRad;
|
||||
#endif
|
||||
// Counter the interpolated orientation angle by the root motion direction angle delta.
|
||||
// This prevents our interpolation from fighting the natural root motion that's flowing through the graph.
|
||||
RootMotionDeltaAngleRad = UE::Anim::SignedAngleRadBetweenNormals(RootMotionDeltaDirection, PreviousRootMotionDeltaDirection, RotationAxisVector);
|
||||
// Root motion may have large deltas i.e. bad blends or sudden direction changes like pivots.
|
||||
// If there's an instantaneous pop in root motion direction, this is likely a pivot.
|
||||
const float MaxRootMotionDeltaToCompensateRad = FMath::DegreesToRadians(MaxRootMotionDeltaToCompensateDegrees);
|
||||
if (FMath::Abs(RootMotionDeltaAngleRad) < MaxRootMotionDeltaToCompensateRad)
|
||||
{
|
||||
ActualOrientationAngleRad = FMath::UnwindRadians(ActualOrientationAngleRad + RootMotionDeltaAngleRad);
|
||||
}
|
||||
}
|
||||
|
||||
// Rotate the root motion delta fully by the warped angle
|
||||
const FVector WarpedRootMotionTranslationDelta = FQuat(RotationAxisVector, TargetOrientationAngleRad).RotateVector(RootMotionDeltaTranslation);
|
||||
RootMotionTransformDelta.SetTranslation(WarpedRootMotionTranslationDelta);
|
||||
}
|
||||
|
||||
// Forward the side effects of orientation warping on the root motion contribution for this sub-graph
|
||||
const bool bRootMotionOverridden = RootMotionProvider->OverrideRootMotion(RootMotionTransformDelta, Output.CustomAttributes);
|
||||
ensureMsgf(bRootMotionOverridden, TEXT("Graph driven Orientation Warping expected a root motion delta to be present in the attribute stream prior to warping/overriding it."));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Early exit on missing root motion delta attribute
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Manual orientation warping will take the angle directly
|
||||
TargetOrientationAngleRad = FRotator::NormalizeAxis(OrientationAngle);
|
||||
TargetOrientationAngleRad = FMath::DegreesToRadians(TargetOrientationAngleRad);
|
||||
}
|
||||
|
||||
// Optionally interpolate the effective orientation towards the target orientation angle
|
||||
// When the orientation warping node becomes relevant, the input pose orientation may not be aligned with the desired orientation.
|
||||
// Instead of interpolating this difference, snap to the desired orientation if it's our first update to minimize corrections over-time.
|
||||
if ((RotationInterpSpeed > 0.f) && !bIsFirstUpdate)
|
||||
{
|
||||
const float SmoothOrientationAngleRad = FMath::FInterpTo(ActualOrientationAngleRad, TargetOrientationAngleRad, DeltaSeconds, RotationInterpSpeed);
|
||||
// Limit our interpolation rate to prevent pops.
|
||||
// @TODO: Use better, more physically accurate interpolation here.
|
||||
ActualOrientationAngleRad = FMath::Clamp(SmoothOrientationAngleRad, ActualOrientationAngleRad - MaxAngleCorrectionRad, ActualOrientationAngleRad + MaxAngleCorrectionRad);
|
||||
}
|
||||
else
|
||||
{
|
||||
ActualOrientationAngleRad = TargetOrientationAngleRad;
|
||||
}
|
||||
|
||||
ActualOrientationAngleRad = FMath::Clamp(ActualOrientationAngleRad, -MaxAngleCorrectionRad, MaxAngleCorrectionRad);
|
||||
// Allow the alpha value of the node to affect the final rotation
|
||||
ActualOrientationAngleRad *= ActualAlpha;
|
||||
|
||||
if (bScaleByGlobalBlendWeight)
|
||||
{
|
||||
ActualOrientationAngleRad *= BlendWeight;
|
||||
}
|
||||
|
||||
#if ENABLE_ANIM_DEBUG
|
||||
bool bDebugging = false;
|
||||
#if WITH_EDITORONLY_DATA
|
||||
bDebugging = bDebugging || bEnableDebugDraw;
|
||||
#else
|
||||
constexpr float DebugDrawScale = 1.f;
|
||||
#endif
|
||||
const int32 DebugIndex = CVarAnimNodeOrientationWarpingDebug.GetValueOnAnyThread();
|
||||
bDebugging = bDebugging || (DebugIndex > 0);
|
||||
|
||||
if (bDebugging)
|
||||
{
|
||||
const FTransform ComponentTransform = Output.AnimInstanceProxy->GetComponentTransform();
|
||||
const FVector ActorForwardDirection = Output.AnimInstanceProxy->GetActorTransform().GetRotation().GetForwardVector();
|
||||
FVector DebugArrowOffset = FVector::ZAxisVector * DebugDrawScale;
|
||||
|
||||
// Draw debug shapes
|
||||
{
|
||||
const FVector ForwardDirection = bGraphDrivenWarping
|
||||
? ComponentTransform.GetRotation().RotateVector(LocomotionForward)
|
||||
: ActorForwardDirection;
|
||||
|
||||
Output.AnimInstanceProxy->AnimDrawDebugDirectionalArrow(
|
||||
ComponentTransform.GetLocation() + DebugArrowOffset,
|
||||
ComponentTransform.GetLocation() + DebugArrowOffset + ForwardDirection * 100.f * DebugDrawScale,
|
||||
40.f * DebugDrawScale, FColor::Red, false, 0.f, 2.f * DebugDrawScale);
|
||||
|
||||
const FVector RotationDirection = bGraphDrivenWarping
|
||||
? ComponentTransform.GetRotation().RotateVector(RootMotionDeltaDirection)
|
||||
: ActorForwardDirection.RotateAngleAxis(OrientationAngle, RotationAxisVector);
|
||||
|
||||
DebugArrowOffset += FVector::ZAxisVector * DebugDrawScale;
|
||||
Output.AnimInstanceProxy->AnimDrawDebugDirectionalArrow(
|
||||
ComponentTransform.GetLocation() + DebugArrowOffset,
|
||||
ComponentTransform.GetLocation() + DebugArrowOffset + RotationDirection * 100.f * DebugDrawScale,
|
||||
40.f * DebugDrawScale, FColor::Blue, false, 0.f, 2.f * DebugDrawScale);
|
||||
|
||||
const float ActualOrientationAngleDegrees = FMath::RadiansToDegrees(ActualOrientationAngleRad);
|
||||
const FVector WarpedRotationDirection = bGraphDrivenWarping
|
||||
? RotationDirection.RotateAngleAxis(ActualOrientationAngleDegrees, RotationAxisVector)
|
||||
: ActorForwardDirection.RotateAngleAxis(ActualOrientationAngleDegrees, RotationAxisVector);
|
||||
|
||||
DebugArrowOffset += FVector::ZAxisVector * DebugDrawScale;
|
||||
Output.AnimInstanceProxy->AnimDrawDebugDirectionalArrow(
|
||||
ComponentTransform.GetLocation() + DebugArrowOffset,
|
||||
ComponentTransform.GetLocation() + DebugArrowOffset + WarpedRotationDirection * 100.f * DebugDrawScale,
|
||||
40.f * DebugDrawScale, FColor::Green, false, 0.f, 2.f * DebugDrawScale);
|
||||
}
|
||||
|
||||
// Draw text on mesh in world space
|
||||
{
|
||||
TStringBuilder<1024> DebugLine;
|
||||
|
||||
const float PreviousOrientationAngleDegrees = FMath::RadiansToDegrees(PreviousOrientationAngleRad);
|
||||
const float ActualOrientationAngleDegrees = FMath::RadiansToDegrees(ActualOrientationAngleRad);
|
||||
const float TargetOrientationAngleDegrees = FMath::RadiansToDegrees(TargetOrientationAngleRad);
|
||||
if (Mode == EWarpingEvaluationMode::Manual)
|
||||
{
|
||||
DebugLine.Appendf(TEXT("\n - Previous Orientation Angle: (%.3fd)"), PreviousOrientationAngleDegrees);
|
||||
DebugLine.Appendf(TEXT("\n - Orientation Angle: (%.3fd)"), ActualOrientationAngleDegrees);
|
||||
DebugLine.Appendf(TEXT("\n - Target Orientation Angle: (%.3fd)"), TargetOrientationAngleRad);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (RotationInterpSpeed > 0.0f)
|
||||
{
|
||||
DebugLine.Appendf(TEXT("\n - Previous Orientation Angle: (%.3fd)"), FMath::RadiansToDegrees(PreviousOrientationAngleRad));
|
||||
DebugLine.Appendf(TEXT("\n - Root Motion Frame Delta Angle: (%.3fd)"), FMath::RadiansToDegrees(RootMotionDeltaAngleRad));
|
||||
}
|
||||
DebugLine.Appendf(TEXT("\n - Actual Orientation Angle: (%.3fd)"), FMath::RadiansToDegrees(ActualOrientationAngleRad));
|
||||
DebugLine.Appendf(TEXT("\n - Target Orientation Angle: (%.3fd)"), FMath::RadiansToDegrees(TargetOrientationAngleRad));
|
||||
// Locomotion angle is already in degrees.
|
||||
DebugLine.Appendf(TEXT("\n - Locomotion Angle: (%.3fd)"), LocomotionAngle);
|
||||
DebugLine.Appendf(TEXT("\n - Root Motion Delta: %s)"), *RootMotionTransformDelta.GetTranslation().ToString());
|
||||
DebugLine.Appendf(TEXT("\n - Root Motion Speed: %.3fd)"), RootMotionTransformDelta.GetTranslation().Size() / DeltaSeconds);
|
||||
}
|
||||
Output.AnimInstanceProxy->AnimDrawDebugInWorldMessage(DebugLine.ToString(), FVector::UpVector * 50.0f, FColor::Yellow, 1.f /*TextScale*/);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ANIM_TRACE_ENABLED
|
||||
{
|
||||
const float PreviousOrientationAngleDegrees = FMath::RadiansToDegrees(PreviousOrientationAngleRad);
|
||||
const float ActualOrientationAngleDegrees = FMath::RadiansToDegrees(ActualOrientationAngleRad);
|
||||
const float TargetOrientationAngleDegrees = FMath::RadiansToDegrees(TargetOrientationAngleRad);
|
||||
|
||||
TRACE_ANIM_NODE_VALUE(Output, TEXT("Previous OrientationAngle Degrees"), PreviousOrientationAngleDegrees);
|
||||
TRACE_ANIM_NODE_VALUE(Output, TEXT("Actual Orientation Angle Degrees"), ActualOrientationAngleDegrees);
|
||||
TRACE_ANIM_NODE_VALUE(Output, TEXT("Target Orientation Angle Degrees"), TargetOrientationAngleDegrees);
|
||||
|
||||
if (Mode == EWarpingEvaluationMode::Graph)
|
||||
{
|
||||
const float RootMotionDeltaAngleDegrees = FMath::RadiansToDegrees(RootMotionDeltaAngleRad);
|
||||
TRACE_ANIM_NODE_VALUE(Output, TEXT("Root Motion Delta Angle Degrees"), RootMotionDeltaAngleDegrees);
|
||||
TRACE_ANIM_NODE_VALUE(Output, TEXT("Locomotion Angle"), LocomotionAngle);
|
||||
TRACE_ANIM_NODE_VALUE(Output, TEXT("Root Motion Translation Delta"), RootMotionTransformDelta.GetTranslation());
|
||||
|
||||
const float RootMotionSpeed = RootMotionTransformDelta.GetTranslation().Size() / DeltaSeconds;
|
||||
TRACE_ANIM_NODE_VALUE(Output, TEXT("Root Motion Speed"), RootMotionSpeed);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if ENABLE_VISUAL_LOG
|
||||
if (FVisualLogger::IsRecording())
|
||||
{
|
||||
const FTransform ComponentTransform = Output.AnimInstanceProxy->GetComponentTransform();
|
||||
const FVector ActorForwardDirection = Output.AnimInstanceProxy->GetActorTransform().GetRotation().GetForwardVector();
|
||||
FVector DebugArrowOffset = FVector::ZAxisVector * DebugDrawScale;
|
||||
|
||||
// Draw debug shapes
|
||||
{
|
||||
const FVector ForwardDirection = bGraphDrivenWarping
|
||||
? ComponentTransform.GetRotation().RotateVector(LocomotionForward)
|
||||
: ActorForwardDirection;
|
||||
|
||||
UE_VLOG_ARROW(Output.AnimInstanceProxy->GetAnimInstanceObject(), "OrientationWarping", Display,
|
||||
ComponentTransform.GetLocation() + DebugArrowOffset,
|
||||
ComponentTransform.GetLocation() + DebugArrowOffset + ForwardDirection * 100.f * DebugDrawScale,
|
||||
FColor::Red, TEXT(""));
|
||||
|
||||
const FVector RotationDirection = bGraphDrivenWarping
|
||||
? ComponentTransform.GetRotation().RotateVector(RootMotionDeltaDirection)
|
||||
: ActorForwardDirection.RotateAngleAxis(OrientationAngle, RotationAxisVector);
|
||||
|
||||
DebugArrowOffset += FVector::ZAxisVector * DebugDrawScale;
|
||||
UE_VLOG_ARROW(Output.AnimInstanceProxy->GetAnimInstanceObject(), "OrientationWarping", Display,
|
||||
ComponentTransform.GetLocation() + DebugArrowOffset,
|
||||
ComponentTransform.GetLocation() + DebugArrowOffset + RotationDirection * 100.f * DebugDrawScale,
|
||||
FColor::Blue, TEXT(""));
|
||||
|
||||
const float ActualOrientationAngleDegrees = FMath::RadiansToDegrees(ActualOrientationAngleRad);
|
||||
const FVector WarpedRotationDirection = bGraphDrivenWarping
|
||||
? RotationDirection.RotateAngleAxis(ActualOrientationAngleDegrees, RotationAxisVector)
|
||||
: ActorForwardDirection.RotateAngleAxis(ActualOrientationAngleDegrees, RotationAxisVector);
|
||||
|
||||
DebugArrowOffset += FVector::ZAxisVector * DebugDrawScale;
|
||||
|
||||
UE_VLOG_ARROW(Output.AnimInstanceProxy->GetAnimInstanceObject(), "OrientationWarping", Display,
|
||||
ComponentTransform.GetLocation() + DebugArrowOffset,
|
||||
ComponentTransform.GetLocation() + DebugArrowOffset + WarpedRotationDirection * 100.f * DebugDrawScale,
|
||||
FColor::Green, TEXT(""));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
const float RootOffset = FMath::UnwindRadians(ActualOrientationAngleRad * DistributedBoneOrientationAlpha);
|
||||
|
||||
// Rotate Root Bone first, as that cheaply rotates the whole pose with one transformation.
|
||||
if (!FMath::IsNearlyZero(RootOffset, KINDA_SMALL_NUMBER))
|
||||
{
|
||||
const FQuat RootRotation = FQuat(RotationAxisVector, RootOffset);
|
||||
const FCompactPoseBoneIndex RootBoneIndex(0);
|
||||
|
||||
FTransform RootBoneTransform(Output.Pose.GetComponentSpaceTransform(RootBoneIndex));
|
||||
RootBoneTransform.SetRotation(RootRotation * RootBoneTransform.GetRotation());
|
||||
RootBoneTransform.NormalizeRotation();
|
||||
Output.Pose.SetComponentSpaceTransform(RootBoneIndex, RootBoneTransform);
|
||||
}
|
||||
|
||||
const int32 NumSpineBones = SpineBoneDataArray.Num();
|
||||
const bool bSpineOrientationAlpha = !FMath::IsNearlyZero(DistributedBoneOrientationAlpha, KINDA_SMALL_NUMBER);
|
||||
const bool bUpdateSpineBones = (NumSpineBones > 0) && bSpineOrientationAlpha;
|
||||
|
||||
if (bUpdateSpineBones)
|
||||
{
|
||||
// Spine bones counter rotate body orientation evenly across all bones.
|
||||
for (int32 ArrayIndex = 0; ArrayIndex < NumSpineBones; ArrayIndex++)
|
||||
{
|
||||
const FOrientationWarpingSpineBoneData& BoneData = SpineBoneDataArray[ArrayIndex];
|
||||
const FQuat SpineBoneCounterRotation = FQuat(RotationAxisVector, -ActualOrientationAngleRad * DistributedBoneOrientationAlpha * BoneData.Weight);
|
||||
check(BoneData.Weight > 0.f);
|
||||
|
||||
FTransform SpineBoneTransform(Output.Pose.GetComponentSpaceTransform(BoneData.BoneIndex));
|
||||
SpineBoneTransform.SetRotation((SpineBoneCounterRotation * SpineBoneTransform.GetRotation()));
|
||||
SpineBoneTransform.NormalizeRotation();
|
||||
Output.Pose.SetComponentSpaceTransform(BoneData.BoneIndex, SpineBoneTransform);
|
||||
}
|
||||
}
|
||||
|
||||
const float IKFootRootOrientationAlpha = 1.f - DistributedBoneOrientationAlpha;
|
||||
const bool bUpdateIKFootRoot = (IKFootData.IKFootRootBoneIndex != FCompactPoseBoneIndex(INDEX_NONE)) && !FMath::IsNearlyZero(IKFootRootOrientationAlpha, KINDA_SMALL_NUMBER);
|
||||
|
||||
// Rotate IK Foot Root
|
||||
if (bUpdateIKFootRoot)
|
||||
{
|
||||
const FQuat BoneRotation = FQuat(RotationAxisVector, ActualOrientationAngleRad * IKFootRootOrientationAlpha);
|
||||
|
||||
FTransform IKFootRootTransform(Output.Pose.GetComponentSpaceTransform(IKFootData.IKFootRootBoneIndex));
|
||||
IKFootRootTransform.SetRotation(BoneRotation * IKFootRootTransform.GetRotation());
|
||||
IKFootRootTransform.NormalizeRotation();
|
||||
Output.Pose.SetComponentSpaceTransform(IKFootData.IKFootRootBoneIndex, IKFootRootTransform);
|
||||
|
||||
// IK Feet
|
||||
// These match the root orientation, so don't rotate them. Just preserve root rotation.
|
||||
// We need to update their translation though, since we rotated their parent (the IK Foot Root bone).
|
||||
const int32 NumIKFootBones = IKFootData.IKFootBoneIndexArray.Num();
|
||||
const bool bUpdateIKFootBones = bUpdateIKFootRoot && (NumIKFootBones > 0);
|
||||
|
||||
if (bUpdateIKFootBones)
|
||||
{
|
||||
const FQuat IKFootRotation = FQuat(RotationAxisVector, -ActualOrientationAngleRad * IKFootRootOrientationAlpha);
|
||||
|
||||
for (int32 ArrayIndex = 0; ArrayIndex < NumIKFootBones; ArrayIndex++)
|
||||
{
|
||||
const FCompactPoseBoneIndex& IKFootBoneIndex = IKFootData.IKFootBoneIndexArray[ArrayIndex];
|
||||
|
||||
FTransform IKFootBoneTransform(Output.Pose.GetComponentSpaceTransform(IKFootBoneIndex));
|
||||
IKFootBoneTransform.SetRotation(IKFootRotation * IKFootBoneTransform.GetRotation());
|
||||
IKFootBoneTransform.NormalizeRotation();
|
||||
Output.Pose.SetComponentSpaceTransform(IKFootBoneIndex, IKFootBoneTransform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OutBoneTransforms.Sort(FCompareBoneTransformIndex());
|
||||
bIsFirstUpdate = false;
|
||||
}
|
||||
|
||||
bool FGMS_AnimNode_OrientationWarping::IsValidToEvaluate(const USkeleton* Skeleton, const FBoneContainer& RequiredBones)
|
||||
{
|
||||
#if ENABLE_ANIM_DEBUG
|
||||
if (CVarAnimNodeOrientationWarpingEnable.GetValueOnAnyThread() == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
if (RotationAxis == EAxis::None)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Mode == EWarpingEvaluationMode::Manual && UE::Anim::IsInvalidWarpingAngleDegrees(OrientationAngle, KINDA_SMALL_NUMBER))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SpineBoneDataArray.IsEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto& Spine : SpineBoneDataArray)
|
||||
{
|
||||
if (Spine.BoneIndex == INDEX_NONE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (IKFootData.IKFootRootBoneIndex == INDEX_NONE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IKFootData.IKFootBoneIndexArray.IsEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto& IKFootBoneIndex : IKFootData.IKFootBoneIndexArray)
|
||||
{
|
||||
if (IKFootBoneIndex == INDEX_NONE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void FGMS_AnimNode_OrientationWarping::InitializeBoneReferences(const FBoneContainer& RequiredBones)
|
||||
{
|
||||
ExternalBoneReference.IKFootRootBone.Initialize(RequiredBones);
|
||||
IKFootData.IKFootRootBoneIndex = ExternalBoneReference.IKFootRootBone.GetCompactPoseIndex(RequiredBones);
|
||||
|
||||
IKFootData.IKFootBoneIndexArray.Reset();
|
||||
for (auto& Bone : ExternalBoneReference.IKFootBones)
|
||||
{
|
||||
Bone.Initialize(RequiredBones);
|
||||
IKFootData.IKFootBoneIndexArray.Add(Bone.GetCompactPoseIndex(RequiredBones));
|
||||
}
|
||||
|
||||
SpineBoneDataArray.Reset();
|
||||
for (auto& Bone : ExternalBoneReference.SpineBones)
|
||||
{
|
||||
Bone.Initialize(RequiredBones);
|
||||
SpineBoneDataArray.Add(FOrientationWarpingSpineBoneData(Bone.GetCompactPoseIndex(RequiredBones)));
|
||||
}
|
||||
|
||||
if (SpineBoneDataArray.Num() > 0)
|
||||
{
|
||||
// Sort bones indices so we can transform parent before child
|
||||
SpineBoneDataArray.Sort(FOrientationWarpingSpineBoneData::FCompareBoneIndex());
|
||||
|
||||
// Assign Weights.
|
||||
TArray<int32, TInlineAllocator<20>> IndicesToUpdate;
|
||||
|
||||
for (int32 Index = SpineBoneDataArray.Num() - 1; Index >= 0; Index--)
|
||||
{
|
||||
// If this bone's weight hasn't been updated, scan its parents.
|
||||
// If parents have weight, we add it to 'ExistingWeight'.
|
||||
// split (1.f - 'ExistingWeight') between all members of the chain that have no weight yet.
|
||||
if (SpineBoneDataArray[Index].Weight == 0.f)
|
||||
{
|
||||
IndicesToUpdate.Reset(SpineBoneDataArray.Num());
|
||||
float ExistingWeight = 0.f;
|
||||
IndicesToUpdate.Add(Index);
|
||||
|
||||
const FCompactPoseBoneIndex CompactBoneIndex = SpineBoneDataArray[Index].BoneIndex;
|
||||
for (int32 ParentIndex = Index - 1; ParentIndex >= 0; ParentIndex--)
|
||||
{
|
||||
if (RequiredBones.BoneIsChildOf(CompactBoneIndex, SpineBoneDataArray[ParentIndex].BoneIndex))
|
||||
{
|
||||
if (SpineBoneDataArray[ParentIndex].Weight > 0.f)
|
||||
{
|
||||
ExistingWeight += SpineBoneDataArray[ParentIndex].Weight;
|
||||
}
|
||||
else
|
||||
{
|
||||
IndicesToUpdate.Add(ParentIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
check(IndicesToUpdate.Num() > 0);
|
||||
const float WeightToShare = 1.f - ExistingWeight;
|
||||
const float IndividualWeight = WeightToShare / float(IndicesToUpdate.Num());
|
||||
|
||||
for (int32 UpdateListIndex = 0; UpdateListIndex < IndicesToUpdate.Num(); UpdateListIndex++)
|
||||
{
|
||||
SpineBoneDataArray[IndicesToUpdate[UpdateListIndex]].Weight = IndividualWeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FGMS_AnimNode_OrientationWarping::Reset(const FAnimationBaseContext& Context)
|
||||
{
|
||||
bIsFirstUpdate = true;
|
||||
RootMotionDeltaDirection = FVector::ZeroVector;
|
||||
ManualRootMotionVelocity = FVector::ZeroVector;
|
||||
ActualOrientationAngleRad = 0.f;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Settings/GMS_SettingEnumLibrary.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_SettingEnumLibrary)
|
||||
@@ -0,0 +1,277 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Settings/GMS_SettingObjectLibrary.h"
|
||||
#include "Locomotions/GMS_AnimLayer.h"
|
||||
#include "Animation/BlendSpace.h"
|
||||
#include "Locomotions/GMS_AnimLayer_Additive.h"
|
||||
#include "Locomotions/GMS_AnimLayer_Overlay.h"
|
||||
#include "Locomotions/GMS_AnimLayer_SkeletalControls.h"
|
||||
#include "Locomotions/GMS_AnimLayer_States.h"
|
||||
#include "Locomotions/GMS_AnimLayer_View_Default.h"
|
||||
#include "Misc/DataValidation.h"
|
||||
#include "Utility/GMS_Utility.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_SettingObjectLibrary)
|
||||
|
||||
#pragma region CommonSettings
|
||||
|
||||
#if WITH_EDITOR
|
||||
#include "UObject/ObjectSaveContext.h"
|
||||
|
||||
void UGMS_MovementDefinition::PreSave(FObjectPreSaveContext SaveContext)
|
||||
{
|
||||
Super::PreSave(SaveContext);
|
||||
}
|
||||
|
||||
EDataValidationResult UGMS_MovementDefinition::IsDataValid(class FDataValidationContext& Context) const
|
||||
{
|
||||
for (const TTuple<FGameplayTag, FGMS_MovementSetSetting>& Pair : MovementSets)
|
||||
{
|
||||
if (Pair.Value.ControlSetting == nullptr)
|
||||
{
|
||||
Context.AddError(FText::FromString(FString::Format(TEXT("ControlSetting is required on {0}!!!"), {Pair.Key.GetTagName().ToString()})));
|
||||
}
|
||||
|
||||
if (Pair.Value.bUseInstancedStatesSetting && Pair.Value.AnimLayerSetting_States && Pair.Value.AnimLayerSetting_States->IsDataValid(Context) == EDataValidationResult::Invalid)
|
||||
{
|
||||
return EDataValidationResult::Invalid;
|
||||
}
|
||||
|
||||
if (Pair.Value.bUseInstancedOverlaySetting && Pair.Value.AnimLayerSetting_Overlay && Pair.Value.AnimLayerSetting_Overlay->IsDataValid(Context) == EDataValidationResult::Invalid)
|
||||
{
|
||||
return EDataValidationResult::Invalid;
|
||||
}
|
||||
|
||||
if (Pair.Value.AnimLayerSetting_Additive && Pair.Value.AnimLayerSetting_Additive->IsDataValid(Context) == EDataValidationResult::Invalid)
|
||||
{
|
||||
return EDataValidationResult::Invalid;
|
||||
}
|
||||
|
||||
if (Pair.Value.AnimLayerSetting_View && Pair.Value.AnimLayerSetting_View->IsDataValid(Context) == EDataValidationResult::Invalid)
|
||||
{
|
||||
return EDataValidationResult::Invalid;
|
||||
}
|
||||
|
||||
if (Pair.Value.AnimLayerSetting_SkeletalControls && Pair.Value.AnimLayerSetting_SkeletalControls->IsDataValid(Context) == EDataValidationResult::Invalid)
|
||||
{
|
||||
return EDataValidationResult::Invalid;
|
||||
}
|
||||
}
|
||||
return Super::IsDataValid(Context);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
FGameplayTag UGMS_MovementControlSetting_Default::MatchStateTagBySpeed(float Speed, float Threshold) const
|
||||
{
|
||||
for (const FGMS_MovementStateSetting& MovementState : MovementStates)
|
||||
{
|
||||
if (MovementState.Speed > 0.0f && MovementState.Speed < Speed + Threshold)
|
||||
{
|
||||
return MovementState.Tag;
|
||||
}
|
||||
}
|
||||
return FGameplayTag::EmptyTag;
|
||||
}
|
||||
|
||||
bool UGMS_MovementControlSetting_Default::GetStateByIndex(const int32& Index, FGMS_MovementStateSetting& OutSetting) const
|
||||
{
|
||||
if (MovementStates.IsValidIndex(Index))
|
||||
{
|
||||
OutSetting = MovementStates[Index];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UGMS_MovementControlSetting_Default::GetStateBySpeedLevel(const int32& Level, FGMS_MovementStateSetting& OutSetting) const
|
||||
{
|
||||
if (SpeedLevelToArrayIndex.Contains(Level))
|
||||
{
|
||||
OutSetting = MovementStates[SpeedLevelToArrayIndex[Level]];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UGMS_MovementControlSetting_Default::GetStateByTag(const FGameplayTag& Tag, FGMS_MovementStateSetting& OutSetting) const
|
||||
{
|
||||
if (auto Setting = GetMovementStateSetting(Tag))
|
||||
{
|
||||
OutSetting = *Setting;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const FGMS_MovementStateSetting* UGMS_MovementControlSetting_Default::GetMovementStateSetting(const FGameplayTag& Tag) const
|
||||
{
|
||||
if (!Tag.IsValid())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return MovementStates.FindByPredicate([Tag](const FGMS_MovementStateSetting& Setting)
|
||||
{
|
||||
return Setting.Tag.IsValid() && Setting.Tag == Tag;
|
||||
});
|
||||
}
|
||||
|
||||
const FGMS_MovementStateSetting* UGMS_MovementControlSetting_Default::GetMovementStateSetting(const FGameplayTag& Tag, bool bHasFallback) const
|
||||
{
|
||||
if (auto Setting = GetMovementStateSetting(Tag))
|
||||
{
|
||||
return Setting;
|
||||
}
|
||||
|
||||
if (bHasFallback)
|
||||
{
|
||||
checkf(!MovementStates.IsEmpty(), TEXT("%s: MovementStates can't be empty!"), *GetNameSafe(this))
|
||||
return &MovementStates.Last();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region ControlSettings
|
||||
|
||||
#if WITH_EDITOR
|
||||
|
||||
float UGMS_MovementControlSetting_Default::MigrateRotationInterpolationSpeed(float Old)
|
||||
{
|
||||
if (Old <= 0.0f)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
// larger old value, smaller new value.
|
||||
return FMath::GetMappedRangeValueClamped(FVector2f{0.0f, 20.0f}, FVector2f{0.3f, 0.1f}, Old);
|
||||
}
|
||||
|
||||
void UGMS_MovementControlSetting_Default::PreSave(FObjectPreSaveContext SaveContext)
|
||||
{
|
||||
Super::PreSave(SaveContext);
|
||||
|
||||
SpeedLevelToArrayIndex.Empty();
|
||||
|
||||
MovementStates.Sort([](const FGMS_MovementStateSetting& A, const FGMS_MovementStateSetting& B)
|
||||
{
|
||||
return A.SpeedLevel < B.SpeedLevel;
|
||||
});
|
||||
|
||||
for (int i = 0; i < MovementStates.Num(); ++i)
|
||||
{
|
||||
FGMS_MovementStateSetting& Setting = MovementStates[i];
|
||||
Setting.EditorFriendlyName = FString::Format(TEXT("State({0}) SpeedLevel({1}) Speed({2})"), {UGMS_Utility::GetSimpleTagName(Setting.Tag).ToString(), Setting.SpeedLevel, Setting.Speed});
|
||||
SpeedLevelToArrayIndex.Emplace(Setting.SpeedLevel, i);
|
||||
}
|
||||
|
||||
// Migration code for GMS1.5, TODO remove in 1.6
|
||||
PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
||||
if (MovementStates.Num() > 0)
|
||||
{
|
||||
const FGMS_MovementStateSetting& LastMovementState = MovementStates[MovementStates.Num() - 1];
|
||||
|
||||
// migrate velocity direction setting.
|
||||
if (!VelocityDirectionSetting.IsValid())
|
||||
{
|
||||
if (LastMovementState.VelocityDirectionSetting.DirectionMode != EGMS_VelocityDirectionMode_DEPRECATED::TurningCircle)
|
||||
{
|
||||
FGMS_VelocityDirectionSetting_Default Temp;
|
||||
Temp.bEnableRotationWhenNotMoving = LastMovementState.VelocityDirectionSetting.bEnableRotationWhenNotMoving;
|
||||
Temp.TargetYawAngleRotationSpeed = LastMovementState.TargetYawAngleRotationSpeed;
|
||||
Temp.RotationInterpolationSpeed = MigrateRotationInterpolationSpeed(LastMovementState.RotationInterpolationSpeed);
|
||||
Temp.bOrientateToMoveInputIntent = LastMovementState.VelocityDirectionSetting.DirectionMode == EGMS_VelocityDirectionMode_DEPRECATED::OrientToInputDirection;
|
||||
|
||||
VelocityDirectionSetting.InitializeAs<FGMS_VelocityDirectionSetting_Default>(Temp);
|
||||
}
|
||||
else if (LastMovementState.VelocityDirectionSetting.DirectionMode == EGMS_VelocityDirectionMode_DEPRECATED::TurningCircle)
|
||||
{
|
||||
FGMS_VelocityDirectionSetting_RateBased Temp;
|
||||
Temp.bEnableRotationWhenNotMoving = LastMovementState.VelocityDirectionSetting.bEnableRotationWhenNotMoving;
|
||||
Temp.TurnRate = LastMovementState.VelocityDirectionSetting.TurningRate;
|
||||
VelocityDirectionSetting.InitializeAs<FGMS_VelocityDirectionSetting_RateBased>(Temp);
|
||||
}
|
||||
}
|
||||
|
||||
// migrate view direction setting.
|
||||
if (!ViewDirectionSetting.IsValid())
|
||||
{
|
||||
if (LastMovementState.ViewDirectionSetting.DirectionMode == EGMS_ViewDirectionMode_DEPRECATED::Aiming)
|
||||
{
|
||||
FGMS_ViewDirectionSetting_Aiming Temp;
|
||||
Temp.bEnableRotationWhenNotMoving = LastMovementState.ViewDirectionSetting.bRotateToViewDirectionWhileNotMoving;
|
||||
Temp.TargetYawAngleRotationSpeed = LastMovementState.TargetYawAngleRotationSpeed;
|
||||
Temp.RotationInterpolationSpeed = MigrateRotationInterpolationSpeed(LastMovementState.RotationInterpolationSpeed);
|
||||
Temp.MinAimingYawAngleLimit = LastMovementState.ViewDirectionSetting.MinAimingYawAngleLimit;
|
||||
|
||||
ViewDirectionSetting.InitializeAs<FGMS_ViewDirectionSetting_Aiming>(Temp);
|
||||
}
|
||||
else if (LastMovementState.ViewDirectionSetting.DirectionMode == EGMS_ViewDirectionMode_DEPRECATED::Default)
|
||||
{
|
||||
FGMS_ViewDirectionSetting_Default Temp;
|
||||
Temp.bEnableRotationWhenNotMoving = LastMovementState.ViewDirectionSetting.bRotateToViewDirectionWhileNotMoving;
|
||||
Temp.TargetYawAngleRotationSpeed = LastMovementState.TargetYawAngleRotationSpeed;
|
||||
Temp.RotationInterpolationSpeed = MigrateRotationInterpolationSpeed(LastMovementState.RotationInterpolationSpeed);
|
||||
ViewDirectionSetting.InitializeAs<FGMS_ViewDirectionSetting_Default>(Temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
||||
|
||||
// Safety guard if still invalid.
|
||||
if (!VelocityDirectionSetting.IsValid())
|
||||
{
|
||||
VelocityDirectionSetting.InitializeAs(FGMS_VelocityDirectionSetting_Default());
|
||||
}
|
||||
if (!ViewDirectionSetting.IsValid())
|
||||
{
|
||||
ViewDirectionSetting.InitializeAs(FGMS_ViewDirectionSetting_Default());
|
||||
}
|
||||
}
|
||||
|
||||
EDataValidationResult UGMS_MovementControlSetting_Default::IsDataValid(class FDataValidationContext& Context) const
|
||||
{
|
||||
for (int32 i = 0; i < MovementStates.Num(); i++)
|
||||
{
|
||||
const FGMS_MovementStateSetting& MRSetting = MovementStates[i];
|
||||
if (!MRSetting.Tag.IsValid())
|
||||
{
|
||||
Context.AddError(FText::FromString(FString::Format(TEXT("Invalid tag at index({0}) of MovementStates"), {i})));
|
||||
return EDataValidationResult::Invalid;
|
||||
}
|
||||
if (MRSetting.AllowedRotationModes.IsEmpty())
|
||||
{
|
||||
Context.AddError(FText::FromString(
|
||||
FString::Format(TEXT("AllowedRotationModes at index({0}) of MovementStates can't be empty!"), {i})));
|
||||
return EDataValidationResult::Invalid;
|
||||
}
|
||||
}
|
||||
if (!ViewDirectionSetting.IsValid())
|
||||
{
|
||||
Context.AddError(FText::FromString(TEXT("Invalid view direction setting")));
|
||||
return EDataValidationResult::Invalid;
|
||||
}
|
||||
if (ViewDirectionSetting.IsValid() && ViewDirectionSetting.GetScriptStruct() == FGMS_ViewDirectionSetting::StaticStruct())
|
||||
{
|
||||
Context.AddError(FText::FromString(FString::Format(TEXT("View direction setting({0}) was deprecated!"), {FGMS_ViewDirectionSetting::StaticStruct()->GetName()})));
|
||||
return EDataValidationResult::Invalid;
|
||||
}
|
||||
|
||||
if (!VelocityDirectionSetting.IsValid())
|
||||
{
|
||||
Context.AddError(FText::FromString(TEXT("Invalid velocity direction setting")));
|
||||
return EDataValidationResult::Invalid;
|
||||
}
|
||||
if (VelocityDirectionSetting.IsValid() && VelocityDirectionSetting.GetScriptStruct() == FGMS_VelocityDirectionSetting::StaticStruct())
|
||||
{
|
||||
Context.AddError(FText::FromString(FString::Format(TEXT("Velocity direction setting({0}) was deprecated!"), {FGMS_VelocityDirectionSetting::StaticStruct()->GetName()})));
|
||||
return EDataValidationResult::Invalid;
|
||||
}
|
||||
|
||||
return Super::IsDataValid(Context);
|
||||
}
|
||||
#endif
|
||||
|
||||
#pragma endregion
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Settings/GMS_SettingStructLibrary.h"
|
||||
|
||||
#include "Settings/GMS_SettingObjectLibrary.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_SettingStructLibrary)
|
||||
|
||||
|
||||
#if WITH_EDITOR
|
||||
void FGMS_AnimDataSetting_General::PostEditChangeProperty(const FPropertyChangedEvent& PropertyChangedEvent)
|
||||
{
|
||||
if (PropertyChangedEvent.GetPropertyName() != GET_MEMBER_NAME_CHECKED(FGMS_AnimDataSetting_General, GroundPredictionResponseChannels))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GroundPredictionSweepResponses.SetAllChannels(ECR_Ignore);
|
||||
|
||||
for (const auto CollisionChannel : GroundPredictionResponseChannels)
|
||||
{
|
||||
GroundPredictionSweepResponses.SetResponse(CollisionChannel, ECR_Block);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#include "Utility/GMS_Log.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "Components/ActorComponent.h"
|
||||
#include "Animation/AnimInstance.h"
|
||||
|
||||
|
||||
const FName GMSLog::MessageLogName{TEXTVIEW("GMS")};
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogGMS)
|
||||
DEFINE_LOG_CATEGORY(LogGMS_Animation)
|
||||
|
||||
FString GetGMSLogContextString(const UObject* ContextObject)
|
||||
{
|
||||
ENetRole Role = ROLE_None;
|
||||
FString RoleName = TEXT("None");
|
||||
FString Name = "None";
|
||||
|
||||
if (const AActor* Actor = Cast<AActor>(ContextObject))
|
||||
{
|
||||
Role = Actor->GetLocalRole();
|
||||
Name = Actor->GetName();
|
||||
}
|
||||
else if (const UActorComponent* Component = Cast<UActorComponent>(ContextObject))
|
||||
{
|
||||
Role = Component->GetOwnerRole();
|
||||
Name = Component->GetOwner()->GetName();
|
||||
}
|
||||
else if (const UAnimInstance* AnimInstance = Cast<UAnimInstance>(ContextObject))
|
||||
{
|
||||
if (AnimInstance->GetOwningActor())
|
||||
{
|
||||
Role = AnimInstance->GetOwningActor()->GetLocalRole();
|
||||
Name = AnimInstance->GetOwningActor()->GetName();
|
||||
}
|
||||
}
|
||||
else if (IsValid(ContextObject))
|
||||
{
|
||||
Name = ContextObject->GetName();
|
||||
}
|
||||
|
||||
if (Role != ROLE_None)
|
||||
{
|
||||
RoleName = (Role == ROLE_Authority) ? TEXT("Server") : TEXT("Client");
|
||||
}
|
||||
return FString::Printf(TEXT("[%s] (%s)"), *RoleName, *Name);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Utility/GMS_Math.h"
|
||||
|
||||
#include "Locomotions/GMS_LocomotionEnumLibrary.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_Math)
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#include "Utility/GMS_Tags.h"
|
||||
|
||||
namespace GMS_MovementModeTags
|
||||
{
|
||||
UE_DEFINE_GAMEPLAY_TAG(None, FName{TEXTVIEW("GMS.LocomotionMode.None")})
|
||||
UE_DEFINE_GAMEPLAY_TAG(Grounded, FName{TEXTVIEW("GMS.LocomotionMode.Grounded")})
|
||||
UE_DEFINE_GAMEPLAY_TAG(InAir, FName{TEXTVIEW("GMS.LocomotionMode.InAir")})
|
||||
UE_DEFINE_GAMEPLAY_TAG(Flying, FName{TEXTVIEW("GMS.LocomotionMode.Flying")})
|
||||
UE_DEFINE_GAMEPLAY_TAG(Swimming, FName{TEXTVIEW("GMS.LocomotionMode.Swimming")})
|
||||
UE_DEFINE_GAMEPLAY_TAG(Zipline, FName{TEXTVIEW("GMS.LocomotionMode.Zipline")})
|
||||
}
|
||||
|
||||
namespace GMS_RotationModeTags
|
||||
{
|
||||
UE_DEFINE_GAMEPLAY_TAG_COMMENT(VelocityDirection, FName{TEXTVIEW("GMS.RotationMode.VelocityDirection")}, "Rotate(orientate) to given velocity direction.(转向指定的的速率方向)")
|
||||
UE_DEFINE_GAMEPLAY_TAG_COMMENT(ViewDirection, FName{TEXTVIEW("GMS.RotationMode.ViewDirection")}, "Rotate(orientate) to view direction(转向看的方向)")
|
||||
}
|
||||
|
||||
namespace GMS_MovementStateTags
|
||||
{
|
||||
UE_DEFINE_GAMEPLAY_TAG(Walk, FName{TEXTVIEW("GMS.MovementState.Walk")})
|
||||
UE_DEFINE_GAMEPLAY_TAG(Jog, FName{TEXTVIEW("GMS.MovementState.Jog")})
|
||||
UE_DEFINE_GAMEPLAY_TAG(Sprint, FName{TEXTVIEW("GMS.MovementState.Sprint")})
|
||||
}
|
||||
|
||||
namespace GMS_OverlayModeTags
|
||||
{
|
||||
UE_DEFINE_GAMEPLAY_TAG(None, FName{TEXTVIEW("GMS.OverlayMode.None")})
|
||||
UE_DEFINE_GAMEPLAY_TAG(Default, FName{TEXTVIEW("GMS.OverlayMode.Default")})
|
||||
}
|
||||
|
||||
namespace GMS_SMTags
|
||||
{
|
||||
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Root, FName{TEXTVIEW("GMS.SM")}, "State Machine Root Tag");
|
||||
UE_DEFINE_GAMEPLAY_TAG_COMMENT(InAir, FName{TEXTVIEW("GMS.SM.InAir")}, "InAir States")
|
||||
UE_DEFINE_GAMEPLAY_TAG(InAir_Jump, FName{TEXTVIEW("GMS.SM.InAir.Jump")})
|
||||
UE_DEFINE_GAMEPLAY_TAG(InAir_Fall, FName{TEXTVIEW("GMS.SM.InAir.Fall")})
|
||||
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Grounded, FName{TEXTVIEW("GMS.SM.Grounded")}, "Grounded States")
|
||||
UE_DEFINE_GAMEPLAY_TAG(Grounded_Idle, FName{TEXTVIEW("GMS.SM.Grounded.Idle")})
|
||||
UE_DEFINE_GAMEPLAY_TAG(Grounded_Start, FName{TEXTVIEW("GMS.SM.Grounded.Start")})
|
||||
UE_DEFINE_GAMEPLAY_TAG(Grounded_Cycle, FName{TEXTVIEW("GMS.SM.Grounded.Cycle")})
|
||||
UE_DEFINE_GAMEPLAY_TAG(Grounded_Stop, FName{TEXTVIEW("GMS.SM.Grounded.Stop")})
|
||||
UE_DEFINE_GAMEPLAY_TAG(Grounded_Pivot, FName{TEXTVIEW("GMS.SM.Grounded.Pivot")})
|
||||
UE_DEFINE_GAMEPLAY_TAG(Grounded_Land, FName{TEXTVIEW("GMS.SM.Grounded.Land")})
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Utility/GMS_Utility.h"
|
||||
|
||||
#include "Chooser.h"
|
||||
#include "ChooserPropertyAccess.h"
|
||||
#include "PoseSearch/PoseSearchDatabase.h"
|
||||
#include "GameplayTagsManager.h"
|
||||
#include "IObjectChooser.h"
|
||||
#include "Animation/AnimInstance.h"
|
||||
#include "Components/SkeletalMeshComponent.h"
|
||||
#include "Engine/World.h"
|
||||
#include "GameFramework/Character.h"
|
||||
#include "GameFramework/HUD.h"
|
||||
#include "Animation/AnimSequenceBase.h"
|
||||
#include "Locomotions/GMS_AnimLayer.h"
|
||||
#include "Locomotions/GMS_MainAnimInstance.h"
|
||||
#include "Settings/GMS_SettingObjectLibrary.h"
|
||||
#include "Utility/GMS_Log.h"
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_Utility)
|
||||
|
||||
FString UGMS_Utility::NameToDisplayString(const FName& Name, const bool bNameIsBool)
|
||||
{
|
||||
return FName::NameToDisplayString(Name.ToString(), bNameIsBool);
|
||||
}
|
||||
|
||||
float UGMS_Utility::GetAnimationCurveValueFromCharacter(const ACharacter* Character, const FName& CurveName)
|
||||
{
|
||||
const auto* Mesh{IsValid(Character) ? Character->GetMesh() : nullptr};
|
||||
const auto* AnimationInstance{IsValid(Mesh) ? Mesh->GetAnimInstance() : nullptr};
|
||||
|
||||
return IsValid(AnimationInstance) ? AnimationInstance->GetCurveValue(CurveName) : 0.0f;
|
||||
}
|
||||
|
||||
FGameplayTagContainer UGMS_Utility::GetChildTags(const FGameplayTag& Tag)
|
||||
{
|
||||
return UGameplayTagsManager::Get().RequestGameplayTagChildren(Tag);
|
||||
}
|
||||
|
||||
FName UGMS_Utility::GetSimpleTagName(const FGameplayTag& Tag)
|
||||
{
|
||||
const auto TagNode{UGameplayTagsManager::Get().FindTagNode(Tag)};
|
||||
|
||||
return TagNode.IsValid() ? TagNode->GetSimpleTagName() : NAME_None;
|
||||
}
|
||||
|
||||
bool UGMS_Utility::ShouldDisplayDebugForActor(const AActor* Actor, const FName& DisplayName)
|
||||
{
|
||||
const auto* World{IsValid(Actor) ? Actor->GetWorld() : nullptr};
|
||||
const auto* PlayerController{IsValid(World) ? World->GetFirstPlayerController() : nullptr};
|
||||
auto* Hud{IsValid(PlayerController) ? PlayerController->GetHUD() : nullptr};
|
||||
|
||||
return IsValid(Hud) && Hud->ShouldDisplayDebug(DisplayName) && Hud->GetCurrentDebugTargetActor() == Actor;
|
||||
}
|
||||
|
||||
|
||||
float UGMS_Utility::CalculateAnimatedSpeed(const UAnimSequenceBase* AnimSequence)
|
||||
{
|
||||
if (AnimSequence == nullptr)
|
||||
{
|
||||
UE_LOG(LogGMS, Warning, TEXT("Passed invalid anim sequence"));
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
const float AnimLength = AnimSequence->GetPlayLength();
|
||||
// Calculate the speed as: (distance traveled by the animation) / (length of the animation)
|
||||
const FVector RootMotionTranslation = AnimSequence->ExtractRootMotionFromRange(0.0f, AnimLength).GetTranslation();
|
||||
const float RootMotionDistance = RootMotionTranslation.Size2D();
|
||||
if (!FMath::IsNearlyZero(RootMotionDistance))
|
||||
{
|
||||
const float AnimationSpeed = RootMotionDistance / AnimLength;
|
||||
return AnimationSpeed;
|
||||
}
|
||||
UE_LOG(LogGMS, Warning, TEXT("Unable to Calculate animation speed for animation with no root motion delta (%s)."), *GetNameSafe(AnimSequence));
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
|
||||
UAnimSequence* UGMS_Utility::SelectAnimationWithFloat(const TArray<FGMS_AnimationWithDistance>& Animations, const float& ReferenceValue)
|
||||
{
|
||||
if (Animations.IsEmpty())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
float Delta = FLT_MAX; // 使用FLT_MAX来初始化Delta
|
||||
int32 Found = INDEX_NONE;
|
||||
|
||||
for (int32 i = 0; i < Animations.Num(); i++)
|
||||
{
|
||||
// 计算与传入Float的绝对差值
|
||||
float TempDelta = FMath::Abs(ReferenceValue - Animations[i].Distance);
|
||||
|
||||
// 如果当前的差值更小,则更新Delta和Found
|
||||
if (TempDelta < Delta)
|
||||
{
|
||||
Delta = TempDelta;
|
||||
Found = i;
|
||||
}
|
||||
}
|
||||
|
||||
// 返回找到的动画或最后一个动画作为备选
|
||||
return (Found != INDEX_NONE) ? Animations[Found].Animation : Animations.Last().Animation;
|
||||
}
|
||||
|
||||
bool UGMS_Utility::ValidatePoseSearchDatabasesChooser(const UChooserTable* ChooserTable, FText& OutMessage)
|
||||
{
|
||||
if (!IsValid(ChooserTable))
|
||||
{
|
||||
OutMessage = FText::FromName("Invalid ChooserTable");
|
||||
return false;
|
||||
}
|
||||
if (ChooserTable->GetContextData().Num() != 2)
|
||||
{
|
||||
OutMessage = FText::FromString(FString::Format(TEXT("ChooserTable({0}):Context is empty, and only allow 2 element!"), {*ChooserTable->GetName()}));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ChooserTable->ResultType != EObjectChooserResultType::ObjectResult || ChooserTable->OutputObjectType != UPoseSearchDatabase::StaticClass())
|
||||
{
|
||||
OutMessage = FText::FromString(FString::Format(TEXT("ChooserTable({0}):Result type must be ObjectResult and the OutputObjectType must be PoseSearchDatabase."), {*ChooserTable->GetName()}));
|
||||
return false;
|
||||
}
|
||||
|
||||
const FContextObjectTypeClass* Ctx1 = ChooserTable->GetContextData()[0].GetPtr<FContextObjectTypeClass>();
|
||||
|
||||
bool bValidCtx1 = Ctx1 != nullptr && Ctx1->Class != nullptr && Ctx1->Class->IsChildOf(UGMS_MainAnimInstance::StaticClass());
|
||||
|
||||
if (!bValidCtx1)
|
||||
{
|
||||
OutMessage = FText::FromString(FString::Format(
|
||||
TEXT("ChooserTable({0}): First context must be ContextObjectTypeClass and the class must be Subclass of UGMS_MainAnimInstance."), {*ChooserTable->GetName()}));
|
||||
return false;
|
||||
}
|
||||
|
||||
const FContextObjectTypeClass* Ctx2 = ChooserTable->GetContextData()[1].GetPtr<FContextObjectTypeClass>();
|
||||
|
||||
bool bValidCtx2 = Ctx2 != nullptr && Ctx2->Class != nullptr && Ctx2->Class->IsChildOf(UGMS_AnimLayer::StaticClass());
|
||||
|
||||
if (!bValidCtx2)
|
||||
{
|
||||
OutMessage = FText::FromString(
|
||||
FString::Format(TEXT("ChooserTable({0}): Secondary context must be ContextObjectTypeClass and the class must be Subclass of UGMS_AnimLayer."), {*ChooserTable->GetName()}));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UGMS_Utility::IsValidPoseSearchDatabasesChooser(const UChooserTable* ChooserTable)
|
||||
{
|
||||
if (!IsValid(ChooserTable))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (ChooserTable->GetContextData().Num() != 2)
|
||||
{
|
||||
UE_LOG(LogGMS, Warning, TEXT("ChooserTable(%s):Context is empty, and only allow 2 element!"), *ChooserTable->GetName());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ChooserTable->ResultType != EObjectChooserResultType::ObjectResult || ChooserTable->OutputObjectType != UPoseSearchDatabase::StaticClass())
|
||||
{
|
||||
UE_LOG(LogGMS, Warning, TEXT("ChooserTable(%s):Result type must be ObjectResult and the OutputObjectType must be PoseSearchDatabase."), *ChooserTable->GetName());
|
||||
return false;
|
||||
}
|
||||
|
||||
const FContextObjectTypeClass* Ctx1 = ChooserTable->GetContextData()[0].GetPtr<FContextObjectTypeClass>();
|
||||
|
||||
bool bValidCtx1 = Ctx1 != nullptr && Ctx1->Class != nullptr && Ctx1->Class->IsChildOf(UGMS_MainAnimInstance::StaticClass());
|
||||
|
||||
if (!bValidCtx1)
|
||||
{
|
||||
UE_LOG(LogGMS, Warning, TEXT("ChooserTable(%s): First context must be ContextObjectTypeClass and the class must be Subclass of UGMS_MainAnimInstance."),
|
||||
*ChooserTable->GetName());
|
||||
return false;
|
||||
}
|
||||
|
||||
const FContextObjectTypeClass* Ctx2 = ChooserTable->GetContextData()[1].GetPtr<FContextObjectTypeClass>();
|
||||
|
||||
bool bValidCtx2 = Ctx2 != nullptr && Ctx2->Class != nullptr && Ctx2->Class->IsChildOf(UGMS_AnimLayer::StaticClass());
|
||||
|
||||
if (!bValidCtx2)
|
||||
{
|
||||
GMS_LOG(Warning, "ChooserTable(%s): Secondary context must be ContextObjectTypeClass and the class must be Subclass of UGMS_AnimLayer.",
|
||||
*ChooserTable->GetName())
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TArray<UPoseSearchDatabase*> UGMS_Utility::EvaluatePoseSearchDatabasesChooser(const UGMS_MainAnimInstance* MainAnimInstance, const UGMS_AnimLayer* AnimLayerInstance,
|
||||
UChooserTable* ChooserTable)
|
||||
{
|
||||
TArray<UPoseSearchDatabase*> Ret;
|
||||
|
||||
if (!IsValid(ChooserTable))
|
||||
{
|
||||
return Ret;
|
||||
}
|
||||
|
||||
// Fallback single context object version
|
||||
FChooserEvaluationContext Context;
|
||||
Context.AddObjectParam(const_cast<UGMS_MainAnimInstance*>(MainAnimInstance));
|
||||
Context.AddObjectParam(const_cast<UGMS_AnimLayer*>(AnimLayerInstance));
|
||||
|
||||
auto Callback = FObjectChooserBase::FObjectChooserIteratorCallback::CreateLambda([&Ret](UObject* InResult)
|
||||
{
|
||||
if (InResult && InResult->IsA(UPoseSearchDatabase::StaticClass()))
|
||||
{
|
||||
Ret.Add(Cast<UPoseSearchDatabase>(InResult));
|
||||
}
|
||||
return FObjectChooserBase::EIteratorStatus::Continue;
|
||||
});
|
||||
|
||||
UChooserTable::EvaluateChooser(Context, ChooserTable, Callback);
|
||||
return Ret;
|
||||
}
|
||||
|
||||
const UGMS_MovementSetUserSetting* UGMS_Utility::GetMovementSetUserSetting(const FGMS_MovementSetSetting& MovementSetSetting, TSubclassOf<UGMS_MovementSetUserSetting> DesiredClass)
|
||||
{
|
||||
if (!IsValid(DesiredClass))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (TObjectPtr<UGMS_MovementSetUserSetting> UserSetting : MovementSetSetting.UserSettings)
|
||||
{
|
||||
if (UserSetting->GetClass() == DesiredClass)
|
||||
{
|
||||
return UserSetting;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#include "Utility/GMS_Vector.h"
|
||||
|
||||
|
||||
#include UE_INLINE_GENERATED_CPP_BY_NAME(GMS_Vector)
|
||||
|
||||
FVector UGMS_Vector::SlerpSkipNormalization(const FVector& From, const FVector& To, const float Ratio)
|
||||
{
|
||||
// http://number-none.com/product/Understanding%20Slerp,%20Then%20Not%20Using%20It/
|
||||
|
||||
auto Dot{From | To};
|
||||
|
||||
if (Dot > 0.9995f)
|
||||
{
|
||||
return FMath::Lerp(From, To, Ratio).GetSafeNormal();
|
||||
}
|
||||
|
||||
Dot = FMath::Max(-1.0f, Dot);
|
||||
|
||||
const auto Theta{UE_REAL_TO_FLOAT(FMath::Acos(Dot)) * Ratio};
|
||||
|
||||
float Sin, Cos;
|
||||
FMath::SinCos(&Sin, &Cos, Theta);
|
||||
|
||||
auto FromPerpendicular{To - From * Dot};
|
||||
FromPerpendicular.Normalize();
|
||||
|
||||
return From * Cos + FromPerpendicular * Sin;
|
||||
}
|
||||
@@ -0,0 +1,616 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GMS_MovementSystemComponent.h"
|
||||
#include "GameFramework/Character.h"
|
||||
#include "GMS_CharacterMovementSystemComponent.generated.h"
|
||||
|
||||
class UGMS_CharacterMovementSetting_Default;
|
||||
class UGMS_CharacterRotationSetting_Default;
|
||||
|
||||
/**
|
||||
* Movement system component for characters.
|
||||
* 角色的运动系统组件。
|
||||
*/
|
||||
UCLASS(ClassGroup=GMS, BlueprintType, Blueprintable, meta=(BlueprintSpawnableComponent), DisplayName="GMS Movement System Component(Character)")
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_CharacterMovementSystemComponent : public UGMS_MovementSystemComponent
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor with object initializer.
|
||||
* 使用对象初始化器构造函数。
|
||||
*/
|
||||
explicit UGMS_CharacterMovementSystemComponent(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get());
|
||||
|
||||
/**
|
||||
* Gets lifetime replicated properties.
|
||||
* 获取生命周期复制属性。
|
||||
* @param OutLifetimeProps The lifetime properties. 生命周期属性。
|
||||
*/
|
||||
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
||||
|
||||
/**
|
||||
* Gets the character movement component.
|
||||
* 获取角色运动组件。
|
||||
* @return The character movement component. 角色运动组件。
|
||||
*/
|
||||
UCharacterMovementComponent* GetCharacterMovement() const { return CharacterMovement; };
|
||||
|
||||
protected:
|
||||
/**
|
||||
* The character movement component.
|
||||
* 角色运动组件。
|
||||
*/
|
||||
UPROPERTY()
|
||||
TObjectPtr<UCharacterMovementComponent> CharacterMovement;
|
||||
|
||||
/**
|
||||
* The owning character.
|
||||
* 拥有该组件的角色。
|
||||
*/
|
||||
UPROPERTY()
|
||||
TObjectPtr<ACharacter> OwnerCharacter{nullptr};
|
||||
|
||||
/**
|
||||
* Maps movement modes to gameplay tags.
|
||||
* 将运动模式映射到游戏标签。
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Settings|GameplayTags", meta=(Categories="GMS.LocomotionMode"))
|
||||
TMap<TEnumAsByte<EMovementMode>, FGameplayTag> MovementModeToTagMapping;
|
||||
|
||||
/**
|
||||
* Maps custom movement modes to gameplay tags.
|
||||
* 将自定义运动模式映射到游戏标签。
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Settings|GameplayTags", meta=(Categories="GMS.LocomotionMode"))
|
||||
TMap<uint8, FGameplayTag> CustomMovementModeToTagMapping;
|
||||
|
||||
/**
|
||||
* Whether to apply movement settings to the character movement component.
|
||||
* 是否将运动设置应用于角色运动组件。
|
||||
* @note Can be disabled for external control (e.g., AI). 可禁用以进行外部控制(例如AI)。
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Settings|DynamicMovementState")
|
||||
bool bAllowRefreshCharacterMovementSettings{true};
|
||||
|
||||
/**
|
||||
* Curve mapping ground speed to movement state index (e.g., walk, jog, sprint).
|
||||
* 将地面速度映射到运动状态索引的曲线(例如走、慢跑、冲刺)。
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Settings|DynamicMovementState", meta=(EditCondition="!bAllowRefreshCharacterMovementSettings"))
|
||||
TObjectPtr<UCurveFloat> SpeedToMovementStateCurve;
|
||||
|
||||
/**
|
||||
* Initializes the component.
|
||||
* 初始化组件。
|
||||
*/
|
||||
virtual void InitializeComponent() override;
|
||||
|
||||
/**
|
||||
* Called when the game starts.
|
||||
* 游戏开始时调用。
|
||||
*/
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
/**
|
||||
* Called when the game ends.
|
||||
* 游戏结束时调用。
|
||||
* @param EndPlayReason The reason for ending. 结束原因。
|
||||
*/
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Called every frame.
|
||||
* 每帧调用。
|
||||
* @param DeltaTime Time since last frame. 上一帧以来的时间。
|
||||
* @param TickType The type of tick. tick类型。
|
||||
* @param ThisTickFunction The tick function. tick函数。
|
||||
*/
|
||||
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Called when the character's movement mode changes.
|
||||
* 角色运动模式更改时调用。
|
||||
* @param InCharacter The character. 角色。
|
||||
* @param PrevMovementMode The previous movement mode. 之前的运动模式。
|
||||
* @param PreviousCustomMode The previous custom mode. 之前的自定义模式。
|
||||
*/
|
||||
UFUNCTION()
|
||||
virtual void OnCharacterMovementModeChanged(ACharacter* InCharacter, EMovementMode PrevMovementMode, uint8 PreviousCustomMode = 0);
|
||||
|
||||
/**
|
||||
* Called when the locomotion mode is replicated.
|
||||
* 运动模式复制时调用。
|
||||
* @param PreviousLocomotionMode The previous locomotion mode. 之前的运动模式。
|
||||
*/
|
||||
virtual void OnReplicated_LocomotionMode(const FGameplayTag& PreviousLocomotionMode) override;
|
||||
|
||||
/**
|
||||
* Calculates the actual movement state.
|
||||
* 计算实际的运动状态。
|
||||
* @return The actual movement state. 实际的运动状态。
|
||||
*/
|
||||
virtual FGameplayTag CalculateActualMovementState();
|
||||
|
||||
/**
|
||||
* Applies the desired movement state settings to the character movement component.
|
||||
* 将期望的运动状态设置应用于角色运动组件。
|
||||
*/
|
||||
virtual void ApplyMovementSetting() override;
|
||||
|
||||
/**
|
||||
* Called when the rotation mode changes.
|
||||
* 旋转模式更改时调用。
|
||||
* @param PreviousRotationMode The previous rotation mode. 之前的旋转模式。
|
||||
*/
|
||||
virtual void OnRotationModeChanged_Implementation(const FGameplayTag& PreviousRotationMode) override;
|
||||
|
||||
virtual void RefreshMovementBase() override;
|
||||
|
||||
#pragma region MovementState
|
||||
|
||||
public:
|
||||
/**
|
||||
* Gets the desired movement state.
|
||||
* 获取期望的运动状态。
|
||||
* @return The desired movement state. 期望的运动状态。
|
||||
*/
|
||||
virtual const FGameplayTag& GetDesiredMovementState() const override;
|
||||
|
||||
/**
|
||||
* Sets the desired movement state.
|
||||
* 设置期望的运动状态。
|
||||
* @param NewDesiredMovement The new desired movement state. 新的期望运动状态。
|
||||
*/
|
||||
virtual void SetDesiredMovement(const FGameplayTag& NewDesiredMovement) override;
|
||||
|
||||
/**
|
||||
* Gets the current movement state.
|
||||
* 获取当前的运动状态。
|
||||
* @return The current movement state. 当前的运动状态。
|
||||
*/
|
||||
virtual const FGameplayTag& GetMovementState() const override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Sets the movement state.
|
||||
* 设置运动状态。
|
||||
* @param NewMovementState The new movement state. 新运动状态。
|
||||
*/
|
||||
virtual void SetMovementState(const FGameplayTag& NewMovementState);
|
||||
|
||||
/**
|
||||
* Refreshes the movement state.
|
||||
* 刷新运动状态。
|
||||
*/
|
||||
virtual void RefreshMovementState();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Sets the desired movement state with optional RPC.
|
||||
* 设置期望的运动状态,可选择是否发送RPC。
|
||||
* @param NewDesiredMovement The new desired movement state. 新的期望运动状态。
|
||||
* @param bSendRpc Whether to send RPC. 是否发送RPC。
|
||||
*/
|
||||
void SetDesiredMovement(const FGameplayTag& NewDesiredMovement, bool bSendRpc);
|
||||
|
||||
/**
|
||||
* Client RPC to set the desired movement state.
|
||||
* 客户端RPC设置期望的运动状态。
|
||||
* @param NewDesiredMovement The new desired movement state. 新的期望运动状态。
|
||||
*/
|
||||
UFUNCTION(Client, Reliable)
|
||||
void ClientSetDesiredMovement(const FGameplayTag& NewDesiredMovement);
|
||||
virtual void ClientSetDesiredMovement_Implementation(const FGameplayTag& NewDesiredMovement);
|
||||
|
||||
/**
|
||||
* Server RPC to set the desired movement state.
|
||||
* 服务器RPC设置期望的运动状态。
|
||||
* @param NewDesiredMovement The new desired movement state. 新的期望运动状态。
|
||||
*/
|
||||
UFUNCTION(Server, Reliable)
|
||||
void ServerSetDesiredMovement(const FGameplayTag& NewDesiredMovement);
|
||||
virtual void ServerSetDesiredMovement_Implementation(const FGameplayTag& NewDesiredMovement);
|
||||
#pragma endregion
|
||||
|
||||
#pragma region RotationMode
|
||||
|
||||
public:
|
||||
/**
|
||||
* Gets the desired rotation mode.
|
||||
* 获取期望的旋转模式。
|
||||
* @return The desired rotation mode. 期望的旋转模式。
|
||||
*/
|
||||
virtual const FGameplayTag& GetDesiredRotationMode() const override;
|
||||
|
||||
/**
|
||||
* Sets the desired rotation mode.
|
||||
* 设置期望的旋转模式。
|
||||
* @param NewDesiredRotationMode The new desired rotation mode. 新的期望旋转模式。
|
||||
*/
|
||||
virtual void SetDesiredRotationMode(const FGameplayTag& NewDesiredRotationMode) override;
|
||||
|
||||
/**
|
||||
* Gets the current rotation mode.
|
||||
* 获取当前的旋转模式。
|
||||
* @return The current rotation mode. 当前的旋转模式。
|
||||
*/
|
||||
virtual const FGameplayTag& GetRotationMode() const override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Sets the rotation mode.
|
||||
* 设置旋转模式。
|
||||
* @param NewRotationMode The new rotation mode. 新旋转模式。
|
||||
*/
|
||||
virtual void SetRotationMode(const FGameplayTag& NewRotationMode);
|
||||
|
||||
/**
|
||||
* Refreshes the rotation mode.
|
||||
* 刷新旋转模式。
|
||||
*/
|
||||
virtual void RefreshRotationMode();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Sets the desired rotation mode with optional RPC.
|
||||
* 设置期望的旋转模式,可选择是否发送RPC。
|
||||
* @param NewDesiredRotationMode The new desired rotation mode. 新的期望旋转模式。
|
||||
* @param bSendRpc Whether to send RPC. 是否发送RPC。
|
||||
*/
|
||||
void SetDesiredRotationMode(const FGameplayTag& NewDesiredRotationMode, bool bSendRpc);
|
||||
|
||||
/**
|
||||
* Client RPC to set the desired rotation mode.
|
||||
* 客户端RPC设置期望的旋转模式。
|
||||
* @param NewDesiredRotationMode The new desired rotation mode. 新的期望旋转模式。
|
||||
*/
|
||||
UFUNCTION(Client, Reliable)
|
||||
void ClientSetDesiredRotationMode(const FGameplayTag& NewDesiredRotationMode);
|
||||
virtual void ClientSetDesiredRotationMode_Implementation(const FGameplayTag& NewDesiredRotationMode);
|
||||
|
||||
/**
|
||||
* Server RPC to set the desired rotation mode.
|
||||
* 服务器RPC设置期望的旋转模式。
|
||||
* @param NewDesiredRotationMode The new desired rotation mode. 新的期望旋转模式。
|
||||
*/
|
||||
UFUNCTION(Server, Reliable)
|
||||
void ServerSetDesiredRotationMode(const FGameplayTag& NewDesiredRotationMode);
|
||||
virtual void ServerSetDesiredRotationMode_Implementation(const FGameplayTag& NewDesiredRotationMode);
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Input
|
||||
|
||||
public:
|
||||
/**
|
||||
* Gets the movement intent.
|
||||
* 获取移动意图。
|
||||
* @return The movement intent vector. 移动意图向量。
|
||||
*/
|
||||
virtual FVector GetMovementIntent() const override;
|
||||
|
||||
/**
|
||||
* Sets the movement intent.
|
||||
* 设置移动意图。
|
||||
* @param NewMovementIntent The new movement intent vector. 新移动意图向量。
|
||||
*/
|
||||
void SetMovementIntent(FVector NewMovementIntent);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Refreshes input handling.
|
||||
* 刷新输入处理。
|
||||
* @param DeltaTime Time since last frame. 上一帧以来的时间。
|
||||
*/
|
||||
virtual void RefreshInput(float DeltaTime) override;
|
||||
|
||||
/**
|
||||
* Replicated movement intent.
|
||||
* 复制的移动意图。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, Category="State|Input", Transient, Replicated)
|
||||
FVector_NetQuantizeNormal MovementIntent;
|
||||
|
||||
/**
|
||||
* Desired movement state.
|
||||
* 期望的运动状态。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="Settings", Replicated, meta=(Categories="GMS.MovementState"))
|
||||
FGameplayTag DesiredMovementState{GMS_MovementStateTags::Jog};
|
||||
|
||||
/**
|
||||
* Desired rotation mode.
|
||||
* 期望的旋转模式。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Settings", Replicated, meta=(Categories="GMS.RotationMode"))
|
||||
FGameplayTag DesiredRotationMode{GMS_RotationModeTags::ViewDirection};
|
||||
|
||||
/**
|
||||
* Current movement state.
|
||||
* 当前的运动状态。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, Category="State", Transient, meta=(Categories="GMS.MovementState"))
|
||||
FGameplayTag MovementState{GMS_MovementStateTags::Jog};
|
||||
|
||||
/**
|
||||
* Current rotation mode.
|
||||
* 当前的旋转模式。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="State", Transient, meta=(Categories="GMS.RotationMode"))
|
||||
FGameplayTag RotationMode{GMS_RotationModeTags::ViewDirection};
|
||||
#pragma endregion
|
||||
|
||||
#pragma region ViewSystem
|
||||
/**
|
||||
* Refreshes the view system.
|
||||
* 刷新视图系统。
|
||||
* @param DeltaTime Time since last frame. 上一帧以来的时间。
|
||||
*/
|
||||
virtual void RefreshView(float DeltaTime);
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Abstraction
|
||||
|
||||
public:
|
||||
/**
|
||||
* Checks if the character is crouching.
|
||||
* 检查角色是否在蹲伏。
|
||||
* @return True if crouching. 如果在蹲伏返回true。
|
||||
*/
|
||||
virtual bool IsCrouching() const override;
|
||||
|
||||
/**
|
||||
* Gets the maximum speed.
|
||||
* 获取最大速度。
|
||||
* @return The maximum speed. 最大速度。
|
||||
*/
|
||||
virtual float GetMaxSpeed() const override;
|
||||
|
||||
/**
|
||||
* Gets the scaled capsule radius.
|
||||
* 获取缩放的胶囊体半径。
|
||||
* @return The capsule radius. 胶囊体半径。
|
||||
*/
|
||||
virtual float GetScaledCapsuleRadius() const override;
|
||||
|
||||
/**
|
||||
* Gets the scaled capsule half height.
|
||||
* 获取缩放的胶囊体半高。
|
||||
* @return The capsule half height. 胶囊体半高。
|
||||
*/
|
||||
virtual float GetScaledCapsuleHalfHeight() const override;
|
||||
|
||||
/**
|
||||
* Gets the maximum acceleration.
|
||||
* 获取最大加速度。
|
||||
* @return The maximum acceleration. 最大加速度。
|
||||
*/
|
||||
virtual float GetMaxAcceleration() const override;
|
||||
|
||||
/**
|
||||
* Gets the maximum braking deceleration.
|
||||
* 获取最大制动减速度。
|
||||
* @return The maximum braking deceleration. 最大制动减速度。
|
||||
*/
|
||||
virtual float GetMaxBrakingDeceleration() const override;
|
||||
|
||||
/**
|
||||
* Gets the walkable floor Z value.
|
||||
* 获取可行走地面Z值。
|
||||
* @return The walkable floor Z. 可行走地面Z值。
|
||||
*/
|
||||
virtual float GetWalkableFloorZ() const override;
|
||||
|
||||
/**
|
||||
* Gets the gravity Z value.
|
||||
* 获取重力Z值。
|
||||
* @return The gravity Z. 重力Z值。
|
||||
*/
|
||||
virtual float GetGravityZ() const override;
|
||||
|
||||
/**
|
||||
* Gets the skeletal mesh component.
|
||||
* 获取骨骼网格组件。
|
||||
* @return The skeletal mesh component. 骨骼网格组件。
|
||||
*/
|
||||
virtual USkeletalMeshComponent* GetMesh() const override;
|
||||
|
||||
virtual bool IsMovingOnGround() const override;
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Locomotion
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Early refresh for locomotion.
|
||||
* 运动的早期刷新。
|
||||
*/
|
||||
virtual void RefreshLocomotionEarly();
|
||||
|
||||
/**
|
||||
* Refreshes locomotion state.
|
||||
* 刷新运动状态。
|
||||
* @param DeltaTime Time since last frame. 上一帧以来的时间。
|
||||
*/
|
||||
virtual void RefreshLocomotion(float DeltaTime);
|
||||
|
||||
/**
|
||||
* Late refresh for locomotion.
|
||||
* 运动的后期刷新。
|
||||
* @param DeltaTime Time since last frame. 上一帧以来的时间。
|
||||
*/
|
||||
virtual void RefreshLocomotionLate(float DeltaTime);
|
||||
|
||||
/**
|
||||
* Refreshes dynamic movement state.
|
||||
* 刷新动态运动状态。
|
||||
*/
|
||||
virtual void RefreshDynamicMovementState();
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Rotation System
|
||||
|
||||
public:
|
||||
/**
|
||||
* Sets rotation instantly.
|
||||
* 立即设置旋转。
|
||||
* @param TargetYawAngle The target yaw angle. 目标偏航角。
|
||||
* @param Teleport The teleport type. 传送类型。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GMS|MovementSystem")
|
||||
void SetRotationInstant(const float TargetYawAngle, const ETeleportType Teleport);
|
||||
|
||||
/**
|
||||
* Sets rotation smoothly.
|
||||
* 平滑设置旋转。
|
||||
* @param TargetYawAngle The target yaw angle. 目标偏航角。
|
||||
* @param DeltaTime Time since last frame. 上一帧以来的时间。
|
||||
* @param InterpolationHalfLife The rotation interpolation speed. 旋转插值速度。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GMS|MovementSystem")
|
||||
void SetRotationSmooth(float TargetYawAngle, float DeltaTime, float InterpolationHalfLife);
|
||||
|
||||
/**
|
||||
* Sets rotation with extra smoothness.
|
||||
* 额外平滑设置旋转。
|
||||
* @param TargetYawAngle The target yaw angle. 目标偏航角。
|
||||
* @param DeltaTime Time since last frame. 上一帧以来的时间。
|
||||
* @param InterpolationHalfLife The rotation interpolation speed. 旋转插值速度。
|
||||
* @param TargetYawAngleRotationSpeed The target yaw rotation speed. 目标偏航旋转速度。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GMS|MovementSystem")
|
||||
void SetRotationExtraSmooth(float TargetYawAngle, float DeltaTime, float InterpolationHalfLife, float TargetYawAngleRotationSpeed);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Refreshes the character's rotation.
|
||||
* 刷新角色的旋转。
|
||||
* @param DeltaTime Time since last frame. 上一帧以来的时间。
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category="GMS|MovementSystem")
|
||||
void RefreshRotation(float DeltaTime);
|
||||
virtual void RefreshRotation_Implementation(float DeltaTime);
|
||||
|
||||
/**
|
||||
* Refreshes grounded rotation.
|
||||
* 刷新地面旋转。
|
||||
* @param DeltaTime Time since last frame. 上一帧以来的时间。
|
||||
*/
|
||||
virtual void RefreshGroundedRotation(float DeltaTime);
|
||||
|
||||
/**
|
||||
* Refreshes rotation when grounded and not moving.
|
||||
* 地面上且不移动时刷新旋转。
|
||||
* @param DeltaTime Time since last frame. 上一帧以来的时间。
|
||||
*/
|
||||
virtual void RefreshGroundedNotMovingRotation(float DeltaTime);
|
||||
|
||||
|
||||
virtual float CalculateGroundedMovingRotationInterpolationSpeed(TObjectPtr<UCurveFloat> SpeedCurve, float Default = 12.0f) const;
|
||||
|
||||
/**
|
||||
* Refreshes rotation when grounded and moving.
|
||||
* 地面上且移动时刷新旋转。
|
||||
* @param DeltaTime Time since last frame. 上一帧以来的时间。
|
||||
*/
|
||||
virtual void RefreshGroundedMovingRotation(float DeltaTime);
|
||||
|
||||
/**
|
||||
* Constrains aiming rotation.
|
||||
* 约束瞄准旋转。
|
||||
* @param ActorRotation The actor's rotation. Actor的旋转。
|
||||
* @param DeltaTime Time since last frame. 上一帧以来的时间。
|
||||
* @param bApplySecondaryConstraint Whether to apply secondary constraints. 是否应用次级约束。
|
||||
* @return True if rotation was constrained. 如果旋转被约束返回true。
|
||||
*/
|
||||
virtual bool ConstrainAimingRotation(FRotator& ActorRotation, float DeltaTime, bool bApplySecondaryConstraint = false);
|
||||
|
||||
/**
|
||||
* Applies rotation yaw speed animation curve.
|
||||
* 应用旋转偏航速度动画曲线。
|
||||
* @param DeltaTime Time since last frame. 上一帧以来的时间。
|
||||
* @return True if the curve was applied. 如果应用了曲线返回true。
|
||||
*/
|
||||
bool ApplyRotationYawSpeedAnimationCurve(float DeltaTime);
|
||||
|
||||
/**
|
||||
* Custom rotation logic for grounded moving state.
|
||||
* 地面移动状态的自定义旋转逻辑。
|
||||
* @param DeltaTime Time since last frame. 上一帧以来的时间。
|
||||
* @return True if default rotation logic should be skipped. 如果应跳过默认旋转逻辑返回true。
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category="GMS|MovementSystem")
|
||||
bool RefreshCustomGroundedMovingRotation(float DeltaTime);
|
||||
|
||||
/**
|
||||
* Custom rotation logic for grounded not moving state.
|
||||
* 地面不移动状态的自定义旋转逻辑。
|
||||
* @param DeltaTime Time since last frame. 上一帧以来的时间。
|
||||
* @return True if default rotation logic should be skipped. 如果应跳过默认旋转逻辑返回true。
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category="GMS|MovementSystem")
|
||||
bool RefreshCustomGroundedNotMovingRotation(float DeltaTime);
|
||||
|
||||
/**
|
||||
* Refreshes in-air rotation.
|
||||
* 刷新空中旋转。
|
||||
* @param DeltaTime Time since last frame. 上一帧以来的时间。
|
||||
*/
|
||||
virtual void RefreshInAirRotation(float DeltaTime);
|
||||
|
||||
/**
|
||||
* Refreshes the target yaw angle using actor's rotation.
|
||||
* 使用Actor的旋转刷新目标偏航角。
|
||||
*/
|
||||
void RefreshTargetYawAngleUsingActorRotation();
|
||||
|
||||
/**
|
||||
* Sets the target yaw angle.
|
||||
* 设置目标偏航角。
|
||||
* @param TargetYawAngle The target yaw angle. 目标偏航角。
|
||||
*/
|
||||
void SetTargetYawAngle(float TargetYawAngle);
|
||||
|
||||
/**
|
||||
* Sets the target yaw angle smoothly.
|
||||
* 平滑设置目标偏航角。
|
||||
* @param TargetYawAngle The target yaw angle. 目标偏航角。
|
||||
* @param DeltaTime Time since last frame. 上一帧以来的时间。
|
||||
* @param RotationSpeed The rotation speed. 旋转速度。
|
||||
*/
|
||||
void SetTargetYawAngleSmooth(float TargetYawAngle, float DeltaTime, float RotationSpeed);
|
||||
|
||||
/**
|
||||
* Refreshes the view-relative target yaw angle.
|
||||
* 刷新相对于视图的目标偏航角。
|
||||
*/
|
||||
void RefreshViewRelativeTargetYawAngle();
|
||||
|
||||
virtual void SetActorRotation(FRotator DesiredRotation);
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region DistanceMatching
|
||||
|
||||
public:
|
||||
/**
|
||||
* Gets parameters for predicting ground movement pivot location.
|
||||
* 获取预测地面运动枢轴位置的参数。
|
||||
* @return The pivot location parameters. 枢轴位置参数。
|
||||
*/
|
||||
virtual FGMS_PredictGroundMovementPivotLocationParams GetPredictGroundMovementPivotLocationParams() const override;
|
||||
|
||||
/**
|
||||
* Gets parameters for predicting ground movement stop location.
|
||||
* 获取预测地面运动停止位置的参数。
|
||||
* @return The stop location parameters. 停止位置参数。
|
||||
*/
|
||||
virtual FGMS_PredictGroundMovementStopLocationParams GetPredictGroundMovementStopLocationParams() const override;
|
||||
#pragma endregion
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,546 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GMS_MovementSystemComponent.h"
|
||||
#include "MoverSimulationTypes.h"
|
||||
#include "GMS_MoverMovementSystemComponent.generated.h"
|
||||
|
||||
class UMoverTrajectoryPredictor;
|
||||
class UNavMoverComponent;
|
||||
class UGMS_InputProducer;
|
||||
|
||||
/**
|
||||
* Movement system component for mover-based movement (work in progress).
|
||||
* 基于Mover的运动系统组件(开发中)。
|
||||
* @note Not recommended for use until production ready. 在标记为生产就绪之前不建议使用。
|
||||
*/
|
||||
UCLASS(ClassGroup=GMS, BlueprintType, Blueprintable, meta=(BlueprintSpawnableComponent), DisplayName="GMS Movement System Component(Mover)")
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_MoverMovementSystemComponent : public UGMS_MovementSystemComponent, public IMoverInputProducerInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor with object initializer.
|
||||
* 使用对象初始化器构造函数。
|
||||
*/
|
||||
UGMS_MoverMovementSystemComponent(const FObjectInitializer& ObjectInitializer);
|
||||
|
||||
/**
|
||||
* Gets lifetime replicated properties.
|
||||
* 获取生命周期复制属性。
|
||||
* @param OutLifetimeProps The lifetime properties. 生命周期属性。
|
||||
*/
|
||||
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
||||
|
||||
/**
|
||||
* Produces input for the mover simulation.
|
||||
* 为Mover模拟生成输入。
|
||||
* @param SimTimeMs Simulation time in milliseconds. 模拟时间(毫秒)。
|
||||
* @param InputCmdResult The input command context (output). 输入命令上下文(输出)。
|
||||
*/
|
||||
virtual void ProduceInput_Implementation(int32 SimTimeMs, FMoverInputCmdContext& InputCmdResult) override;
|
||||
|
||||
/**
|
||||
* Called every frame.
|
||||
* 每帧调用。
|
||||
* @param DeltaTime Time since last frame. 上一帧以来的时间。
|
||||
* @param TickType The type of tick. tick类型。
|
||||
* @param ThisTickFunction The tick function. tick函数。
|
||||
*/
|
||||
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
|
||||
|
||||
/**
|
||||
* Called before the mover simulation tick.
|
||||
* Mover模拟tick前调用。
|
||||
* @param TimeStep The mover time step. Mover时间步。
|
||||
* @param InputCmd The input command context. 输入命令上下文。
|
||||
*/
|
||||
UFUNCTION()
|
||||
virtual void OnMoverPreSimulationTick(const FMoverTimeStep& TimeStep, const FMoverInputCmdContext& InputCmd);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* The mover component.
|
||||
* Mover组件。
|
||||
*/
|
||||
UPROPERTY()
|
||||
TObjectPtr<UMoverComponent> MoverComponent{nullptr};
|
||||
|
||||
|
||||
UPROPERTY()
|
||||
TObjectPtr<UMoverTrajectoryPredictor> TrajectoryPredictor{nullptr};
|
||||
|
||||
/**
|
||||
* The skeletal mesh component.
|
||||
* 骨骼网格组件。
|
||||
*/
|
||||
UPROPERTY()
|
||||
TObjectPtr<USkeletalMeshComponent> MeshComponent{nullptr};
|
||||
|
||||
/**
|
||||
* Handles navigation movement data and functions.
|
||||
* 处理导航运动数据和函数。
|
||||
*/
|
||||
UPROPERTY()
|
||||
TObjectPtr<UNavMoverComponent> NavMoverComponent{nullptr};
|
||||
|
||||
/**
|
||||
* Maps movement modes to gameplay tags.
|
||||
* 将运动模式映射到游戏标签。
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Settings|GameplayTags", meta=(Categories="GMS.LocomotionMode"))
|
||||
TMap<FName, FGameplayTag> MovementModeToTagMapping;
|
||||
|
||||
/**
|
||||
* Initializes the component.
|
||||
* 初始化组件。
|
||||
*/
|
||||
virtual void InitializeComponent() override;
|
||||
|
||||
/**
|
||||
* Called when the game starts.
|
||||
* 游戏开始时调用。
|
||||
*/
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
/**
|
||||
* Called when the game ends.
|
||||
* 游戏结束时调用。
|
||||
* @param EndPlayReason The reason for ending. 结束原因。
|
||||
*/
|
||||
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
||||
|
||||
/**
|
||||
* Handle for the movement state modifier (e.g., crouching).
|
||||
* 运动状态修饰符的句柄(例如蹲伏)。
|
||||
*/
|
||||
FMovementModifierHandle MovementStateModiferHandle;
|
||||
|
||||
/**
|
||||
* Called when the mover movement mode changes.
|
||||
* Mover运动模式更改时调用。
|
||||
* @param PreviousMovementModeName The previous movement mode name. 之前的运动模式名称。
|
||||
* @param NewMovementModeName The new movement mode name. 新运动模式名称。
|
||||
*/
|
||||
UFUNCTION()
|
||||
virtual void OnMoverMovementModeChanged(const FName& PreviousMovementModeName, const FName& NewMovementModeName);
|
||||
|
||||
/**
|
||||
* Applies movement settings.
|
||||
* 应用运动设置。
|
||||
*/
|
||||
virtual void ApplyMovementSetting() override;
|
||||
|
||||
#pragma region ViewSystem
|
||||
/**
|
||||
* Refreshes the view system.
|
||||
* 刷新视图系统。
|
||||
* @param DeltaTime Time since last frame. 上一帧以来的时间。
|
||||
*/
|
||||
virtual void RefreshView(float DeltaTime);
|
||||
|
||||
/**
|
||||
* Server RPC to set replicated view rotation.
|
||||
* 服务器RPC设置复制的视图旋转。
|
||||
* @param NewViewRotation The new view rotation. 新视图旋转。
|
||||
*/
|
||||
virtual void ServerSetReplicatedViewRotation_Implementation(const FRotator& NewViewRotation) override;
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Locomotion
|
||||
|
||||
public:
|
||||
virtual TScriptInterface<IPoseSearchTrajectoryPredictorInterface> GetTrajectoryPredictor() const override;
|
||||
/**
|
||||
* Checks if the character is crouching.
|
||||
* 检查角色是否在蹲伏。
|
||||
* @return True if crouching. 如果在蹲伏返回true。
|
||||
*/
|
||||
virtual bool IsCrouching() const override;
|
||||
|
||||
/**
|
||||
* Gets the maximum speed.
|
||||
* 获取最大速度。
|
||||
* @return The maximum speed. 最大速度。
|
||||
*/
|
||||
virtual float GetMaxSpeed() const override;
|
||||
|
||||
/**
|
||||
* Gets the scaled capsule radius.
|
||||
* 获取缩放的胶囊体半径。
|
||||
* @return The capsule radius. 胶囊体半径。
|
||||
*/
|
||||
virtual float GetScaledCapsuleRadius() const override;
|
||||
|
||||
/**
|
||||
* Gets the scaled capsule half height.
|
||||
* 获取缩放的胶囊体半高。
|
||||
* @return The capsule half height. 胶囊体半高。
|
||||
*/
|
||||
virtual float GetScaledCapsuleHalfHeight() const override;
|
||||
|
||||
/**
|
||||
* Gets the maximum acceleration.
|
||||
* 获取最大加速度。
|
||||
* @return The maximum acceleration. 最大加速度。
|
||||
*/
|
||||
virtual float GetMaxAcceleration() const override;
|
||||
|
||||
/**
|
||||
* Gets the maximum braking deceleration.
|
||||
* 获取最大制动减速度。
|
||||
* @return The maximum braking deceleration. 最大制动减速度。
|
||||
*/
|
||||
virtual float GetMaxBrakingDeceleration() const override;
|
||||
|
||||
/**
|
||||
* Gets the walkable floor Z value.
|
||||
* 获取可行走地面Z值。
|
||||
* @return The walkable floor Z. 可行走地面Z值。
|
||||
*/
|
||||
virtual float GetWalkableFloorZ() const override;
|
||||
|
||||
/**
|
||||
* Gets the gravity Z value.
|
||||
* 获取重力Z值。
|
||||
* @return The gravity Z. 重力Z值。
|
||||
*/
|
||||
virtual float GetGravityZ() const override;
|
||||
|
||||
/**
|
||||
* Gets the skeletal mesh component.
|
||||
* 获取骨骼网格组件。
|
||||
* @return The skeletal mesh component. 骨骼网格组件。
|
||||
*/
|
||||
virtual USkeletalMeshComponent* GetMesh() const override;
|
||||
|
||||
virtual bool IsMovingOnGround() const override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Early refresh for locomotion.
|
||||
* 运动的早期刷新。
|
||||
*/
|
||||
virtual void RefreshLocomotionEarly();
|
||||
|
||||
/**
|
||||
* Refreshes locomotion state.
|
||||
* 刷新运动状态。
|
||||
* @param DeltaTime Time since last frame. 上一帧以来的时间。
|
||||
*/
|
||||
virtual void RefreshLocomotion(float DeltaTime);
|
||||
|
||||
/**
|
||||
* Refreshes dynamic movement state.
|
||||
* 刷新动态运动状态。
|
||||
*/
|
||||
virtual void RefreshDynamicMovementState();
|
||||
|
||||
/**
|
||||
* Late refresh for locomotion.
|
||||
* 运动的后期刷新。
|
||||
* @param DeltaTime Time since last frame. 上一帧以来的时间。
|
||||
*/
|
||||
virtual void RefreshLocomotionLate(float DeltaTime);
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Rotation System
|
||||
|
||||
public:
|
||||
/**
|
||||
* Refreshes the target yaw angle using locomotion rotation.
|
||||
* 使用运动旋转刷新目标偏航角。
|
||||
*/
|
||||
void RefreshTargetYawAngleUsingActorRotation();
|
||||
|
||||
/**
|
||||
* Sets the target yaw angle.
|
||||
* 设置目标偏航角。
|
||||
* @param TargetYawAngle The target yaw angle. 目标偏航角。
|
||||
*/
|
||||
void SetTargetYawAngle(float TargetYawAngle);
|
||||
|
||||
/**
|
||||
* Refreshes the view-relative target yaw angle.
|
||||
* 刷新相对于视图的目标偏航角。
|
||||
*/
|
||||
void RefreshViewRelativeTargetYawAngle();
|
||||
#pragma endregion
|
||||
|
||||
#pragma region DistanceMatching
|
||||
/**
|
||||
* Gets parameters for predicting ground movement pivot location.
|
||||
* 获取预测地面运动枢轴位置的参数。
|
||||
* @return The pivot location parameters. 枢轴位置参数。
|
||||
*/
|
||||
virtual FGMS_PredictGroundMovementPivotLocationParams GetPredictGroundMovementPivotLocationParams() const override;
|
||||
|
||||
/**
|
||||
* Gets parameters for predicting ground movement stop location.
|
||||
* 获取预测地面运动停止位置的参数。
|
||||
* @return The stop location parameters. 停止位置参数。
|
||||
*/
|
||||
virtual FGMS_PredictGroundMovementStopLocationParams GetPredictGroundMovementStopLocationParams() const override;
|
||||
#pragma endregion
|
||||
|
||||
#pragma region MovementState
|
||||
|
||||
virtual void RefreshMovementBase() override;
|
||||
|
||||
/**
|
||||
* Gets the desired movement state.
|
||||
* 获取期望的运动状态。
|
||||
* @return The desired movement state. 期望的运动状态。
|
||||
*/
|
||||
virtual const FGameplayTag& GetDesiredMovementState() const override;
|
||||
|
||||
/**
|
||||
* Sets the desired movement state.
|
||||
* 设置期望的运动状态。
|
||||
* @param NewDesiredMovement The new desired movement state. 新的期望运动状态。
|
||||
*/
|
||||
virtual void SetDesiredMovement(const FGameplayTag& NewDesiredMovement) override;
|
||||
|
||||
/**
|
||||
* Gets the current movement state.
|
||||
* 获取当前的运动状态。
|
||||
* @return The current movement state. 当前的运动状态。
|
||||
*/
|
||||
virtual const FGameplayTag& GetMovementState() const override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Applies the movement state and related settings.
|
||||
* 应用运动状态及相关设置。
|
||||
* @param NewMovementState The new movement state. 新运动状态。
|
||||
*/
|
||||
virtual void ApplyMovementState(const FGameplayTag& NewMovementState);
|
||||
#pragma endregion
|
||||
|
||||
#pragma region RotationMode
|
||||
|
||||
public:
|
||||
/**
|
||||
* Gets the desired rotation mode.
|
||||
* 获取期望的旋转模式。
|
||||
* @return The desired rotation mode. 期望的旋转模式。
|
||||
*/
|
||||
virtual const FGameplayTag& GetDesiredRotationMode() const override;
|
||||
|
||||
/**
|
||||
* Sets the desired rotation mode.
|
||||
* 设置期望的旋转模式。
|
||||
* @param NewDesiredRotationMode The new desired rotation mode. 新的期望旋转模式。
|
||||
*/
|
||||
virtual void SetDesiredRotationMode(const FGameplayTag& NewDesiredRotationMode) override;
|
||||
|
||||
/**
|
||||
* Gets the current rotation mode.
|
||||
* 获取当前的旋转模式。
|
||||
* @return The current rotation mode. 当前的旋转模式。
|
||||
*/
|
||||
virtual const FGameplayTag& GetRotationMode() const override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Applies the rotation mode and related settings.
|
||||
* 应用旋转模式及相关设置。
|
||||
* @param NewRotationMode The new rotation mode. 新旋转模式。
|
||||
*/
|
||||
virtual void ApplyRotationMode(const FGameplayTag& NewRotationMode);
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Input
|
||||
/**
|
||||
* Requests movement with an intended directional magnitude.
|
||||
* 请求以指定方向强度移动。
|
||||
* @param DesiredIntent The desired movement intent. 期望的移动意图。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GMS|MovementSystem")
|
||||
virtual void RequestMoveByIntent(const FVector& DesiredIntent) { CachedMoveInputIntent = DesiredIntent; }
|
||||
|
||||
/**
|
||||
* Requests movement with a desired velocity.
|
||||
* 请求以指定速度移动。
|
||||
* @param DesiredVelocity The desired velocity. 期望的速度。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GMS|MovementSystem")
|
||||
virtual void RequestMoveByVelocity(const FVector& DesiredVelocity) { CachedMoveInputVelocity = DesiredVelocity; }
|
||||
|
||||
/**
|
||||
* Clears movement requests.
|
||||
* 清除移动请求。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GMS|MovementSystem")
|
||||
virtual void ClearMoveRequest()
|
||||
{
|
||||
CachedMoveInputIntent = FVector::ZeroVector;
|
||||
CachedMoveInputVelocity = FVector::ZeroVector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the movement intent.
|
||||
* 获取移动意图。
|
||||
* @return The movement intent vector. 移动意图向量。
|
||||
*/
|
||||
virtual FVector GetMovementIntent() const override;
|
||||
|
||||
/**
|
||||
* Produces input for the simulation frame.
|
||||
* 为模拟帧生成输入。
|
||||
* @param DeltaMs Time delta in milliseconds. 时间增量(毫秒)。
|
||||
* @param InputCmd The input command context. 输入命令上下文。
|
||||
* @return The produced input command context. 生成的输入命令上下文。
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category="GMS|MovementSystem")
|
||||
FMoverInputCmdContext OnProduceInput(float DeltaMs, FMoverInputCmdContext InputCmd);
|
||||
|
||||
/**
|
||||
* Adjusts orientation intent based on context.
|
||||
* 根据上下文调整方向意图。
|
||||
* @param DeltaSeconds Time since last frame. 上一帧以来的时间。
|
||||
* @param OrientationIntent The orientation intent. 方向意图。
|
||||
* @return The adjusted orientation intent. 调整后的方向意图。
|
||||
*/
|
||||
virtual FVector AdjustOrientationIntent(float DeltaSeconds, const FVector& OrientationIntent) const;
|
||||
|
||||
/**
|
||||
* Whether to use base-relative movement.
|
||||
* 是否使用基于基础的移动。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Settings|Input")
|
||||
bool bUseBaseRelativeMovement = true;
|
||||
|
||||
/**
|
||||
* Whether to maintain the last input orientation after input ceases.
|
||||
* 输入停止后是否保持最后输入方向。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Settings|Input")
|
||||
bool bMaintainLastInputOrientation = false;
|
||||
|
||||
/**
|
||||
* Last non-zero movement input.
|
||||
* 最后非零移动输入。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="State|Input")
|
||||
FVector LastAffirmativeMoveInput = FVector::ZeroVector;
|
||||
|
||||
/**
|
||||
* If true, the actor will remain vertical despite any rotation applied to the actor
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=MoverExamples)
|
||||
bool bShouldRemainVertical = true;
|
||||
|
||||
/**
|
||||
* Cached movement input intent.
|
||||
* 缓存的移动输入意图。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="State|Input")
|
||||
FVector CachedMoveInputIntent = FVector::ZeroVector;
|
||||
|
||||
/**
|
||||
* Cached movement input velocity.
|
||||
* 缓存的移动输入速度。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="State|Input")
|
||||
FVector CachedMoveInputVelocity = FVector::ZeroVector;
|
||||
|
||||
/**
|
||||
* Cached turn input.
|
||||
* 缓存的转向输入。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="State|Input")
|
||||
FRotator CachedTurnInput = FRotator::ZeroRotator;
|
||||
|
||||
/**
|
||||
* Cached look input.
|
||||
* 缓存的观察输入。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="State|Input")
|
||||
FRotator CachedLookInput = FRotator::ZeroRotator;
|
||||
|
||||
/**
|
||||
* Current desired movement state.
|
||||
* 当前期望的运动状态。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="State|Input", meta=(Categories="GMS.MovementState"))
|
||||
FGameplayTag DesiredMovementState{GMS_MovementStateTags::Jog};
|
||||
|
||||
/**
|
||||
* Current desired rotation mode.
|
||||
* 当前期望的旋转模式。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="Settings", meta=(Categories="GMS.RotationMode"))
|
||||
FGameplayTag DesiredRotationMode{GMS_RotationModeTags::ViewDirection};
|
||||
|
||||
/**
|
||||
* Current movement state.
|
||||
* 当前的运动状态。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, Category="State", ReplicatedUsing=OnMovementStateChanged, meta=(Categories="GMS.MovementState"))
|
||||
FGameplayTag MovementState{GMS_MovementStateTags::Jog};
|
||||
|
||||
/**
|
||||
* Current rotation mode.
|
||||
* 当前的旋转模式。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, Category="State", ReplicatedUsing=OnRotationModeChanged, meta=(Categories="GMS.RotationMode"))
|
||||
FGameplayTag RotationMode{GMS_RotationModeTags::ViewDirection};
|
||||
|
||||
public:
|
||||
/**
|
||||
* Whether the jump input was just pressed.
|
||||
* 跳跃输入是否刚被按下。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="State|Input")
|
||||
bool bIsJumpJustPressed = false;
|
||||
|
||||
/**
|
||||
* Whether the jump input is held.
|
||||
* 跳跃输入是否被持续按住。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="State|Input")
|
||||
bool bIsJumpPressed = false;
|
||||
|
||||
/**
|
||||
* Input tags for the component.
|
||||
* 组件的输入标签。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Transient, Category="State|Input")
|
||||
FGameplayTagContainer InputTags;
|
||||
|
||||
/**
|
||||
* Whether flying is active.
|
||||
* 飞行是否激活。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="State|Input")
|
||||
bool bIsFlyingActive = false;
|
||||
|
||||
/**
|
||||
* Whether to toggle flying.
|
||||
* 是否切换飞行状态。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="State|Input")
|
||||
bool bShouldToggleFlying = false;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Flag indicating if input is produced in Blueprint.
|
||||
* 指示是否在蓝图中生成输入的标志。
|
||||
*/
|
||||
uint8 bHasProduceInputInBpFunc : 1;
|
||||
#pragma endregion
|
||||
|
||||
#if WITH_EDITOR
|
||||
/**
|
||||
* Validates data in the editor.
|
||||
* 在编辑器中验证数据。
|
||||
* @param Context The validation context. 验证上下文。
|
||||
* @return The validation result. 验证结果。
|
||||
*/
|
||||
virtual EDataValidationResult IsDataValid(class FDataValidationContext& Context) const override;
|
||||
#endif
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
struct FAutoCompleteCommand;
|
||||
|
||||
class FGenericMovementSystemModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
/** IModuleInterface implementation */
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
|
||||
private:
|
||||
#if ALLOW_CONSOLE
|
||||
void Console_OnRegisterAutoCompleteEntries(TArray<FAutoCompleteCommand>& AutoCompleteCommands);
|
||||
#endif
|
||||
};
|
||||
@@ -0,0 +1,160 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GMS_LocomotionStructLibrary.h"
|
||||
#include "Engine/DataAsset.h"
|
||||
#include "Animation/AnimInstance.h"
|
||||
#include "GMS_AnimLayer.generated.h"
|
||||
|
||||
class UGMS_MovementDefinition;
|
||||
class UGMS_AnimLayer;
|
||||
class UGMS_MovementSystemComponent;
|
||||
class UGMS_MainAnimInstance;
|
||||
class APawn;
|
||||
|
||||
/**
|
||||
* Base class for animation layer settings.
|
||||
* 动画层设置的基类。
|
||||
*/
|
||||
UCLASS(Abstract, BlueprintType, EditInlineNew, Const, CollapseCategories)
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_AnimLayerSetting : public UDataAsset
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Gets the override animation layer class.
|
||||
* 获取覆盖的动画层类。
|
||||
* @param OutLayerClass The output animation layer class. 输出的动画层类。
|
||||
* @return True if an override class is provided. 如果提供了覆盖类则返回true。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GMS|AnimationLayer")
|
||||
bool GetOverrideAnimLayerClass(TSubclassOf<UGMS_AnimLayer>& OutLayerClass) const;
|
||||
|
||||
/**
|
||||
* Validates the data for this animation layer setting.
|
||||
* 验证此动画层设置的数据。
|
||||
* @param ErrorText The error message if validation fails. 如果验证失败的错误信息。
|
||||
* @return True if data is valid. 如果数据有效则返回true。
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category="GMS|AnimationLayer", meta=(DisplayName="Is Data Valid"))
|
||||
bool K2_IsDataValid(FText& ErrorText) const;
|
||||
|
||||
#if WITH_EDITOR
|
||||
|
||||
public:
|
||||
/**
|
||||
* Validates data in the editor.
|
||||
* 在编辑器中验证数据。
|
||||
* @param Context The validation context. 验证上下文。
|
||||
* @return The validation result. 验证结果。
|
||||
*/
|
||||
virtual EDataValidationResult IsDataValid(class FDataValidationContext& Context) const override;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* Base class for all animation layers.
|
||||
* 所有动画层的基类。
|
||||
* @note Classes inheriting from this must only be linked to GMS_MainAnimInstance (the main animation instance). 从该类继承的类只能链接到GMS_MainAnimInstance(主动画实例)。
|
||||
*/
|
||||
UCLASS(BlueprintType, Abstract)
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_AnimLayer : public UAnimInstance
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
/**
|
||||
* The owning pawn of this animation layer.
|
||||
* 此动画层的拥有Pawn。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="State", Transient)
|
||||
TObjectPtr<APawn> PawnOwner;
|
||||
|
||||
/**
|
||||
* Reference to the movement system component.
|
||||
* 运动系统组件的引用。
|
||||
*/
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<UGMS_MovementSystemComponent> MovementSystem;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Weak reference to the parent main animation instance.
|
||||
* 对父主动画实例的弱引用。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, Category="State", Transient)
|
||||
TWeakObjectPtr<UGMS_MainAnimInstance> Parent;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* 构造函数。
|
||||
*/
|
||||
UGMS_AnimLayer();
|
||||
|
||||
/**
|
||||
* Gets the parent main animation instance.
|
||||
* 获取父主动画实例。
|
||||
* @return The parent main animation instance. 父主动画实例。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="GMS|AnimationLayer", Meta = (BlueprintThreadSafe, ReturnDisplayName = "Parent"))
|
||||
UGMS_MainAnimInstance* GetParent() const;
|
||||
|
||||
/**
|
||||
* Called when the animation layer is linked to the main animation instance.
|
||||
* 当动画层链接到主动画实例时调用。
|
||||
* @note Suitable for initialization tasks similar to BeginPlay. 适合执行类似BeginPlay的初始化任务。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GMS|AnimationLayer")
|
||||
void OnLinked();
|
||||
virtual void OnLinked_Implementation();
|
||||
|
||||
/**
|
||||
* Called when the animation layer is unlinked from the main animation instance.
|
||||
* 当动画层从主动画实例取消链接时调用。
|
||||
* @note Suitable for cleanup tasks similar to EndPlay. 适合执行类似EndPlay的清理任务。
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category="GMS|AnimationLayer")
|
||||
void OnUnlinked();
|
||||
virtual void OnUnlinked_Implementation();
|
||||
|
||||
/**
|
||||
* Applies settings to the animation layer.
|
||||
* 向动画层应用设置。
|
||||
* @param Setting The setting object to apply, cast to the desired type. 要应用的设置对象,可转换为所需类型。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GMS|AnimationLayer")
|
||||
void ApplySetting(const UGMS_AnimLayerSetting* Setting);
|
||||
virtual void ApplySetting_Implementation(const UGMS_AnimLayerSetting* Setting);
|
||||
|
||||
/**
|
||||
* Resets the settings of the animation layer.
|
||||
* 重置动画层的设置。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GMS|AnimationLayer")
|
||||
void ResetSetting();
|
||||
virtual void ResetSetting_Implementation();
|
||||
|
||||
/**
|
||||
* Initializes the animation.
|
||||
* 初始化动画。
|
||||
*/
|
||||
virtual void NativeInitializeAnimation() override;
|
||||
|
||||
/**
|
||||
* Called when the game starts.
|
||||
* 游戏开始时调用。
|
||||
*/
|
||||
virtual void NativeBeginPlay() override;
|
||||
|
||||
/**
|
||||
* Maps animation state names to gameplay tags for checking node relevance.
|
||||
* 将动画状态名称映射到游戏标签以检查节点相关性。
|
||||
* @note Used to determine if an animation state node is active via NodeRelevantTags in the main animation instance. 用于通过主动画实例中的NodeRelevantTags确定动画状态节点是否活跃。
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Settings", meta=(TitleProperty="Tag"))
|
||||
TArray<FGMS_AnimStateNameToTag> AnimStateNameToTagMapping;
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GMS_AnimLayer.h"
|
||||
#include "GMS_AnimLayer_Additive.generated.h"
|
||||
|
||||
/**
|
||||
* Base class for additive animation layer settings.
|
||||
* 附加动画层设置的基类。
|
||||
*/
|
||||
UCLASS(Abstract, Blueprintable)
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_AnimLayerSetting_Additive : public UGMS_AnimLayerSetting
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GMS_AnimLayer.h"
|
||||
#include "Settings/GMS_SettingObjectLibrary.h"
|
||||
#include "GMS_AnimLayer_Overlay.generated.h"
|
||||
|
||||
|
||||
#pragma region Blend Data Structures
|
||||
|
||||
USTRUCT()
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimData_PoseBlendSetting
|
||||
{
|
||||
GENERATED_BODY()
|
||||
virtual ~FGMS_AnimData_PoseBlendSetting() = default;
|
||||
/**
|
||||
* The overall adoption of the pose(0~1).
|
||||
* 姿势的整体采用度(0~1),1是开启,0是关闭。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(ClampMin=0, ClampMax=1, DisplayPriority=0))
|
||||
float BlendAmount{1.0f};
|
||||
};
|
||||
|
||||
USTRUCT()
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimData_PoseBlendSetting_TwoParams : public FGMS_AnimData_PoseBlendSetting
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Overall adoption of Montage playing on this slot (0~1)
|
||||
* 在此槽上播放的Montage的整体采用度(0~1)
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(ClampMin=0, ClampMax=1, DisplayPriority=2))
|
||||
float SlotBlendAmount{1.0f};
|
||||
};
|
||||
|
||||
USTRUCT()
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimData_PoseBlendSetting_ThreeParams : public FGMS_AnimData_PoseBlendSetting_TwoParams
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* How much to blend the overlay pose with the underlying motion.
|
||||
* 叠加姿势与底层运动的混合程度。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(ClampMin=0, ClampMax=1, DisplayPriority=1))
|
||||
float AdditiveBlendAmount{0.0f};
|
||||
};
|
||||
|
||||
USTRUCT()
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimData_PoseBlendSetting_FourParams : public FGMS_AnimData_PoseBlendSetting_ThreeParams
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(ClampMin=0, ClampMax=1, DisplayPriority=3))
|
||||
bool bMeshSpace{true};
|
||||
};
|
||||
|
||||
#pragma endregion
|
||||
|
||||
|
||||
/**
|
||||
* Base class for overlay animation layer settings.
|
||||
* 叠加动画层设置的基类。
|
||||
*/
|
||||
UCLASS(Abstract, Blueprintable)
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_AnimLayerSetting_Overlay : public UGMS_AnimLayerSetting
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Checks if the overlay mode is valid.
|
||||
* 检查叠加模式是否有效。
|
||||
* @param NewOverlayMode The overlay mode to check. 要检查的叠加模式。
|
||||
* @return True if the overlay mode is valid, false otherwise. 如果叠加模式有效则返回true,否则返回false。
|
||||
*/
|
||||
virtual bool IsValidForOverlayMode(const FGameplayTag& NewOverlayMode) const { return true; };
|
||||
};
|
||||
@@ -0,0 +1,351 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GMS_AnimLayer.h"
|
||||
#include "Animation/AnimExecutionContext.h"
|
||||
#include "Animation/AnimNodeReference.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "GMS_AnimLayer_Overlay.h"
|
||||
#include "GMS_AnimState.h"
|
||||
#include "InstancedStruct.h"
|
||||
#include "StructUtils/InstancedStruct.h"
|
||||
#include "GMS_AnimLayer_Overlay_ParallelPoseStack.generated.h"
|
||||
|
||||
|
||||
#pragma region Settings
|
||||
|
||||
/**
|
||||
* The body mask type for human body.
|
||||
* 针对人的身体遮罩。
|
||||
* @attention The enum order also controls the override priority.枚举顺序同时控制了姿势覆盖的优先级。
|
||||
*/
|
||||
UENUM(BlueprintType)
|
||||
enum class EGMS_BodyMask : uint8
|
||||
{
|
||||
Head,
|
||||
ArmLeft,
|
||||
ArmRight,
|
||||
UpperBody,
|
||||
LowerBody,
|
||||
FullBody,
|
||||
MAX UMETA(Hidden)
|
||||
};
|
||||
|
||||
USTRUCT()
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimData_BodyPose
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
virtual ~FGMS_AnimData_BodyPose() = default;
|
||||
|
||||
/**
|
||||
* Gameplay tag query against to main anim instance's relevance tags(the tags representing the relevant/active state of the anim state machine nodes).
|
||||
* 针对主动画实例的相关性标签的查询,相关性标签指:用于标识动画状态机节点是否激活的标签。
|
||||
* @details This pose will be considered if the tags matches this query.此姿势会在标签匹配此查询时被考虑。
|
||||
* @note Left empty will bypass this filter. 留空则不应用此过滤。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(Categories="GMS.SM"))
|
||||
FGameplayTagQuery RelevanceQuery;
|
||||
|
||||
/**
|
||||
* Gameplay tag query against to the movement system's owned tags.
|
||||
* 针对运动系统组件所拥有标签的查询。
|
||||
* @details This pose will be considered if the tags matches this query.此姿势会在标签匹配此查询时被考虑。
|
||||
* @note Left empty will bypass this filter. 留空则不应用此过滤。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
FGameplayTagQuery TagQuery;
|
||||
|
||||
/**
|
||||
* The pose sequence.
|
||||
* 姿势。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> Pose{nullptr};
|
||||
|
||||
/**
|
||||
* The pose time.
|
||||
* 姿势的帧位置。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
float PoseExplicitTime{0.0f};
|
||||
|
||||
UPROPERTY()
|
||||
int32 Priority = 999;
|
||||
|
||||
virtual EGMS_BodyMask GetBodyPart() const { return EGMS_BodyMask::FullBody; }
|
||||
|
||||
virtual void Reset()
|
||||
{
|
||||
Pose = nullptr;
|
||||
Priority = 999;
|
||||
};
|
||||
|
||||
virtual void UpdateLayeringState(FGMS_AnimState_Layering& LayeringState) const
|
||||
{
|
||||
};
|
||||
|
||||
bool IsValid() const { return Pose != nullptr; }
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
UPROPERTY(EditAnywhere, Category="GES", meta=(EditCondition=false, EditConditionHides))
|
||||
FString EditorFriendlyName;
|
||||
virtual void PreSave();
|
||||
#endif
|
||||
};
|
||||
|
||||
USTRUCT()
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimData_BodyPose_Full : public FGMS_AnimData_BodyPose
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(DisplayAfter="PoseExplicitTime"))
|
||||
FGMS_AnimData_PoseBlendSetting_ThreeParams HeadBlend;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(DisplayAfter="PoseExplicitTime"))
|
||||
FGMS_AnimData_PoseBlendSetting_FourParams ArmLeftBlend;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(DisplayAfter="PoseExplicitTime"))
|
||||
FGMS_AnimData_PoseBlendSetting_FourParams ArmRightBlend;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(DisplayAfter="PoseExplicitTime"))
|
||||
FGMS_AnimData_PoseBlendSetting HandLeftBlend;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(DisplayAfter="PoseExplicitTime"))
|
||||
FGMS_AnimData_PoseBlendSetting HandRightBlend;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(DisplayAfter="PoseExplicitTime"))
|
||||
FGMS_AnimData_PoseBlendSetting_ThreeParams SpineBlend;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(DisplayAfter="PoseExplicitTime"))
|
||||
FGMS_AnimData_PoseBlendSetting_TwoParams PelvisBlend;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(DisplayAfter="PoseExplicitTime"))
|
||||
FGMS_AnimData_PoseBlendSetting_TwoParams LegsBlend;
|
||||
|
||||
virtual EGMS_BodyMask GetBodyPart() const override { return EGMS_BodyMask::FullBody; }
|
||||
virtual void UpdateLayeringState(FGMS_AnimState_Layering& LayeringState) const override;
|
||||
};
|
||||
|
||||
USTRUCT()
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimData_BodyPose_Upper : public FGMS_AnimData_BodyPose
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(DisplayAfter="PoseExplicitTime"))
|
||||
FGMS_AnimData_PoseBlendSetting_ThreeParams HeadBlend;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(DisplayAfter="PoseExplicitTime"))
|
||||
FGMS_AnimData_PoseBlendSetting_FourParams ArmLeftBlend;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(DisplayAfter="PoseExplicitTime"))
|
||||
FGMS_AnimData_PoseBlendSetting_FourParams ArmRightBlend;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(DisplayAfter="PoseExplicitTime"))
|
||||
FGMS_AnimData_PoseBlendSetting HandLeftBlend;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(DisplayAfter="PoseExplicitTime"))
|
||||
FGMS_AnimData_PoseBlendSetting HandRightBlend;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(DisplayAfter="PoseExplicitTime"))
|
||||
FGMS_AnimData_PoseBlendSetting_ThreeParams SpineBlend;
|
||||
|
||||
virtual EGMS_BodyMask GetBodyPart() const override { return EGMS_BodyMask::UpperBody; }
|
||||
virtual void UpdateLayeringState(FGMS_AnimState_Layering& LayeringState) const override;
|
||||
};
|
||||
|
||||
USTRUCT()
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimData_BodyPose_Head : public FGMS_AnimData_BodyPose
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(DisplayAfter="PoseExplicitTime"))
|
||||
FGMS_AnimData_PoseBlendSetting_ThreeParams Blend;
|
||||
|
||||
virtual EGMS_BodyMask GetBodyPart() const override { return EGMS_BodyMask::Head; }
|
||||
virtual void UpdateLayeringState(FGMS_AnimState_Layering& LayeringState) const override;
|
||||
};
|
||||
|
||||
USTRUCT(meta=(Hidden))
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimData_BodyPose_Arms : public FGMS_AnimData_BodyPose
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(DisplayAfter="PoseExplicitTime"))
|
||||
FGMS_AnimData_PoseBlendSetting_FourParams Blend;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(DisplayAfter="PoseExplicitTime"))
|
||||
FGMS_AnimData_PoseBlendSetting HandBlend;
|
||||
};
|
||||
|
||||
USTRUCT()
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimData_BodyPose_ArmLeft : public FGMS_AnimData_BodyPose_Arms
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
virtual EGMS_BodyMask GetBodyPart() const override { return EGMS_BodyMask::ArmLeft; }
|
||||
virtual void UpdateLayeringState(FGMS_AnimState_Layering& LayeringState) const override;
|
||||
};
|
||||
|
||||
USTRUCT()
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimData_BodyPose_ArmRight : public FGMS_AnimData_BodyPose_Arms
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
virtual EGMS_BodyMask GetBodyPart() const override { return EGMS_BodyMask::ArmRight; }
|
||||
|
||||
virtual void UpdateLayeringState(FGMS_AnimState_Layering& LayeringState) const override;
|
||||
};
|
||||
|
||||
USTRUCT()
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimData_BodyPose_Lower : public FGMS_AnimData_BodyPose
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(DisplayAfter="PoseExplicitTime"))
|
||||
FGMS_AnimData_PoseBlendSetting_TwoParams PelvisBlend;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(DisplayAfter="PoseExplicitTime"))
|
||||
FGMS_AnimData_PoseBlendSetting_TwoParams LegsBlend;
|
||||
|
||||
virtual EGMS_BodyMask GetBodyPart() const override { return EGMS_BodyMask::LowerBody; }
|
||||
virtual void UpdateLayeringState(FGMS_AnimState_Layering& LayeringState) const override;
|
||||
};
|
||||
|
||||
USTRUCT()
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_OverlayModeSetting_ParallelPoseStack
|
||||
{
|
||||
GENERATED_BODY()
|
||||
virtual ~FGMS_OverlayModeSetting_ParallelPoseStack() = default;
|
||||
|
||||
/**
|
||||
* Unique tag for this overlay mode.
|
||||
* 此叠加模式的唯一标签。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(Categories="GMS.OverlayMode"))
|
||||
FGameplayTag Tag;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> BasePose = nullptr;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
TArray<TInstancedStruct<FGMS_AnimData_BodyPose_Full>> FullBodyPoses;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
TArray<TInstancedStruct<FGMS_AnimData_BodyPose_Upper>> UpperBodyPoses;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
TArray<TInstancedStruct<FGMS_AnimData_BodyPose_Lower>> LowerBodyPoses;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
TArray<TInstancedStruct<FGMS_AnimData_BodyPose_ArmLeft>> ArmLeftPoses;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
TArray<TInstancedStruct<FGMS_AnimData_BodyPose_ArmRight>> ArmRightPoses;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
TArray<TInstancedStruct<FGMS_AnimData_BodyPose_Head>> HeadPoses;
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
virtual void PreSave();
|
||||
#endif
|
||||
};
|
||||
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_BodyPartOverridePolicy
|
||||
{
|
||||
FGMS_BodyPartOverridePolicy();
|
||||
|
||||
bool CanOverride(EGMS_BodyMask NewPart, EGMS_BodyMask ExistingPart, int32 NewPriority, int32 ExistingPriority) const;
|
||||
void ApplyCoverage(EGMS_BodyMask BodyPart, TArray<TInstancedStruct<FGMS_AnimData_BodyPose>>& SelectedPoses, const TInstancedStruct<FGMS_AnimData_BodyPose>& NewPose, int32 NewPriority) const;
|
||||
|
||||
TMap<EGMS_BodyMask, TArray<EGMS_BodyMask>> FallbackChain;
|
||||
};
|
||||
|
||||
/**
|
||||
* Anim layer setting for ParallelPoseStack overlay system.
|
||||
* 针对"并行姿势栈"的动画叠加系统设置。
|
||||
* @details Similar to PoseStack, but allowing multiple body part has different overlay at the same time.与姿势栈相似,但允许同时让身体的不同部位有不同的姿势。
|
||||
*/
|
||||
UCLASS(NotBlueprintable)
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_AnimLayerSetting_Overlay_ParallelPoseStack final : public UGMS_AnimLayerSetting_Overlay
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, Category="Settings", meta=(EditCondition=false, EditConditionHides))
|
||||
TMap<FGameplayTag, FGMS_OverlayModeSetting_ParallelPoseStack> AcceleratedOverlayModes;
|
||||
|
||||
protected:
|
||||
UPROPERTY(EditAnywhere, Category="Settings", meta=(TitleProperty="Tag"))
|
||||
TArray<FGMS_OverlayModeSetting_ParallelPoseStack> OverlayModes;
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
virtual void PreSave(FObjectPreSaveContext SaveContext) override;
|
||||
#endif
|
||||
};
|
||||
|
||||
#pragma endregion
|
||||
|
||||
/**
|
||||
* Anim layer implementation for ParallelPoseStack overlay system.
|
||||
* 针对"并行姿势栈"的动画叠加系统实现。
|
||||
*/
|
||||
UCLASS(Abstract)
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_AnimLayer_Overlay_ParallelPoseStack : public UGMS_AnimLayer
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
virtual void ApplySetting_Implementation(const UGMS_AnimLayerSetting* Setting) override;
|
||||
virtual void ResetSetting_Implementation() override;
|
||||
virtual void NativeInitializeAnimation() override;
|
||||
virtual void NativeThreadSafeUpdateAnimation(float DeltaSeconds) override;
|
||||
|
||||
const FGMS_OverlayModeSetting_ParallelPoseStack& GetOverlayModeSetting() const;
|
||||
void UpdateAnim(const FAnimUpdateContext& Context, const FAnimNodeReference& Node, const EGMS_BodyMask& BodyMask, const FGMS_AnimData_BodyPose& BodyPose);
|
||||
|
||||
void SelectPoses(const FGameplayTagContainer& Tags, const FGameplayTagContainer& Nodes);
|
||||
void UpdateLayeringState(float DeltaSeconds);
|
||||
void UpdateLayeringSmoothState(float DeltaSeconds);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GMS|Animation", meta=(BlueprintThreadSafe))
|
||||
void BasePose_AnimUpdate(UPARAM(ref)
|
||||
FAnimUpdateContext& Context, UPARAM(ref)
|
||||
FAnimNodeReference& Node);
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GMS|Animation", meta=(BlueprintThreadSafe))
|
||||
void BodyPart_AnimUpdate(UPARAM(ref)
|
||||
FAnimUpdateContext& Context, UPARAM(ref)
|
||||
FAnimNodeReference& Node, EGMS_BodyMask BodyMask);
|
||||
|
||||
UPROPERTY()
|
||||
TArray<TInstancedStruct<FGMS_AnimData_BodyPose>> SelectedBodyPoses; // Indexed by EGMS_BodyPart
|
||||
|
||||
UPROPERTY()
|
||||
TObjectPtr<const UGMS_AnimLayerSetting_Overlay_ParallelPoseStack> CurrentSetting = nullptr;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
FGameplayTag CurrentOverlayMode;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> BasePose = nullptr;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GMS")
|
||||
bool bHasValidSetting = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="State")
|
||||
FGMS_AnimState_LayeringSmooth LayeringSmoothState;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="State")
|
||||
bool bArmLeftMeshSpace{false};
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="State")
|
||||
bool bArmRightMeshSpace{false};
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="State")
|
||||
FGMS_AnimState_Layering LayeringState;
|
||||
|
||||
FGMS_BodyPartOverridePolicy OverridePolicy;
|
||||
};
|
||||
@@ -0,0 +1,381 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GMS_AnimLayer.h"
|
||||
#include "GMS_AnimLayer_Overlay.h"
|
||||
#include "Settings/GMS_SettingObjectLibrary.h"
|
||||
#include "GMS_AnimLayer_Overlay_ParallelSequenceStack.generated.h"
|
||||
|
||||
struct FCachedAnimStateData;
|
||||
|
||||
/**
|
||||
* Single entry within "GMS_ParallelSequenceStack".
|
||||
* "GMS_ParallelSequenceStack"中的单个条目
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_ParallelSequenceStackEntry
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Gameplay tag query against to the movement system's owned tags.
|
||||
* 针对运动系统组件所拥有标签的查询。
|
||||
* @details This sequence will be considered if the tags matches this query.此序列会在标签匹配此查询时被考虑。
|
||||
* @note Left empty will bypass this filter. 留空则不应用此过滤。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GMS")
|
||||
FGameplayTagQuery TagQuery;
|
||||
|
||||
/**
|
||||
* Animation sequence for the overlay.
|
||||
* 叠加的动画序列。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GMS")
|
||||
TObjectPtr<UAnimSequenceBase> Sequence = nullptr;
|
||||
|
||||
/**
|
||||
* Blend weight for the overlay.
|
||||
* 叠加的混合权重。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GMS", meta=(ClampMax=1, ClampMin=0))
|
||||
float BlendWeight = 1.0f;
|
||||
|
||||
/**
|
||||
* Whether to blend in mesh space.
|
||||
* 是否在网格空间中混合。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GMS")
|
||||
bool MeshSpaceBlend = true;
|
||||
|
||||
/**
|
||||
* Play mode for the overlay animation.
|
||||
* 叠加动画的播放模式。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GMS")
|
||||
EGMS_OverlayPlayMode PlayMode{EGMS_OverlayPlayMode::SequenceEvaluator};
|
||||
|
||||
/**
|
||||
* Start position for sequence player mode.
|
||||
* 序列播放器模式的起始位置。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GMS", meta=(ClampMin=0, EditCondition="PlayMode == EGMS_OverlayPlayMode::SequencePlayer", EditConditionHides))
|
||||
float StartPosition = 0.0f;
|
||||
|
||||
/**
|
||||
* Explicit time for sequence evaluator mode.
|
||||
* 序列评估器模式的明确时间。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GMS", meta=(ClampMin=0, EditCondition="PlayMode == EGMS_OverlayPlayMode::SequenceEvaluator", EditConditionHides))
|
||||
float ExplicitTime = 0.0f;
|
||||
|
||||
/**
|
||||
* Blend mode for the overlay animation.
|
||||
* 叠加动画的混合模式。
|
||||
* @attention Use branch filters for multiple skeletons to improve reusability. 如果项目中使用多个骨架,建议使用分支过滤器以提高复用性。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GMS")
|
||||
EGMS_LayeredBoneBlendMode BlendMode{EGMS_LayeredBoneBlendMode::BlendMask};
|
||||
|
||||
/**
|
||||
* Blend mask name for the overlay.
|
||||
* 叠加的混合遮罩名称。
|
||||
* @attention Must be defined in the skeleton first. 必须先在骨架中定义。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GMS", meta=(EditCondition="BlendMode == EGMS_LayeredBoneBlendMode::BlendMask", EditConditionHides))
|
||||
FName BlendMaskName;
|
||||
|
||||
/**
|
||||
* Branch filters for the overlay.
|
||||
* 叠加的分支过滤器。
|
||||
* @attention Refer to documentation for usage details. 请参阅文档了解使用详情。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GMS", meta=(EditCondition="BlendMode == EGMS_LayeredBoneBlendMode::BranchFilter", EditConditionHides))
|
||||
FGMS_InputBlendPose BranchFilters;
|
||||
|
||||
/**
|
||||
* Speed to blend to the specified blend weight.
|
||||
* 混合到指定混合权重的速度。
|
||||
* @attention Zero means instant blending. 为零表示立即混合。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GMS", meta=(ClampMin=0))
|
||||
float BlendInSpeed = 10.f;
|
||||
|
||||
/**
|
||||
* Speed to blend back to zero weight.
|
||||
* 混合回零权重的速度。
|
||||
* @attention Zero means instant return to zero. 为零表示立即归零。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GMS", meta=(ClampMin=0))
|
||||
float BlendOutSpeed = 10.f;
|
||||
|
||||
/**
|
||||
* Indicates if the overlay data is valid.
|
||||
* 指示叠加数据是否有效。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GMS")
|
||||
bool bValid{false};
|
||||
|
||||
/**
|
||||
* Equality operator for overlay data.
|
||||
* 叠加数据的相等比较运算符。
|
||||
*/
|
||||
friend bool operator==(const FGMS_ParallelSequenceStackEntry& Lhs, const FGMS_ParallelSequenceStackEntry& RHS)
|
||||
{
|
||||
return Lhs.Sequence == RHS.Sequence
|
||||
&& Lhs.BlendMode == RHS.BlendMode
|
||||
&& Lhs.MeshSpaceBlend == RHS.MeshSpaceBlend
|
||||
&& Lhs.bValid == RHS.bValid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inequality operator for overlay data.
|
||||
* 叠加数据的不相等比较运算符。
|
||||
*/
|
||||
friend bool operator!=(const FGMS_ParallelSequenceStackEntry& Lhs, const FGMS_ParallelSequenceStackEntry& RHS)
|
||||
{
|
||||
return !(Lhs == RHS);
|
||||
}
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
/**
|
||||
* Validates the overlay animation data.
|
||||
* 验证叠加动画数据。
|
||||
*/
|
||||
void Validate();
|
||||
/**
|
||||
* Friendly message for displaying in the editor.
|
||||
* 在编辑器中显示的友好消息。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, Category = "GMS", Meta = (EditCondition = False, EditConditionHides))
|
||||
FString EditorMessage;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* A single parallel sequence stack
|
||||
* 堆叠叠加动画数据的结构体。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_ParallelSequenceStack
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Animation nodes that determine overlay relevance.
|
||||
* 确定叠加相关性的动画节点。
|
||||
* @attention Configure state-to-tag mapping in the main AnimInstance or derived GMS_AnimLayer. 在主AnimInstance或派生的GMS_AnimLayer中配置状态到标签的映射。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", meta=(Categories="GMS.SM"))
|
||||
FGameplayTagContainer TargetAnimNodes;
|
||||
|
||||
/**
|
||||
* List of potential overlays, with the first matching one selected.
|
||||
* 潜在叠加列表,第一个匹配的将被选用。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", meta=(TitleProperty="EditorMessage"))
|
||||
TArray<FGMS_ParallelSequenceStackEntry> Overlays;
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
/**
|
||||
* Friendly name for displaying in the editor.
|
||||
* 在编辑器中显示的友好名称。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, Category = "GMS", Meta = (EditCondition = False, EditConditionHides))
|
||||
FString EditorFriendlyName;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* Parallel Sequence Stacks for certain overlay mode.
|
||||
* 针对特定叠加模式的并行Sequence栈
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FGMS_OverlayModeSetting_ParallelSequenceStack
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Unique tag for this overlay mode.
|
||||
* 此叠加模式的唯一标签。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(Categories="GMS.OverlayMode"))
|
||||
FGameplayTag Tag;
|
||||
|
||||
/**
|
||||
* List of parallel sequence stack
|
||||
* 用于并行混合的动画序列栈列表。
|
||||
* @attention Later stacks have higher priority; each stack selects one entry from multiple candidates. 越靠后的栈优先级越高;每个栈从多个候选动画中选择一个。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", meta=(TitleProperty="EditorFriendlyName"))
|
||||
TArray<FGMS_ParallelSequenceStack> Stacks;
|
||||
};
|
||||
|
||||
/**
|
||||
* Struct for the state of a stacked overlay.
|
||||
* 栈式叠加状态的结构体。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_ParallelSequenceStackState
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Indicates if the target animation nodes are relevant.
|
||||
* 指示目标动画节点是否相关。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
bool bRelevant{false};
|
||||
|
||||
/**
|
||||
* Current blend weight of the overlay.
|
||||
* 叠加的当前混合权重。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
float BlendWeight{0.0f};
|
||||
|
||||
/**
|
||||
* Speed to blend out the overlay.
|
||||
* 叠加混合退出的速度。
|
||||
*/
|
||||
float BlendOutSpeed{0.0f};
|
||||
|
||||
/**
|
||||
* Overlay animation data.
|
||||
* 叠加动画数据。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
FGMS_ParallelSequenceStackEntry Overlay;
|
||||
};
|
||||
|
||||
/**
|
||||
* Anim layer setting for ParallelSequenceStack overlay system.
|
||||
* 针对"并行序列栈"的动画叠加系统设置。
|
||||
* @attention Its more recommended to use SequenceStack instead of this one.通常你应该使用序列栈,而不是这个。
|
||||
*/
|
||||
UCLASS(NotBlueprintable)
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_AnimLayerSetting_Overlay_ParallelSequenceStack final : public UGMS_AnimLayerSetting_Overlay
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Map of accelerated overlay modes.
|
||||
* 加速叠加模式的映射。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(EditCondition=false, EditConditionHides))
|
||||
TMap<FGameplayTag, FGMS_OverlayModeSetting_ParallelSequenceStack> AcceleratedOverlayModes;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* List of overlay mode settings.
|
||||
* 叠加模式设置列表。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Settings", meta=(TitleProperty="Tag"))
|
||||
TArray<FGMS_OverlayModeSetting_ParallelSequenceStack> OverlayModes;
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
/**
|
||||
* Called before saving the object.
|
||||
* 在保存对象之前调用。
|
||||
* @param SaveContext The save context. 保存上下文。
|
||||
*/
|
||||
virtual void PreSave(FObjectPreSaveContext SaveContext) override;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Anim layer implementation for ParallelSequenceStack overlay system.
|
||||
* 针对"并行序列栈"的动画叠加系统实现。
|
||||
*/
|
||||
UCLASS(Abstract)
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_AnimLayer_Overlay_ParallelSequenceStack : public UGMS_AnimLayer
|
||||
{
|
||||
GENERATED_BODY()
|
||||
friend UGMS_AnimLayerSetting_Overlay_ParallelSequenceStack;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Called when the component begins play.
|
||||
* 组件开始播放时调用。
|
||||
*/
|
||||
virtual void NativeBeginPlay() override;
|
||||
|
||||
/**
|
||||
* Updates the animation.
|
||||
* 更新动画。
|
||||
* @param DeltaSeconds Time since the last update. 自上次更新以来的时间。
|
||||
*/
|
||||
virtual void NativeUpdateAnimation(float DeltaSeconds) override;
|
||||
|
||||
/**
|
||||
* Updates the animation in a thread-safe manner.
|
||||
* 以线程安全的方式更新动画。
|
||||
* @param DeltaSeconds Time since the last update. 自上次更新以来的时间。
|
||||
*/
|
||||
virtual void NativeThreadSafeUpdateAnimation(float DeltaSeconds) override;
|
||||
|
||||
/**
|
||||
* Applies the specified animation layer setting.
|
||||
* 应用指定的动画层设置。
|
||||
* @param Setting The animation layer setting to apply. 要应用的动画层设置。
|
||||
*/
|
||||
virtual void ApplySetting_Implementation(const UGMS_AnimLayerSetting* Setting) override;
|
||||
|
||||
/**
|
||||
* Resets the animation layer setting.
|
||||
* 重置动画层设置。
|
||||
*/
|
||||
virtual void ResetSetting_Implementation() override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Refreshes the relevance of the overlay stacks.
|
||||
* 刷新叠加堆栈的相关性。
|
||||
*/
|
||||
virtual void RefreshRelevance();
|
||||
|
||||
/**
|
||||
* Refreshes the blending of the overlay stacks.
|
||||
* 刷新叠加堆栈的混合。
|
||||
* @param DeltaSeconds Time since the last update. 自上次更新以来的时间。
|
||||
*/
|
||||
virtual void RefreshBlend(float DeltaSeconds);
|
||||
|
||||
/**
|
||||
* Previous overlay setting.
|
||||
* 前一个叠加设置。
|
||||
*/
|
||||
UPROPERTY()
|
||||
TObjectPtr<const UGMS_AnimLayerSetting_Overlay_ParallelSequenceStack> PrevSetting{nullptr};
|
||||
|
||||
/**
|
||||
* Previous overlay mode tag.
|
||||
* 前一个叠加模式标签。
|
||||
*/
|
||||
UPROPERTY()
|
||||
FGameplayTag PrevOverlayMode;
|
||||
|
||||
/**
|
||||
* List of overlay stacks.
|
||||
* 叠加堆栈列表。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Settings", meta=(TitleProperty="EditorFriendlyName"))
|
||||
TArray<FGMS_ParallelSequenceStack> OverlayStacks;
|
||||
|
||||
/**
|
||||
* List of overlay stack states.
|
||||
* 叠加堆栈状态列表。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="State")
|
||||
TArray<FGMS_ParallelSequenceStackState> OverlayStackStates;
|
||||
|
||||
/**
|
||||
* Maximum number of overlay layers.
|
||||
* 最大叠加层数。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="State", meta=(ClampMin=4))
|
||||
int32 MaxLayers{10};
|
||||
};
|
||||
@@ -0,0 +1,439 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GMS_AnimLayer.h"
|
||||
#include "GMS_AnimLayer_Overlay.h"
|
||||
#include "GMS_AnimState.h"
|
||||
#include "Settings/GMS_SettingObjectLibrary.h"
|
||||
#include "GMS_AnimLayer_Overlay_PoseStack.generated.h"
|
||||
|
||||
#pragma region Deprecated
|
||||
/**
|
||||
* Enum for pose overlay setting types.
|
||||
* 姿势叠加设置类型的枚举。
|
||||
*/
|
||||
UENUM(BlueprintType)
|
||||
enum class EGMS_PoseOverlaySettingType: uint8
|
||||
{
|
||||
Simple,
|
||||
Layered
|
||||
};
|
||||
|
||||
/**
|
||||
* Struct for simple pose overlay animation data.
|
||||
* 简单姿势叠加动画数据的结构体。
|
||||
*/
|
||||
USTRUCT()
|
||||
struct FGMS_AnimData_PoseOverlay_Simple
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Idle pose animation sequence.
|
||||
* 空闲姿势动画序列。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category = "GMS")
|
||||
TObjectPtr<UAnimSequence> IdlePose{nullptr};
|
||||
|
||||
/**
|
||||
* Explicit time for the idle pose.
|
||||
* 空闲姿势的明确时间。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category = "GMS")
|
||||
float IdlePoseExplicitTime{0};
|
||||
|
||||
/**
|
||||
* Moving pose animation sequence.
|
||||
* 移动姿势动画序列。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category = "GMS")
|
||||
TObjectPtr<UAnimSequence> MovingPose{nullptr};
|
||||
|
||||
/**
|
||||
* Explicit time for the moving pose.
|
||||
* 移动姿势的明确时间。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category = "GMS")
|
||||
float MovingPoseExplicitTime{0};
|
||||
|
||||
/**
|
||||
* Aiming sweep pose animation sequence.
|
||||
* 瞄准扫动画序列。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category = "GMS")
|
||||
TObjectPtr<UAnimSequence> AimingSweepPose{nullptr};
|
||||
};
|
||||
|
||||
/**
|
||||
* Struct for layered pose overlay anim data.
|
||||
* 分层姿势叠加动画数据的结构体。
|
||||
*/
|
||||
USTRUCT()
|
||||
struct FGMS_AnimData_PoseOverlay_Layered
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Gameplay tag query for layered pose.
|
||||
* 分层姿势的游戏标签查询。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category = "GMS")
|
||||
FGameplayTagQuery TagQuery;
|
||||
|
||||
/**
|
||||
* Idle pose animation sequence for layered pose.
|
||||
* 分层姿势的空闲姿势动画序列。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category = "GMS")
|
||||
TObjectPtr<UAnimSequence> IdlePose{nullptr};
|
||||
|
||||
/**
|
||||
* Explicit time for the idle pose in layered pose.
|
||||
* 分层姿势中空闲姿势的明确时间。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category = "GMS")
|
||||
float IdlePoseExplicitTime{0};
|
||||
|
||||
/**
|
||||
* Moving pose animation sequence for layered pose.
|
||||
* 分层姿势的移动姿势动画序列。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category = "GMS")
|
||||
TObjectPtr<UAnimSequence> MovingPose{nullptr};
|
||||
|
||||
/**
|
||||
* Explicit time for the moving pose in layered pose.
|
||||
* 分层姿势中移动姿势的明确时间。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category = "GMS")
|
||||
float MovingPoseExplicitTime{0};
|
||||
|
||||
/**
|
||||
* Aiming sweep pose animation sequence for layered pose.
|
||||
* 分层姿势的瞄准扫动画序列。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category = "GMS")
|
||||
TObjectPtr<UAnimSequence> AimingSweepPose{nullptr};
|
||||
};
|
||||
#pragma endregion
|
||||
|
||||
USTRUCT()
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_PerBodyPoseBlendSetting
|
||||
{
|
||||
GENERATED_BODY()
|
||||
virtual ~FGMS_PerBodyPoseBlendSetting() = default;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
FGMS_AnimData_PoseBlendSetting_ThreeParams HeadBlend;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
FGMS_AnimData_PoseBlendSetting_FourParams ArmLeftBlend;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
FGMS_AnimData_PoseBlendSetting_FourParams ArmRightBlend;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
FGMS_AnimData_PoseBlendSetting HandLeftBlend;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
FGMS_AnimData_PoseBlendSetting HandRightBlend;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
FGMS_AnimData_PoseBlendSetting_ThreeParams SpineBlend;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
FGMS_AnimData_PoseBlendSetting_TwoParams PelvisBlend;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
FGMS_AnimData_PoseBlendSetting_TwoParams LegsBlend;
|
||||
|
||||
//Read layering setting from sequence curves.
|
||||
void ApplyFromSequence(const UAnimSequence* InSequence, float ExplicitTime);
|
||||
//Set blend setting to layering state.
|
||||
virtual void ApplyToLayeringState(FGMS_AnimState_Layering& InLayeringState) const;
|
||||
//Read setting from layering state.
|
||||
virtual void ApplyFromLayeringState(const FGMS_AnimState_Layering& InLayeringState);
|
||||
};
|
||||
|
||||
/**
|
||||
* Single entry within GMS_OverlayModeSetting_PoseStack
|
||||
* GMS_OverlayModeSetting_PoseStack中的单个条目。
|
||||
*
|
||||
*/
|
||||
USTRUCT()
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_PoseStackEntry
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Gameplay tag query against to main anim instance's relevance tags(the tags representing the relevant/active state of the anim state machine nodes).
|
||||
* 针对主动画实例的相关性标签的查询,相关性标签指:用于标识动画状态机节点是否激活的标签。
|
||||
* @details This pose will be considered if the tags matches this query.此姿势会在标签匹配此查询时被考虑。
|
||||
* @note Left empty will bypass this filter. 留空则不应用此过滤。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(Categories="GMS.SM"))
|
||||
FGameplayTagQuery RelevanceQuery;
|
||||
|
||||
/**
|
||||
* Gameplay tag query against to the movement system's owned tags.
|
||||
* 针对运动系统组件所拥有标签的查询。
|
||||
* @details This pose will be considered if the tags matches this query.此姿势会在标签匹配此查询时被考虑。
|
||||
* @note Left empty will bypass this filter. 留空则不应用此过滤。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
FGameplayTagQuery TagQuery;
|
||||
|
||||
/**
|
||||
* The sequence for this pose.
|
||||
* 此姿势的动画序列。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> Pose{nullptr};
|
||||
|
||||
/**
|
||||
* Controls the blend weight for each body parts.
|
||||
* 控制每一个身体部位的混合权重。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
FGMS_PerBodyPoseBlendSetting PoseBlend;
|
||||
|
||||
/**
|
||||
* Explicit time for the pose.
|
||||
* 姿势的明确播放时间。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
float ExplicitTime{0};
|
||||
|
||||
/**
|
||||
* Aiming sweep pose animation sequence for layered pose.
|
||||
* 分层姿势的瞄准扫动画序列。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> AimingSweepPose{nullptr};
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
UPROPERTY(EditAnywhere, Category = "GMS", meta = (EditCondition = false, EditConditionHides))
|
||||
FString EditorFriendlyName;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* Pose overlay setting for specific overlay mode.
|
||||
* 针对特定动画叠加模式的姿势叠加设置。
|
||||
*/
|
||||
USTRUCT()
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_OverlayModeSetting_PoseStack
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Unique tag for this overlay mode.
|
||||
* 此叠加模式的唯一标签。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(Categories="GMS.OverlayMode"))
|
||||
FGameplayTag Tag;
|
||||
|
||||
/**
|
||||
* Base pose animation sequence.
|
||||
* 基础姿势动画序列。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> BasePose{nullptr};
|
||||
|
||||
/**
|
||||
* Potential dynamic poses.
|
||||
* 潜在的动态poses。
|
||||
* @note To ensure smooth pose switching, Avoid using "multi frames sequence with different explicit time setup", favors "single frame sequence with 0 explicit time setup."
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(TitleProperty="EditorFriendlyName"))
|
||||
TArray<FGMS_PoseStackEntry> Poses;
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
/**
|
||||
* Type of pose overlay setting.
|
||||
* 姿势叠加设置的类型。
|
||||
*/
|
||||
UE_DEPRECATED (1.5, "deprecated and will be removed in 1.6")
|
||||
UPROPERTY(EditAnywhere, Category = "Deprecated", meta = (EditCondition = false, EditConditionHides))
|
||||
EGMS_PoseOverlaySettingType PoseOverlaySettingType{EGMS_PoseOverlaySettingType::Simple};
|
||||
|
||||
/**
|
||||
* Simple pose overlay settings.
|
||||
* 简单姿势叠加设置。
|
||||
*/
|
||||
UE_DEPRECATED (1.5, "deprecated and will be removed in 1.6")
|
||||
UPROPERTY(EditAnywhere, Category = "Deprecated", meta = (EditCondition = false, EditConditionHides))
|
||||
FGMS_AnimData_PoseOverlay_Simple SimplePoseSetting;
|
||||
|
||||
/**
|
||||
* Layered pose overlay settings.
|
||||
* 分层姿势叠加设置。
|
||||
*/
|
||||
UE_DEPRECATED (1.5, "deprecated and will be removed in 1.6")
|
||||
UPROPERTY(EditAnywhere, Category = "Deprecated", meta = (EditCondition = false, EditConditionHides))
|
||||
TArray<FGMS_AnimData_PoseOverlay_Layered> LayeredPoseSetting;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Anim layer setting for PoseStack overlay system.
|
||||
* 针对"姿势栈"的动画叠加系统设置。
|
||||
* @details Similar to ALS's layering system, but more dynamic and easy to use. 类似于ALS的叠层系统,但更动态且易于使用。
|
||||
*/
|
||||
UCLASS(NotBlueprintable)
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_AnimLayerSetting_Overlay_PoseStack : public UGMS_AnimLayerSetting_Overlay
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Map of accelerated overlay modes.
|
||||
* 加速叠加模式的映射。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(EditCondition=false, EditConditionHides))
|
||||
TMap<FGameplayTag, FGMS_OverlayModeSetting_PoseStack> AcceleratedOverlayModes;
|
||||
|
||||
/**
|
||||
* Checks if the overlay mode is valid.
|
||||
* 检查叠加模式是否有效。
|
||||
* @param NewOverlayMode The overlay mode to check. 要检查的叠加模式。
|
||||
* @return True if the overlay mode is valid, false otherwise. 如果叠加模式有效则返回true,否则返回false。
|
||||
*/
|
||||
virtual bool IsValidForOverlayMode(const FGameplayTag& NewOverlayMode) const override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* List of pose overlay settings.
|
||||
* 姿势叠加设置列表。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(TitleProperty="Tag"))
|
||||
TArray<FGMS_OverlayModeSetting_PoseStack> OverlayModes;
|
||||
|
||||
#if WITH_EDITOR
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, CallInEditor, Category = "GMS")
|
||||
void RunDataMigration(bool bResetDeprecatedSettings = false);
|
||||
|
||||
UFUNCTION(BlueprintCallable, CallInEditor, Category = "GMS")
|
||||
static void RunDataMigrationFromDefinition(UGMS_MovementDefinition* InDefinition, bool bResetDeprecatedSettings = false);
|
||||
|
||||
/**
|
||||
* Called before saving the object.
|
||||
* 在保存对象之前调用。
|
||||
* @param SaveContext The save context. 保存上下文。
|
||||
*/
|
||||
virtual void PreSave(FObjectPreSaveContext SaveContext) override;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* Anim layer implementation for PoseStack overlay system.
|
||||
* 针对"姿势栈"的动画叠加系统实现。
|
||||
*/
|
||||
UCLASS(Abstract)
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_AnimLayer_Overlay_PoseStack : public UGMS_AnimLayer
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Applies the specified animation layer setting.
|
||||
* 应用指定的动画层设置。
|
||||
* @param Setting The animation layer setting to apply. 要应用的动画层设置。
|
||||
*/
|
||||
virtual void ApplySetting_Implementation(const UGMS_AnimLayerSetting* Setting) override;
|
||||
|
||||
/**
|
||||
* Resets the animation layer setting.
|
||||
* 重置动画层设置。
|
||||
*/
|
||||
virtual void ResetSetting_Implementation() override;
|
||||
bool SelectPose();
|
||||
|
||||
/**
|
||||
* Updates the animation in a thread-safe manner.
|
||||
* 以线程安全的方式更新动画。
|
||||
* @param DeltaSeconds Time since the last update. 自上次更新以来的时间。
|
||||
*/
|
||||
virtual void NativeThreadSafeUpdateAnimation(float DeltaSeconds) override;
|
||||
|
||||
const FGMS_OverlayModeSetting_PoseStack& GetOverlayModeSetting() const;
|
||||
|
||||
virtual void UpdateLayeringSmoothState(float DeltaSeconds);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Base pose animation sequence.
|
||||
* 基础姿势动画序列。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Settings")
|
||||
TObjectPtr<UAnimSequence> BasePose{nullptr};
|
||||
|
||||
/**
|
||||
* Current Selected Pose.
|
||||
* 当前选择的Pose.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Settings")
|
||||
TObjectPtr<UAnimSequence> Pose{nullptr};
|
||||
|
||||
UPROPERTY()
|
||||
TObjectPtr<UAnimSequence> PrevPose{nullptr};
|
||||
|
||||
/**
|
||||
* Explicit time for the current pose
|
||||
* 当前姿势的明确时间。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Settings")
|
||||
float ExplicitTime{0};
|
||||
|
||||
/**
|
||||
* Aiming sweep pose animation sequence.
|
||||
* 瞄准扫动画序列。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Settings")
|
||||
TObjectPtr<UAnimSequence> AimingSweepPose{nullptr};
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Settings")
|
||||
bool bValidPose{false};
|
||||
|
||||
/**
|
||||
* Indicates if the aiming pose is valid.
|
||||
* 指示瞄准姿势是否有效。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Settings")
|
||||
bool bValidAimingPose{false};
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Settings")
|
||||
bool bHasValidSetting = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Settings")
|
||||
float LayeringSmoothSpeed{2.0f};
|
||||
|
||||
/**
|
||||
* Current layering state.
|
||||
* 当前分层状态。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="State")
|
||||
FGMS_AnimState_Layering LayeringState;
|
||||
|
||||
/**
|
||||
* Current smoothed layering state.
|
||||
* 当前的平滑分层状态。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="State")
|
||||
FGMS_AnimState_LayeringSmooth LayeringSmoothState;
|
||||
|
||||
/**
|
||||
* Reference to the pose-based overlay settings.
|
||||
* 基于姿势的叠加设置的引用。
|
||||
*/
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<const UGMS_AnimLayerSetting_Overlay_PoseStack> CurrentSetting;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
FGameplayTag CurrentOverlayMode;
|
||||
};
|
||||
@@ -0,0 +1,276 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GMS_AnimLayer_Overlay.h"
|
||||
#include "GMS_AnimLayer_Overlay_SequenceStack.generated.h"
|
||||
|
||||
class UGMS_AnimLayerSetting_Overlay_ParallelSequenceStack;
|
||||
/**
|
||||
* Single entry within "GMS_OverlayModeSetting_SequenceStack".
|
||||
* "GMS_OverlayModeSetting_SequenceStack"中的单个条目
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_SequenceStackEntry
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Gameplay tag query against to main anim instance's relevance tags(the tags representing the relevant/active state of the anim state machine nodes).
|
||||
* 针对主动画实例的相关性标签的查询,相关性标签指:用于标识动画状态机节点是否激活的标签。
|
||||
* @details This pose will be considered if the tags matches this query.此姿势会在标签匹配此查询时被考虑。
|
||||
* @note Left empty will bypass this filter. 留空则不应用此过滤。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GMS", meta=(Categories="GMS.SM"))
|
||||
FGameplayTagQuery RelevanceQuery;
|
||||
|
||||
/**
|
||||
* Gameplay tag query against to the movement system's owned tags.
|
||||
* 针对运动系统组件所拥有标签的查询。
|
||||
* @details This pose will be considered if the tags matches this query.此姿势会在标签匹配此查询时被考虑。
|
||||
* @note Left empty will bypass this filter. 留空则不应用此过滤。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GMS")
|
||||
FGameplayTagQuery TagQuery;
|
||||
|
||||
/**
|
||||
* Animation sequence for the overlay.
|
||||
* 叠加的动画序列。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> Sequence = nullptr;
|
||||
|
||||
/**
|
||||
* Play mode for the overlay animation.
|
||||
* 叠加动画的播放模式。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GMS")
|
||||
EGMS_OverlayPlayMode PlayMode{EGMS_OverlayPlayMode::SequencePlayer};
|
||||
|
||||
/**
|
||||
* Blend weight for the overlay.
|
||||
* 叠加的混合权重。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GMS", meta=(ClampMax=1, ClampMin=0))
|
||||
float BlendWeight = 1.0f;
|
||||
|
||||
/**
|
||||
* Speed to blend to the specified blend weight.
|
||||
* 混合到指定混合权重的速度。
|
||||
* @attention Zero means instant blending. 为零表示立即混合。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GMS", meta=(ClampMin=0))
|
||||
float BlendInSpeed = 10.f;
|
||||
|
||||
/**
|
||||
* Speed to blend back to zero weight.
|
||||
* 混合回零权重的速度。
|
||||
* @attention Zero means instant return to zero. 为零表示立即归零。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GMS", meta=(ClampMin=0))
|
||||
float BlendOutSpeed = 10.f;
|
||||
|
||||
/**
|
||||
* Whether to blend in mesh space.
|
||||
* 是否在网格空间中混合。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GMS")
|
||||
bool MeshSpaceBlend = true;
|
||||
|
||||
/**
|
||||
* The start time of animation.
|
||||
* 动画开始播放的事件。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GMS")
|
||||
float AnimationTime = 0.0f;
|
||||
|
||||
/**
|
||||
* Blend mode for the overlay animation.
|
||||
* 叠加动画的混合模式。
|
||||
* @attention Use branch filters for multiple skeletons to improve reusability. 如果项目中使用多个骨架,建议使用分支过滤器以提高复用性。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GMS")
|
||||
EGMS_LayeredBoneBlendMode BlendMode{EGMS_LayeredBoneBlendMode::BlendMask};
|
||||
|
||||
/**
|
||||
* Blend mask name for the overlay.
|
||||
* 叠加的混合遮罩名称。
|
||||
* @attention Must be defined in the skeleton first. 必须先在骨架中定义。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GMS", meta=(EditCondition="BlendMode == EGMS_LayeredBoneBlendMode::BlendMask", EditConditionHides))
|
||||
FName BlendMaskName;
|
||||
|
||||
/**
|
||||
* Branch filters for the overlay.
|
||||
* 叠加的分支过滤器。
|
||||
* @attention Refer to documentation for usage details. 请参阅文档了解使用详情。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GMS", meta=(EditCondition="BlendMode == EGMS_LayeredBoneBlendMode::BranchFilter", EditConditionHides))
|
||||
FGMS_InputBlendPose BranchFilters;
|
||||
|
||||
/**
|
||||
* Speed to blend to the specified blend weight.
|
||||
* 混合到指定混合权重的速度。
|
||||
* @attention Zero means instant blending. 为零表示立即混合。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GMS", meta=(ClampMin=0))
|
||||
float BlendTime = 0.2f;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GMS", meta=(ClampMin=0))
|
||||
FName BlendProfile = NAME_None;
|
||||
|
||||
friend bool operator==(const FGMS_SequenceStackEntry& Lhs, const FGMS_SequenceStackEntry& RHS)
|
||||
{
|
||||
return Lhs.Sequence == RHS.Sequence
|
||||
&& Lhs.BlendMode == RHS.BlendMode
|
||||
&& Lhs.MeshSpaceBlend == RHS.MeshSpaceBlend;
|
||||
}
|
||||
|
||||
friend bool operator!=(const FGMS_SequenceStackEntry& Lhs, const FGMS_SequenceStackEntry& RHS)
|
||||
{
|
||||
return !(Lhs == RHS);
|
||||
}
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
/**
|
||||
* Friendly message for displaying in the editor.
|
||||
* 在编辑器中显示的友好消息。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, Category = "GMS", Meta = (EditCondition = False, EditConditionHides))
|
||||
FString EditorFriendlyName;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Pose overlay setting for specific overlay mode.
|
||||
* 针对特定动画叠加模式的姿势叠加设置。
|
||||
*/
|
||||
USTRUCT()
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_OverlayModeSetting_SequenceStack
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Unique tag for this overlay mode.
|
||||
* 此叠加模式的唯一标签。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(Categories="GMS.OverlayMode"))
|
||||
FGameplayTag Tag;
|
||||
|
||||
/**
|
||||
* Potential dynamic sequences.
|
||||
* 潜在的动态sequences。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(TitleProperty="EditorFriendlyName"))
|
||||
TArray<FGMS_SequenceStackEntry> Sequences;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Anim layer setting for SequenceStack overlay system.
|
||||
* 针对序列栈的动画叠加系统设置。
|
||||
* @details Dynamically stacking different anim sequence. 可动态叠加不同的动画序列。
|
||||
*/
|
||||
UCLASS(NotBlueprintable)
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_AnimLayerSetting_Overlay_SequenceStack : public UGMS_AnimLayerSetting_Overlay
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(EditCondition=false, EditConditionHides))
|
||||
TMap<FGameplayTag, FGMS_OverlayModeSetting_SequenceStack> AcceleratedOverlayModes;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* List of sequence overlay settings.
|
||||
* 序列叠加设置列表。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(TitleProperty="Tag"))
|
||||
TArray<FGMS_OverlayModeSetting_SequenceStack> OverlayModes;
|
||||
|
||||
#if WITH_EDITOR
|
||||
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, CallInEditor, Category = "GMS")
|
||||
void ConvertToSequenceStack(const UGMS_AnimLayerSetting_Overlay_ParallelSequenceStack* Src);
|
||||
|
||||
UFUNCTION(BlueprintCallable, CallInEditor, Category = "GMS")
|
||||
static void ConvertToSequenceStackFromDefinition(UGMS_MovementDefinition* InDefinition);
|
||||
|
||||
/**
|
||||
* Called before saving the object.
|
||||
* 在保存对象之前调用。
|
||||
* @param SaveContext The save context. 保存上下文。
|
||||
*/
|
||||
virtual void PreSave(FObjectPreSaveContext SaveContext) override;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_AnimLayer_Overlay_SequenceStack : public UGMS_AnimLayer
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Applies the specified animation layer setting.
|
||||
* 应用指定的动画层设置。
|
||||
* @param Setting The animation layer setting to apply. 要应用的动画层设置。
|
||||
*/
|
||||
virtual void ApplySetting_Implementation(const UGMS_AnimLayerSetting* Setting) override;
|
||||
|
||||
/**
|
||||
* Resets the animation layer setting.
|
||||
* 重置动画层设置。
|
||||
*/
|
||||
virtual void ResetSetting_Implementation() override;
|
||||
bool SelectSequence();
|
||||
|
||||
/**
|
||||
* Updates the animation in a thread-safe manner.
|
||||
* 以线程安全的方式更新动画。
|
||||
* @param DeltaSeconds Time since the last update. 自上次更新以来的时间。
|
||||
*/
|
||||
virtual void NativeThreadSafeUpdateAnimation(float DeltaSeconds) override;
|
||||
|
||||
const FGMS_OverlayModeSetting_SequenceStack& GetOverlayModeSetting() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Current Selected Pose.
|
||||
* 当前选择的Pose.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Settings")
|
||||
FGMS_SequenceStackEntry Definition;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Settings")
|
||||
bool bHasValidDefinition = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Settings")
|
||||
float BlendWeight{0.0f};
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Settings")
|
||||
float BlendOutSpeed{0.0f};
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Settings")
|
||||
TObjectPtr<UBlendProfile> BlendProfile{nullptr};
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Settings")
|
||||
bool bHasValidSetting = false;
|
||||
|
||||
/**
|
||||
* Reference to the pose-based overlay settings.
|
||||
* 基于姿势的叠加设置的引用。
|
||||
*/
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<const UGMS_AnimLayerSetting_Overlay_SequenceStack> CurrentSetting;
|
||||
|
||||
UPROPERTY(Transient)
|
||||
FGameplayTag CurrentOverlayMode;
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GMS_AnimLayer.h"
|
||||
#include "GMS_AnimLayer_SkeletalControls.generated.h"
|
||||
|
||||
/**
|
||||
* Base class for skeletal control animation layer settings.
|
||||
* 骨骼控制动画层设置的基类。
|
||||
*/
|
||||
UCLASS(Abstract, Blueprintable)
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_AnimLayerSetting_SkeletalControls : public UGMS_AnimLayerSetting
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GMS_AnimLayer.h"
|
||||
#include "UObject/Object.h"
|
||||
#include "Runtime/Launch/Resources/Version.h"
|
||||
#if ENGINE_MINOR_VERSION < 5
|
||||
#include "InstancedStruct.h"
|
||||
#else
|
||||
#include "StructUtils/InstancedStruct.h"
|
||||
#endif
|
||||
#include "Settings/GMS_SettingObjectLibrary.h"
|
||||
#include "GMS_AnimLayer_States.generated.h"
|
||||
|
||||
/**
|
||||
* Animation data for state-based animation layers.
|
||||
* 状态动画层的数据。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimData
|
||||
{
|
||||
GENERATED_BODY()
|
||||
virtual ~FGMS_AnimData() = default;
|
||||
|
||||
/**
|
||||
* Validates the animation data.
|
||||
* 验证动画数据。
|
||||
*/
|
||||
virtual void Validate();
|
||||
|
||||
/**
|
||||
* Indicates if the animation data is valid.
|
||||
* 指示动画数据是否有效。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="GMS", meta=(EditCondition=false, EditConditionHides))
|
||||
bool bValid{false};
|
||||
};
|
||||
|
||||
/**
|
||||
* Base class for state-based animation layer settings.
|
||||
* 状态动画层设置的基类。
|
||||
* @details Inherit this class to create custom state-based animation layer settings. 继承此类以创建自定义状态动画层设置。
|
||||
*/
|
||||
UCLASS(Abstract, Blueprintable)
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_AnimLayerSetting_States : public UGMS_AnimLayerSetting
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GMS_AnimLayer.h"
|
||||
#include "UObject/Object.h"
|
||||
#include "GMS_AnimLayer_View.generated.h"
|
||||
|
||||
/**
|
||||
* Base class for view animation layer settings.
|
||||
* 视图动画层设置的基类。
|
||||
*/
|
||||
UCLASS(Abstract, Blueprintable)
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_AnimLayerSetting_View : public UGMS_AnimLayerSetting
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
@@ -0,0 +1,109 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GMS_AnimLayer.h"
|
||||
#include "GMS_AnimLayer_View.h"
|
||||
#include "GMS_AnimLayer_View_Default.generated.h"
|
||||
|
||||
class UAimOffsetBlendSpace;
|
||||
|
||||
/**
|
||||
* Native implementation of default view animation layer settings.
|
||||
* 默认视图动画层设置的原生实现。
|
||||
*/
|
||||
UCLASS(NotBlueprintable)
|
||||
class UGMS_AnimLayerSetting_View_Default final : public UGMS_AnimLayerSetting_View
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Blend space for the view animation.
|
||||
* 视图动画的混合空间。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Anim View")
|
||||
TObjectPtr<UBlendSpace> BlendSpace;
|
||||
|
||||
/**
|
||||
* Yaw angle offset for the view.
|
||||
* 视图的偏航角偏移。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Anim View")
|
||||
float YawAngleOffset = 0.0f;
|
||||
|
||||
/**
|
||||
* Additional smoothing speed for aim offset.
|
||||
* 瞄准偏移的额外平滑速度。
|
||||
* @attention Zero means no additional smoothing. 为零表示无额外平滑。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Anim View", meta=(ClampMin=0))
|
||||
float SmoothInterpSpeed = 5.0f;
|
||||
|
||||
/**
|
||||
* Yaw angle limits for the view.
|
||||
* 视图的偏航角限制。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Anim View")
|
||||
FVector2D YawAngleLimit{-90.0f, 90.0f};
|
||||
};
|
||||
|
||||
/**
|
||||
* Native implementation of default view animation layer.
|
||||
* 默认视图动画层的原生实现。
|
||||
*/
|
||||
UCLASS(Abstract)
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_AnimLayer_View_Default : public UGMS_AnimLayer
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Applies the specified animation layer setting.
|
||||
* 应用指定的动画层设置。
|
||||
* @param Setting The animation layer setting to apply. 要应用的动画层设置。
|
||||
*/
|
||||
virtual void ApplySetting_Implementation(const UGMS_AnimLayerSetting* Setting) override;
|
||||
|
||||
/**
|
||||
* Resets the animation layer setting.
|
||||
* 重置动画层设置。
|
||||
*/
|
||||
virtual void ResetSetting_Implementation() override;
|
||||
|
||||
/**
|
||||
* Blend space for the view animation.
|
||||
* 视图动画的混合空间。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Anim View")
|
||||
TObjectPtr<UBlendSpace> BlendSpace;
|
||||
|
||||
/**
|
||||
* Yaw angle offset for the view.
|
||||
* 视图的偏航角偏移。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Anim View")
|
||||
float YawAngleOffset = 0.0f;
|
||||
|
||||
/**
|
||||
* Yaw angle limits for the view.
|
||||
* 视图的偏航角限制。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Anim View")
|
||||
FVector2D YawAngleLimit{ -90.0f, 90.0f };
|
||||
|
||||
/**
|
||||
* Smoothing speed for the view animation.
|
||||
* 视图动画的平滑速度。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Anim View", meta=(ClampMin=0))
|
||||
float SmoothInterpSpeed = 0.0f;
|
||||
|
||||
/**
|
||||
* Indicates if the blend space is valid.
|
||||
* 指示混合空间是否有效。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Anim View")
|
||||
bool bValidBlendSpace;
|
||||
};
|
||||
@@ -0,0 +1,976 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GMS_LocomotionEnumLibrary.h"
|
||||
#include "Animation/TrajectoryTypes.h"
|
||||
#include "BoneControllers/AnimNode_OffsetRootBone.h"
|
||||
#include "UObject/Object.h"
|
||||
#include "GMS_AnimState.generated.h"
|
||||
|
||||
class UAnimSequenceBase;
|
||||
class UAnimSequence;
|
||||
|
||||
/**
|
||||
* Stores locomotion-related animation state data.
|
||||
* 存储与运动相关的动画状态数据。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimState_Locomotion
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* World-space location of the character.
|
||||
* 角色的世界空间位置。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
FVector Location{ForceInit};
|
||||
|
||||
/**
|
||||
* Displacement from the previous frame.
|
||||
* 上一帧的位移。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
float PreviousDisplacement{0.0f};
|
||||
|
||||
/**
|
||||
* Speed of displacement (cm/s).
|
||||
* 位移速度(厘米/秒)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
float DisplacementSpeed{0.0f};
|
||||
|
||||
/**
|
||||
* World-space rotation of the character.
|
||||
* 角色的世界空间旋转。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
FRotator Rotation{ForceInit};
|
||||
|
||||
/**
|
||||
* Quaternion representation of the rotation.
|
||||
* 旋转的四元数表示。
|
||||
*/
|
||||
UPROPERTY()
|
||||
FQuat RotationQuaternion{ForceInit};
|
||||
|
||||
/**
|
||||
* Current velocity vector.
|
||||
* 当前速度向量。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
FVector Velocity{ForceInit};
|
||||
|
||||
/**
|
||||
* 2D local-space velocity vector.
|
||||
* 2D本地空间速度向量。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
FVector LocalVelocity2D{ForceInit};
|
||||
|
||||
/**
|
||||
* Indicates if the character has velocity.
|
||||
* 指示角色是否有速度。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
bool bHasVelocity = false;
|
||||
|
||||
/**
|
||||
* Current speed of the character (cm/s).
|
||||
* 角色的当前速度(厘米/秒)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ForceUnits = "cm/s"))
|
||||
float Speed{0.0f};
|
||||
|
||||
/**
|
||||
* Yaw angle of the local velocity (degrees).
|
||||
* 本地速度的偏航角(度)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = -180, ClampMax = 180, ForceUnits = "deg"))
|
||||
float LocalVelocityYawAngle{0.0f};
|
||||
|
||||
/**
|
||||
* Yaw angle of the local velocity with offset (degrees).
|
||||
* 带偏移的本地速度偏航角(度)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = -180, ClampMax = 180, ForceUnits = "deg"))
|
||||
float LocalVelocityYawAngleWithOffset{0.0f};
|
||||
|
||||
/**
|
||||
* Cardinal direction of the local velocity.
|
||||
* 本地速度的主要方向。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
EGMS_MovementDirection LocalVelocityDirection{EGMS_MovementDirection::Forward};
|
||||
|
||||
/**
|
||||
* Cardinal direction of the local velocity without offset.
|
||||
* 无偏移的本地速度主要方向。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
EGMS_MovementDirection LocalVelocityDirectionNoOffset{EGMS_MovementDirection::Forward};
|
||||
|
||||
/**
|
||||
* Octagonal direction of the local velocity.
|
||||
* 本地速度的八方向。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
EGMS_MovementDirection_8Way LocalVelocityOctagonalDirection{EGMS_MovementDirection_8Way::Forward};
|
||||
|
||||
/**
|
||||
* Indicates if there is active input.
|
||||
* 指示是否有活跃输入。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
bool bHasInput{false};
|
||||
|
||||
/**
|
||||
* Velocity acceleration vector.
|
||||
* 速度加速度向量。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
FVector VelocityAcceleration{ForceInit};
|
||||
|
||||
/**
|
||||
* 2D local-space acceleration vector.
|
||||
* 2D本地空间加速度向量。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
FVector LocalAcceleration2D{ForceInit};
|
||||
|
||||
/**
|
||||
* Indicates if the character is moving.
|
||||
* 指示角色是否在移动。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
bool bMoving{false};
|
||||
|
||||
/**
|
||||
* Actor's rotation yaw speed.
|
||||
* Actor的旋转偏航角变化速度。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = -180, ClampMax = 180, ForceUnits = "deg"))
|
||||
float YawVelocity{0.0f};
|
||||
|
||||
/**
|
||||
* Scale factor for animations.
|
||||
* 动画的缩放因子。
|
||||
*/
|
||||
float Scale{1.0f};
|
||||
|
||||
/**
|
||||
* Radius of the character's capsule.
|
||||
* 角色胶囊体的半径。
|
||||
*/
|
||||
float CapsuleRadius{0.0f};
|
||||
|
||||
/**
|
||||
* Half-height of the character's capsule.
|
||||
* 角色胶囊体的一半高度。
|
||||
*/
|
||||
float CapsuleHalfHeight{0.0f};
|
||||
|
||||
/**
|
||||
* Maximum acceleration of the character.
|
||||
* 角色的最大加速度。
|
||||
*/
|
||||
float MaxAcceleration{0.0f};
|
||||
|
||||
/**
|
||||
* Maximum braking deceleration of the character.
|
||||
* 角色的最大制动减速度。
|
||||
*/
|
||||
float MaxBrakingDeceleration{0.0f};
|
||||
|
||||
/**
|
||||
* Z value for walkable floor detection.
|
||||
* 可行走地板检测的Z值。
|
||||
*/
|
||||
float WalkableFloorZ{0.0f};
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimState_Trajectory
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
FTransformTrajectory Trajectory;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
FVector PastVelocity{FVector::ZeroVector};
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
FVector CurrentVelocity{FVector::ZeroVector};
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
FVector FutureVelocity{FVector::ZeroVector};
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
float DesiredControllerYaw{0.0f};
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores root bone animation state data.
|
||||
* 存储根骨骼动画状态数据。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimState_Root
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Translation mode for the root bone.
|
||||
* 根骨骼的平移模式。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
EOffsetRootBoneMode TranslationMode{EOffsetRootBoneMode::Release};
|
||||
|
||||
/**
|
||||
* Rotation mode for the root bone.
|
||||
* 根骨骼的旋转模式。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
EOffsetRootBoneMode RotationMode{EOffsetRootBoneMode::Release};
|
||||
|
||||
/**
|
||||
* Current world-space transform of the root bone.
|
||||
* 根骨骼的当前世界空间变换。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
FTransform RootTransform{FTransform::Identity};
|
||||
|
||||
/**
|
||||
* Yaw offset relative to the actor's rotation (degrees; <0 left, >0 right).
|
||||
* 相对于Actor旋转的偏航偏移(度;<0左侧,>0右侧)。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="State")
|
||||
float YawOffset{0};
|
||||
|
||||
/**
|
||||
* Maximum rotation error in degrees; values <0 disable the limit.
|
||||
* 最大旋转误差(度);值<0禁用限制。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
float MaxRotationError{-1.0f};
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores turn-in-place animation state data.
|
||||
* 存储原地转身动画状态数据。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimState_TurnInPlace
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Indicates if the state was updated this frame.
|
||||
* 指示此帧是否更新了状态。
|
||||
*/
|
||||
UPROPERTY()
|
||||
uint8 bUpdatedThisFrame : 1 {false};
|
||||
|
||||
/**
|
||||
* Animation sequence for turn-in-place.
|
||||
* 原地转身的动画序列。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UAnimSequenceBase> Animation;
|
||||
|
||||
/**
|
||||
* Indicates if the character should turn.
|
||||
* 指示角色是否应该转身。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
uint8 bShouldTurn : 1 {false};
|
||||
|
||||
/**
|
||||
* Accumulated time for the animation.
|
||||
* 动画的累计时间。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
float AccumulatedTime{0};
|
||||
|
||||
/**
|
||||
* Playback rate for the animation.
|
||||
* 动画的播放速率。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ForceUnits = "x"))
|
||||
float PlayRate{1.0f};
|
||||
|
||||
/**
|
||||
* Scaled playback rate for the animation.
|
||||
* 动画的缩放播放速率。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ForceUnits = "x"))
|
||||
float ScaledPlayRate{1.0f};
|
||||
|
||||
/**
|
||||
* Delay before activating the turn-in-place animation.
|
||||
* 原地转身动画激活前的延迟。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ForceUnits = "s"))
|
||||
float ActivationDelay{0.0f};
|
||||
|
||||
/**
|
||||
* Angle that triggers the turn-in-place animation.
|
||||
* 触发原地转身动画的角度。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
float TriggeredAngle{0.0f};
|
||||
|
||||
/**
|
||||
* Indicates if the turn is 180 degrees.
|
||||
* 指示是否为180度转身。
|
||||
*/
|
||||
bool b180{false};
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores in-air animation state data.
|
||||
* 存储空中动画状态数据。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimState_InAir
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Vertical speed of the character (cm/s).
|
||||
* 角色的垂直速度(厘米/秒)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ForceUnits = "cm/s"))
|
||||
float VerticalSpeed{0.0f};
|
||||
|
||||
/**
|
||||
* Indicates if the character is jumping.
|
||||
* 指示角色是否在跳跃。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
bool bJumping{false};
|
||||
|
||||
/**
|
||||
* Indicates if the character is falling.
|
||||
* 指示角色是否在下落。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
bool bFalling{false};
|
||||
|
||||
/**
|
||||
* Playback rate for the jump animation.
|
||||
* 跳跃动画的播放速率。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ForceUnits = "x"))
|
||||
float JumpPlayRate{1.0f};
|
||||
|
||||
/**
|
||||
* Indicates if valid ground is detected.
|
||||
* 指示是否检测到有效地面。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
bool bValidGround{false};
|
||||
|
||||
/**
|
||||
* Distance to the ground (cm).
|
||||
* 到地面的距离(厘米)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", meta=(ForceUnits = "cm"))
|
||||
float GroundDistance{0.0f};
|
||||
|
||||
/**
|
||||
* Time to reach the jump apex (s).
|
||||
* 到达跳跃顶点的时间(秒)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ClampMax = 1, ForceUnits = "s"))
|
||||
float TimeToJumpApex{0.0f};
|
||||
|
||||
/**
|
||||
* Time spent falling (s).
|
||||
* 下落的时间(秒)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ClampMax = 1, ForceUnits = "s"))
|
||||
float FallingTime{0.0f};
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores idle animation state data.
|
||||
* 存储空闲动画状态数据。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimState_Idle
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Animation sequence for idle.
|
||||
* 空闲动画序列。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> Animation{nullptr};
|
||||
|
||||
/**
|
||||
* Indicates if the idle animation is looped.
|
||||
* 指示空闲动画是否循环。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
bool bLoop{true};
|
||||
|
||||
/**
|
||||
* Playback rate for the idle animation.
|
||||
* 空闲动画的播放速率。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
float PlayRate{1.0f};
|
||||
|
||||
/**
|
||||
* Blend time for the idle animation.
|
||||
* 空闲动画的混合时间。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
float BlendTime{1.0f};
|
||||
|
||||
/**
|
||||
* Blend profile for the idle animation.
|
||||
* 空闲动画的混合配置文件。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UBlendProfile> BlendProfile{nullptr};
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores runtime state for idle break animations.
|
||||
* 存储空闲中断动画的运行时状态。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimState_IdleBreak
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Time until the next idle break animation (s).
|
||||
* 到下一个空闲中断动画的时间(秒)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
float TimeUntilNextIdleBreak{0.0f};
|
||||
|
||||
/**
|
||||
* Index of the current idle break animation.
|
||||
* 当前空闲中断动画的索引。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
int32 CurrentIdleBreakIndex{0};
|
||||
|
||||
/**
|
||||
* Delay between idle break animations (s).
|
||||
* 空闲中断动画之间的延迟(秒)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
float IdleBreakDelayTime{0.0f};
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores lean animation state data.
|
||||
* 存储倾斜动画状态数据。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimState_Lean
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Horizontal acceleration amount for leaning (clamped -1 to 1).
|
||||
* 用于倾斜的水平加速度量(限制在-1到1)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = -1, ClampMax = 1))
|
||||
float RightAmount{0.0f};
|
||||
|
||||
/**
|
||||
* Vertical acceleration amount for leaning (clamped -1 to 1).
|
||||
* 用于倾斜的垂直加速度量(限制在-1到1)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = -1, ClampMax = 1))
|
||||
float ForwardAmount{0.0f};
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores pivot animation state data.
|
||||
* 存储枢轴动画状态数据。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimState_Pivot
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Animation sequence for the pivot.
|
||||
* 枢轴动画序列。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> Animation;
|
||||
|
||||
/**
|
||||
* Acceleration at the start of the pivot.
|
||||
* 枢轴开始时的加速度。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
FVector StartingAcceleration{ForceInit};
|
||||
|
||||
/**
|
||||
* Accumulated time for the pivot animation.
|
||||
* 枢轴动画的累计时间。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
float AccumulatedTime{ForceInit};
|
||||
|
||||
/**
|
||||
* Time at which the pivot stops for distance matching.
|
||||
* 距离匹配时枢轴停止的时间。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
float TimeAtPivotStop{ForceInit};
|
||||
|
||||
/**
|
||||
* Alpha for stride warping during the pivot.
|
||||
* 枢轴期间步幅适配的Alpha值。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
float StrideWarpingAlpha{ForceInit};
|
||||
|
||||
/**
|
||||
* Clamp for the playback rate (min, max).
|
||||
* 播放速率的限制(最小,最大)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
FVector2D PlayRateClamp{0.f, 0.f};
|
||||
|
||||
/**
|
||||
* 2D direction of the pivot.
|
||||
* 枢轴的2D方向。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="State")
|
||||
FVector Direction2D{ForceInit};
|
||||
|
||||
/**
|
||||
* Desired movement direction for the pivot.
|
||||
* 枢轴的期望移动方向。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="State")
|
||||
EGMS_MovementDirection DesiredDirection{ForceInit};
|
||||
|
||||
/**
|
||||
* Initial movement direction for the pivot.
|
||||
* 枢轴的初始移动方向。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="State")
|
||||
EGMS_MovementDirection InitialDirection{ForceInit};
|
||||
|
||||
/**
|
||||
* Remaining cooldown time for the pivot (s).
|
||||
* 枢轴的剩余冷却时间(秒)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="State")
|
||||
float RemainingCooldown{ForceInit};
|
||||
|
||||
/**
|
||||
* Indicates if the pivot is moving perpendicular to the initial direction.
|
||||
* 指示枢轴是否垂直于初始方向移动。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="State")
|
||||
bool bMovingPerpendicularToInitialDirection{ForceInit};
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores start animation state data.
|
||||
* 存储开始动画状态数据。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimState_Start
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Animation sequence for the start movement.
|
||||
* 开始移动的动画序列。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> Animation{nullptr};
|
||||
|
||||
/**
|
||||
* Smooth target rotation for the start movement.
|
||||
* 开始移动的平滑目标旋转。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
FRotator SmoothTargetRotation{ForceInit};
|
||||
|
||||
/**
|
||||
* Yaw delta between acceleration direction and root direction at start (degrees).
|
||||
* 开始时加速度方向与根方向的偏航差(度)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = -180, ClampMax = 180, ForceUnits = "deg"))
|
||||
float YawDeltaToAcceleration{ForceInit};
|
||||
|
||||
/**
|
||||
* Local velocity direction at the start.
|
||||
* 开始时的本地速度方向。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
EGMS_MovementDirection LocalVelocityDirection{ForceInit};
|
||||
|
||||
/**
|
||||
* Alpha for stride warping during the start.
|
||||
* 开始期间步幅适配的Alpha值。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
float StrideWarpingAlpha{ForceInit};
|
||||
|
||||
/**
|
||||
* Clamp for the playback rate (min, max).
|
||||
* 播放速率的限制(最小,最大)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
FVector2D PlayRateClamp{ForceInit};
|
||||
|
||||
/**
|
||||
* Alpha for orientation warping during the start.
|
||||
* 开始期间朝向扭曲的Alpha值。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
float OrientationAlpha{1.0f};
|
||||
|
||||
/**
|
||||
* Time spent in the start state (s).
|
||||
* 开始状态的持续时间(秒)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
float TimeInState{0.0f};
|
||||
|
||||
/**
|
||||
* Blend profile for the start animation.
|
||||
* 开始动画的混合配置文件。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UBlendProfile> BlendProfile{nullptr};
|
||||
|
||||
/**
|
||||
* Remaining time for the start animation (s).
|
||||
* 开始动画的剩余时间(秒)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
float TimeRemaining{0.0f};
|
||||
|
||||
/**
|
||||
* Playback rate for the start animation.
|
||||
* 开始动画的播放速率。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
float PlayRate{0.0f};
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores cycle animation state data.
|
||||
* 存储循环动画状态数据。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimState_Cycle
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Animation sequence for the cycle.
|
||||
* 循环动画序列。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> Animation{nullptr};
|
||||
|
||||
/**
|
||||
* Alpha for orientation warping during the cycle.
|
||||
* 循环期间朝向扭曲的Alpha值。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
float OrientationAlpha{ForceInit};
|
||||
|
||||
/**
|
||||
* Alpha for stride warping during the cycle.
|
||||
* 循环期间步幅适配的Alpha值。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
float StrideWarpingAlpha{ForceInit};
|
||||
|
||||
/**
|
||||
* Playback rate for the cycle animation.
|
||||
* 循环动画的播放速率。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
float PlayRate{1.0f};
|
||||
|
||||
/**
|
||||
* Blend profile for the cycle animation.
|
||||
* 循环动画的混合配置文件。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UBlendProfile> BlendProfile{nullptr};
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores stop animation state data.
|
||||
* 存储停止动画状态数据。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimState_Stop
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Animation sequence for the stop.
|
||||
* 停止动画序列。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> Animation{nullptr};
|
||||
|
||||
/**
|
||||
* Alpha for orientation warping during the stop.
|
||||
* 停止期间朝向扭曲的Alpha值。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
float OrientationAlpha{1.0f};
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores view-related animation state data.
|
||||
* 存储与视图相关的动画状态数据。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimState_View
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Rotation of the view.
|
||||
* 视图的旋转。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
FRotator Rotation{ForceInit};
|
||||
|
||||
/**
|
||||
* Yaw angle between the actor and camera (degrees).
|
||||
* Actor与相机之间的偏航角(度)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = -180, ClampMax = 180, ForceUnits = "deg"))
|
||||
float YawAngle{0.0f};
|
||||
|
||||
/**
|
||||
* Speed of yaw angle change (degrees/s).
|
||||
* 偏航角变化速度(度/秒)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ForceUnits = "deg/s"))
|
||||
float YawSpeed{0.0f};
|
||||
|
||||
/**
|
||||
* Pitch angle of the view (degrees).
|
||||
* 视图的俯仰角(度)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = -90, ClampMax = 90, ForceUnits = "deg"))
|
||||
float PitchAngle{0.0f};
|
||||
|
||||
/**
|
||||
* Amount of pitch applied (clamped 0 to 1).
|
||||
* 应用的俯仰量(限制在0到1)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ClampMax = 1))
|
||||
float PitchAmount{0.5f};
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores layering animation state data for body parts.
|
||||
* 存储身体部位的分层动画状态数据。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimState_Layering
|
||||
{
|
||||
GENERATED_BODY()
|
||||
virtual ~FGMS_AnimState_Layering() = default;
|
||||
/**
|
||||
* Blend amount for the head (0 to 1).
|
||||
* 头部混合量(0到1)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ClampMax = 1))
|
||||
float HeadBlendAmount{0.0f};
|
||||
|
||||
/**
|
||||
* Additive blend amount for the head (0 to 1).
|
||||
* 头部附加混合量(0到1)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ClampMax = 1))
|
||||
float HeadAdditiveBlendAmount{0.0f};
|
||||
|
||||
/**
|
||||
* Slot blend amount for the head (0 to 1).
|
||||
* 头部槽混合量(0到1)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ClampMax = 1))
|
||||
float HeadSlotBlendAmount{1.0f};
|
||||
|
||||
/**
|
||||
* Blend amount for the left arm (0 to 1).
|
||||
* 左臂混合量(0到1)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ClampMax = 1))
|
||||
float ArmLeftBlendAmount{0.0f};
|
||||
|
||||
/**
|
||||
* Additive blend amount for the left arm (0 to 1).
|
||||
* 左臂附加混合量(0到1)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ClampMax = 1))
|
||||
float ArmLeftAdditiveBlendAmount{0.0f};
|
||||
|
||||
/**
|
||||
* Slot blend amount for the left arm (0 to 1).
|
||||
* 左臂槽混合量(0到1)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ClampMax = 1))
|
||||
float ArmLeftSlotBlendAmount{1.0f};
|
||||
|
||||
/**
|
||||
* Local space blend amount for the left arm (0 to 1).
|
||||
* 左臂本地空间混合量(0到1)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ClampMax = 1))
|
||||
float ArmLeftLocalSpaceBlendAmount{0.0f};
|
||||
|
||||
/**
|
||||
* Mesh space blend amount for the left arm (0 to 1).
|
||||
* 左臂网格空间混合量(0到1)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ClampMax = 1))
|
||||
float ArmLeftMeshSpaceBlendAmount{0.0f};
|
||||
|
||||
/**
|
||||
* Blend amount for the right arm (0 to 1).
|
||||
* 右臂混合量(0到1)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ClampMax = 1))
|
||||
float ArmRightBlendAmount{0.0f};
|
||||
|
||||
/**
|
||||
* Additive blend amount for the right arm (0 to 1).
|
||||
* 右臂附加混合量(0到1)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ClampMax = 1))
|
||||
float ArmRightAdditiveBlendAmount{0.0f};
|
||||
|
||||
/**
|
||||
* Slot blend amount for the right arm (0 to 1).
|
||||
* 右臂槽混合量(0到1)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ClampMax = 1))
|
||||
float ArmRightSlotBlendAmount{1.0f};
|
||||
|
||||
/**
|
||||
* Local space blend amount for the right arm (0 to 1).
|
||||
* 右臂本地空间混合量(0到1)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ClampMax = 1))
|
||||
float ArmRightLocalSpaceBlendAmount{0.0f};
|
||||
|
||||
/**
|
||||
* Mesh space blend amount for the right arm (0 to 1).
|
||||
* 右臂网格空间混合量(0到1)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ClampMax = 1))
|
||||
float ArmRightMeshSpaceBlendAmount{0.0f};
|
||||
|
||||
/**
|
||||
* Blend amount for the left hand (0 to 1).
|
||||
* 左手混合量(0到1)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ClampMax = 1))
|
||||
float HandLeftBlendAmount{0.0f};
|
||||
|
||||
/**
|
||||
* Blend amount for the right hand (0 to 1).
|
||||
* 右手混合量(0到1)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ClampMax = 1))
|
||||
float HandRightBlendAmount{0.0f};
|
||||
|
||||
/**
|
||||
* Blend amount for the spine (0 to 1).
|
||||
* 脊椎混合量(0到1)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ClampMax = 1))
|
||||
float SpineBlendAmount{0.0f};
|
||||
|
||||
/**
|
||||
* Additive blend amount for the spine (0 to 1).
|
||||
* 脊椎附加混合量(0到1)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ClampMax = 1))
|
||||
float SpineAdditiveBlendAmount{0.0f};
|
||||
|
||||
/**
|
||||
* Slot blend amount for the spine (0 to 1).
|
||||
* 脊椎槽混合量(0到1)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ClampMax = 1))
|
||||
float SpineSlotBlendAmount{1.0f};
|
||||
|
||||
/**
|
||||
* Blend amount for the pelvis (0 to 1).
|
||||
* 骨盆混合量(0到1)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ClampMax = 1))
|
||||
float PelvisBlendAmount{0.0f};
|
||||
|
||||
/**
|
||||
* Slot blend amount for the pelvis (0 to 1).
|
||||
* 骨盆槽混合量(0到1)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ClampMax = 1))
|
||||
float PelvisSlotBlendAmount{1.0f};
|
||||
|
||||
/**
|
||||
* Blend amount for the legs (0 to 1).
|
||||
* 腿部混合量(0到1)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ClampMax = 1))
|
||||
float LegsBlendAmount{0.0f};
|
||||
|
||||
/**
|
||||
* Slot blend amount for the legs (0 to 1).
|
||||
* 腿部槽混合量(0到1)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ClampMax = 1))
|
||||
float LegsSlotBlendAmount{1.0f};
|
||||
|
||||
void ApplyValueFromSequence(const UAnimSequence* InSequence, float ExplicitTime);
|
||||
|
||||
virtual void ZeroOut();
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimState_LayeringSmooth
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ClampMax = 1))
|
||||
float HeadBlendAmount{0.0f};
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ClampMax = 1))
|
||||
float ArmLeftBlendAmount{0.0f};
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ClampMax = 1))
|
||||
float ArmRightBlendAmount{0.0f};
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ClampMax = 1))
|
||||
float SpineBlendAmount{0.0f};
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ClampMax = 1))
|
||||
float PelvisBlendAmount{0.0f};
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ClampMax = 1))
|
||||
float LegsBlendAmount{0.0f};
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/Object.h"
|
||||
#include "GMS_LocomotionEnumLibrary.generated.h"
|
||||
|
||||
/**
|
||||
* Defines four-directional movement directions.
|
||||
* 定义四方向移动方向。
|
||||
*/
|
||||
UENUM(BlueprintType)
|
||||
enum class EGMS_MovementDirection : uint8
|
||||
{
|
||||
Forward, // Forward movement. 前进移动。
|
||||
Backward, // Backward movement. 后退移动。
|
||||
Left, // Left movement. 左移。
|
||||
Right // Right movement. 右移。
|
||||
};
|
||||
|
||||
/**
|
||||
* Defines eight-directional movement directions.
|
||||
* 定义八方向移动方向。
|
||||
*/
|
||||
UENUM(BlueprintType)
|
||||
enum class EGMS_MovementDirection_8Way : uint8
|
||||
{
|
||||
Forward, // Forward movement. 前进移动。
|
||||
ForwardLeft, // Forward-left movement. 前左移动。
|
||||
ForwardRight, // Forward-right movement. 前右移动。
|
||||
Backward, // Backward movement. 后退移动。
|
||||
BackwardLeft, // Backward-left movement. 后左移动。
|
||||
BackwardRight, // Backward-right movement. 后右移动。
|
||||
Left, // Left movement. 左移。
|
||||
Right // Right movement. 右移。
|
||||
};
|
||||
@@ -0,0 +1,601 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "Animation/CachedAnimData.h"
|
||||
#include "UObject/Object.h"
|
||||
#include "GMS_LocomotionStructLibrary.generated.h"
|
||||
|
||||
class UBlendSpace1D;
|
||||
class UAnimSequenceBase;
|
||||
class UAnimSequence;
|
||||
|
||||
/**
|
||||
* Stores the locomotion state of a character.
|
||||
* 存储角色的运动状态。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_LocomotionState
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Indicates if there is active input.
|
||||
* 表示是否有活跃的输入。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
bool bHasInput{false};
|
||||
|
||||
/**
|
||||
* Input yaw angle in world space.
|
||||
* 世界空间中的输入偏航角。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = -180, ClampMax = 180, ForceUnits = "deg"))
|
||||
float InputYawAngle{0.0f};
|
||||
|
||||
/**
|
||||
* Indicates if the character has speed.
|
||||
* 表示角色是否有速度。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
bool bHasVelocity{false};
|
||||
|
||||
/**
|
||||
* Current speed of the character.
|
||||
* 角色的当前速度。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ForceUnits = "cm/s"))
|
||||
float Speed{0.0f};
|
||||
|
||||
/**
|
||||
* Current velocity vector.
|
||||
* 当前速度向量。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
FVector Velocity{ForceInit};
|
||||
|
||||
/**
|
||||
* Yaw angle of the character's velocity.
|
||||
* 角色速度的偏航角。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = -180, ClampMax = 180, ForceUnits = "deg"))
|
||||
float VelocityYawAngle{0.0f};
|
||||
|
||||
/**
|
||||
* Indicates if the character is moving.
|
||||
* 表示角色是否在移动。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
bool bMoving{false};
|
||||
|
||||
/**
|
||||
* Target yaw angle for the actor's rotation.
|
||||
* Actor旋转的目标偏航角。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = -180, ClampMax = 180, ForceUnits = "deg"))
|
||||
float TargetYawAngle{0.0f};
|
||||
|
||||
/**
|
||||
* Smoothed target yaw angle for extra smooth rotation.
|
||||
* 用于平滑旋转的平滑目标偏航角。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = -180, ClampMax = 180, ForceUnits = "deg"))
|
||||
float SmoothTargetYawAngle{0.0f};
|
||||
|
||||
/**
|
||||
* Angle between view yaw and target yaw.
|
||||
* 视角偏航角与目标偏航角之间的角度。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = -180, ClampMax = 180, ForceUnits = "deg"))
|
||||
float ViewRelativeTargetYawAngle{0.0f};
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
uint8 bAimingLimitAppliedThisFrame : 1 {false};
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
uint8 bResetAimingLimit : 1 {true};
|
||||
|
||||
/**
|
||||
* Limit for the aiming yaw angle.
|
||||
* 瞄准偏航角的限制。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = -180, ClampMax = 180, ForceUnits = "deg"))
|
||||
float AimingYawAngleLimit{180.0f};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Stores the view state of a character.
|
||||
* 存储角色的视图状态。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_ViewState
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Smoothed view rotation, set by replicated view rotation.
|
||||
* 平滑的视角旋转,由复制的视角旋转设置。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
FRotator Rotation{ForceInit};
|
||||
|
||||
/**
|
||||
* Speed of camera rotation from left to right.
|
||||
* 相机左右旋转的速度。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ForceUnits = "deg/s"))
|
||||
float YawSpeed{0.0f};
|
||||
|
||||
/**
|
||||
* View yaw angle from the previous frame.
|
||||
* 上一帧的视角偏航角。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = -180, ClampMax = 180, ForceUnits = "deg"))
|
||||
float PreviousYawAngle{0.0f};
|
||||
};
|
||||
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_MovementBaseState
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UPrimitiveComponent> Primitive;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
FName BoneName;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
uint8 bBaseChanged : 1 {false};
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
uint8 bHasRelativeLocation : 1 {false};
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
uint8 bHasRelativeRotation : 1 {false};
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
FVector Location{ForceInit};
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
FQuat Rotation{ForceInit};
|
||||
|
||||
/**
|
||||
* 基础对象(例如移动平台)从上一帧到当前帧的旋转变化。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
FRotator DeltaRotation{ForceInit};
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Parameters for predicting ground movement stop location.
|
||||
* 预测地面运动停止位置的参数。
|
||||
*/
|
||||
USTRUCT()
|
||||
struct FGMS_PredictGroundMovementStopLocationParams
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Current velocity vector.
|
||||
* 当前速度向量。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
FVector Velocity{ForceInit};
|
||||
|
||||
/**
|
||||
* Whether to use separate braking friction.
|
||||
* 是否使用单独的制动摩擦。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
bool bUseSeparateBrakingFriction{ForceInit};
|
||||
|
||||
/**
|
||||
* Braking friction value.
|
||||
* 制动摩擦值。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
float BrakingFriction{ForceInit};
|
||||
|
||||
/**
|
||||
* Ground friction value.
|
||||
* 地面摩擦值。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
float GroundFriction{ForceInit};
|
||||
|
||||
/**
|
||||
* Braking friction factor.
|
||||
* 制动摩擦因子。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
float BrakingFrictionFactor{ForceInit};
|
||||
|
||||
/**
|
||||
* Braking deceleration for walking.
|
||||
* 行走时的制动减速度。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
float BrakingDecelerationWalking{ForceInit};
|
||||
};
|
||||
|
||||
/**
|
||||
* Parameters for predicting ground movement pivot location.
|
||||
* 预测地面运动枢轴位置的参数。
|
||||
*/
|
||||
USTRUCT()
|
||||
struct FGMS_PredictGroundMovementPivotLocationParams
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Current acceleration vector.
|
||||
* 当前加速度向量。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
FVector Acceleration{ForceInit};
|
||||
|
||||
/**
|
||||
* Current velocity vector.
|
||||
* 当前速度向量。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
FVector Velocity{ForceInit};
|
||||
|
||||
/**
|
||||
* Ground friction value.
|
||||
* 地面摩擦值。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
float GroundFriction{0.0f};
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores animations for four-directional movement.
|
||||
* 存储四方向移动的动画。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FGMS_Animations_4Direction
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Animation for forward movement.
|
||||
* 前进移动的动画。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> Forward = nullptr;
|
||||
|
||||
/**
|
||||
* Animation for backward movement.
|
||||
* 后退移动的动画。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> Backward = nullptr;
|
||||
|
||||
/**
|
||||
* Animation for left movement.
|
||||
* 左移移动的动画。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> Left = nullptr;
|
||||
|
||||
/**
|
||||
* Animation for right movement.
|
||||
* 右移移动的动画。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> Right = nullptr;
|
||||
|
||||
/**
|
||||
* Checks if all animations are valid.
|
||||
* 检查所有动画是否有效。
|
||||
* @return True if all animations are set. 如果所有动画都设置返回true。
|
||||
*/
|
||||
bool ValidAnimations() const;
|
||||
|
||||
/**
|
||||
* Checks if any animation has root motion.
|
||||
* 检查是否有动画包含根运动。
|
||||
* @return True if any animation has root motion. 如果有动画包含根运动返回true。
|
||||
*/
|
||||
bool HasRootMotion() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores animations for eight-directional movement.
|
||||
* 存储八方向移动的动画。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FGMS_Animations_8Direction
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Animation for forward movement.
|
||||
* 前进移动的动画。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> Forward = nullptr;
|
||||
|
||||
/**
|
||||
* Animation for forward-left movement.
|
||||
* 前左移动的动画。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> ForwardLeft = nullptr;
|
||||
|
||||
/**
|
||||
* Animation for forward-right movement.
|
||||
* 前右移动的动画。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> ForwardRight = nullptr;
|
||||
|
||||
/**
|
||||
* Animation for backward movement.
|
||||
* 后退移动的动画。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> Backward = nullptr;
|
||||
|
||||
/**
|
||||
* Animation for backward-left movement.
|
||||
* 后左移动的动画。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> BackwardLeft = nullptr;
|
||||
|
||||
/**
|
||||
* Animation for backward-right movement.
|
||||
* 后右移动的动画。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> BackwardRight = nullptr;
|
||||
|
||||
/**
|
||||
* Animation for left movement.
|
||||
* 左移移动的动画。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> Left = nullptr;
|
||||
|
||||
/**
|
||||
* Animation for right movement.
|
||||
* 右移移动的动画。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> Right = nullptr;
|
||||
|
||||
/**
|
||||
* Checks if all animations are valid.
|
||||
* 检查所有动画是否有效。
|
||||
* @return True if all animations are set. 如果所有动画都设置返回true。
|
||||
*/
|
||||
bool ValidAnimations() const;
|
||||
|
||||
/**
|
||||
* Checks if any animation has root motion.
|
||||
* 检查是否有动画包含根运动。
|
||||
* @return True if any animation has root motion. 如果有动画包含根运动返回true。
|
||||
*/
|
||||
bool HasRootMotion() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores 1D blend space animations for forward and backward movement.
|
||||
* 存储用于前后移动的1D混合空间动画。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FGMS_Animations_BS1D_FwdBwd
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Blend space for forward movement.
|
||||
* 前进移动的混合空间。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
TObjectPtr<UBlendSpace1D> Forward{nullptr};
|
||||
|
||||
/**
|
||||
* Blend space for backward movement.
|
||||
* 后退移动的混合空间。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
TObjectPtr<UBlendSpace1D> Backward{nullptr};
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores animations for starting movement while facing forward.
|
||||
* 存储面向前进时开始移动的动画。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FGMS_Animations_StartForwardFacing
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Animation for starting movement forward.
|
||||
* 前进开始移动的动画。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> StartForward = nullptr;
|
||||
|
||||
/**
|
||||
* Animation for starting movement forward with a 90-degree left turn.
|
||||
* 前进并向左90度开始移动的动画。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> StartForwardL90 = nullptr;
|
||||
|
||||
/**
|
||||
* Animation for starting movement forward with a 90-degree right turn.
|
||||
* 前进并向右90度开始移动的动画。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> StartForwardR90 = nullptr;
|
||||
|
||||
/**
|
||||
* Animation for starting movement forward with a 180-degree left turn.
|
||||
* 前进并向左180度开始移动的动画。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> StartForwardL180 = nullptr;
|
||||
|
||||
/**
|
||||
* Animation for starting movement forward with a 180-degree right turn.
|
||||
* 前进并向右180度开始移动的动画。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> StartForwardR180 = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores animations for starting movement while facing forward in eight directions.
|
||||
* 存储面向前进时八方向开始移动的动画。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FGMS_Animations_StartForwardFacing_8Direction
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Animation for starting movement forward.
|
||||
* 前进开始移动的动画。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> StartForward = nullptr;
|
||||
|
||||
/**
|
||||
* Animation for starting movement forward with a 90-degree left turn.
|
||||
* 前进并向左90度开始移动的动画。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> StartForwardL90 = nullptr;
|
||||
|
||||
/**
|
||||
* Animation for starting movement forward with a 90-degree right turn.
|
||||
* 前进并向右90度开始移动的动画。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> StartForwardR90 = nullptr;
|
||||
|
||||
/**
|
||||
* Animation for starting movement forward with a 180-degree left turn.
|
||||
* 前进并向左180度开始移动的动画。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> StartForwardL180 = nullptr;
|
||||
|
||||
/**
|
||||
* Animation for starting movement forward with a 180-degree right turn.
|
||||
* 前进并向右180度开始移动的动画。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> StartForwardR180 = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores an animation with an associated distance.
|
||||
* 存储与距离关联的动画。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimationWithDistance
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* The animation sequence.
|
||||
* 动画序列。
|
||||
*/
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="GMS")
|
||||
TObjectPtr<UAnimSequence> Animation = nullptr;
|
||||
|
||||
/**
|
||||
* The distance associated with the animation.
|
||||
* 与动画关联的距离。
|
||||
*/
|
||||
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="GMS", meta=(ClampMin=0))
|
||||
float Distance{200};
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
/**
|
||||
* Editor-friendly name for the animation.
|
||||
* 动画的编辑器友好名称。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, Category=AlwaysHidden, Meta=(EditCondition=False, EditConditionHides))
|
||||
FString EditorFriendlyName;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* Maps animation state names to gameplay tags.
|
||||
* 将动画状态名称映射到游戏标签。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct FGMS_AnimStateNameToTag
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* The gameplay tag for the animation state.
|
||||
* 动画状态的游戏标签。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", meta=(Categories="GMS.SM"))
|
||||
FGameplayTag Tag;
|
||||
|
||||
/**
|
||||
* The cached animation state data.
|
||||
* 缓存的动画状态数据。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category= "GMS")
|
||||
FCachedAnimStateData State;
|
||||
|
||||
/**
|
||||
* Equality operator for comparing two animation state mappings.
|
||||
* 比较两个动画状态映射的相等运算符。
|
||||
* @param Lhs Left-hand side mapping. 左侧映射。
|
||||
* @param RHS Right-hand side mapping. 右侧映射。
|
||||
* @return True if mappings are equal. 如果映射相等返回true。
|
||||
*/
|
||||
friend bool operator==(const FGMS_AnimStateNameToTag& Lhs, const FGMS_AnimStateNameToTag& RHS)
|
||||
{
|
||||
return Lhs.Tag == RHS.Tag
|
||||
&& Lhs.State.StateMachineName == RHS.State.StateMachineName && Lhs.State.StateName == RHS.State.StateName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inequality operator for comparing two animation state mappings.
|
||||
* 比较两个动画状态映射的不等运算符。
|
||||
* @param Lhs Left-hand side mapping. 左侧映射。
|
||||
* @param RHS Right-hand side mapping. 右侧映射。
|
||||
* @return True if mappings are not equal. 如果映射不相等返回true。
|
||||
*/
|
||||
friend bool operator!=(const FGMS_AnimStateNameToTag& Lhs, const FGMS_AnimStateNameToTag& RHS)
|
||||
{
|
||||
return !(Lhs == RHS);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrapper for a list of animation state to tag mappings.
|
||||
* 动画状态到标签映射列表的包装器。
|
||||
*/
|
||||
USTRUCT()
|
||||
struct FGMS_AnimStateNameToTagWrapper
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Array of animation state to tag mappings.
|
||||
* 动画状态到标签映射的数组。
|
||||
*/
|
||||
UPROPERTY()
|
||||
TArray<FGMS_AnimStateNameToTag> AnimStateNameToTagMapping;
|
||||
};
|
||||
@@ -0,0 +1,651 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GMS_LocomotionStructLibrary.h"
|
||||
#include "Engine/TimerHandle.h"
|
||||
#include "TimerManager.h"
|
||||
#include "Animation/AnimExecutionContext.h"
|
||||
#include "Animation/AnimInstance.h"
|
||||
#include "Animation/AnimNodeReference.h"
|
||||
#include "Engine/World.h"
|
||||
#include "Settings/GMS_SettingStructLibrary.h"
|
||||
#include "Locomotions/GMS_AnimState.h"
|
||||
#include "Utility/GMS_Tags.h"
|
||||
#include "GMS_MainAnimInstance.generated.h"
|
||||
|
||||
class IPoseSearchTrajectoryPredictorInterface;
|
||||
class UGMS_AnimLayerSetting_Additive;
|
||||
class UGMS_AnimLayer;
|
||||
class UGMS_AnimLayerSetting;
|
||||
class UGMS_AnimLayerSetting_View;
|
||||
class UGMS_AnimLayerSetting_Overlay;
|
||||
class UGMS_AnimLayerSetting_States;
|
||||
class UGMS_MovementDefinition;
|
||||
class UGMS_MovementSystemComponent;
|
||||
|
||||
/**
|
||||
* Base animation template for the main animation instance.
|
||||
* 主动画实例的动画模板基类。
|
||||
*/
|
||||
UCLASS(BlueprintType)
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_MainAnimInstance : public UAnimInstance
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Gets the movement system component.
|
||||
* 获取运动系统组件。
|
||||
* @return The movement system component. 运动系统组件。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="GMS|Animation", meta=(BlueprintThreadSafe))
|
||||
UGMS_MovementSystemComponent* GetMovementSystemComponent() const;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* 构造函数。
|
||||
*/
|
||||
UGMS_MainAnimInstance();
|
||||
|
||||
/**
|
||||
* Initializes the animation.
|
||||
* 初始化动画。
|
||||
*/
|
||||
virtual void NativeInitializeAnimation() override;
|
||||
|
||||
/**
|
||||
* Uninitializes the animation.
|
||||
* 取消初始化动画。
|
||||
*/
|
||||
virtual void NativeUninitializeAnimation() override;
|
||||
|
||||
/**
|
||||
* Called when the game starts.
|
||||
* 游戏开始时调用。
|
||||
*/
|
||||
virtual void NativeBeginPlay() override;
|
||||
|
||||
/**
|
||||
* Updates the animation.
|
||||
* 更新动画。
|
||||
* @param DeltaTime Time since last update. 自上次更新以来的时间。
|
||||
*/
|
||||
virtual void NativeUpdateAnimation(float DeltaTime) override;
|
||||
|
||||
/**
|
||||
* Thread-safe animation update.
|
||||
* 线程安全的动画更新。
|
||||
* @param DeltaTime Time since last update. 自上次更新以来的时间。
|
||||
*/
|
||||
virtual void NativeThreadSafeUpdateAnimation(float DeltaTime) override;
|
||||
|
||||
/**
|
||||
* Applies an animation layer setting to an animation layer instance.
|
||||
* 将动画层设置应用于动画层实例。
|
||||
* @param LayerSetting The layer setting to apply. 要应用的层设置。
|
||||
* @param LayerInstance The layer instance to apply the setting to. 要应用设置的层实例。
|
||||
*/
|
||||
virtual void SetAnimLayerBySetting(const UGMS_AnimLayerSetting* LayerSetting, TObjectPtr<UGMS_AnimLayer>& LayerInstance);
|
||||
|
||||
/**
|
||||
* Registers animation state name to tag mappings for a given animation instance.
|
||||
* 为给定的动画实例注册动画状态名称到标签的映射。
|
||||
* @param SourceAnimInstance The animation instance to register mappings for. 要注册映射的动画实例。
|
||||
* @param Mapping The state name to tag mappings. 状态名称到标签的映射。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GMS|Animation")
|
||||
virtual void RegisterStateNameToTagMapping(UAnimInstance* SourceAnimInstance, TArray<FGMS_AnimStateNameToTag> Mapping);
|
||||
|
||||
/**
|
||||
* Unregisters animation state name to tag mappings for a given animation instance.
|
||||
* 为给定的动画实例取消注册动画状态名称到标签的映射。
|
||||
* @param SourceAnimInstance The animation instance to unregister mappings for. 要取消注册映射的动画实例。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category="GMS|Animation")
|
||||
virtual void UnregisterStateNameToTagMapping(UAnimInstance* SourceAnimInstance);
|
||||
|
||||
/**
|
||||
* Refreshes layer settings when core state changes (e.g., movement set, locomotion mode).
|
||||
* 当核心状态(如运动集、运动模式)更改时刷新层设置。
|
||||
* @note Override if custom movement definitions include additional animation layer settings. 如果自定义运动定义包含额外的动画层设置,则需覆盖。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GMS|Animation")
|
||||
void RefreshLayerSettings();
|
||||
virtual void RefreshLayerSettings_Implementation();
|
||||
|
||||
/**
|
||||
* Sets the offset root bone rotation mode.
|
||||
* 设置偏移根骨骼旋转模式。
|
||||
* @param NewRotationMode The new rotation mode. 新的旋转模式。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GMS|Animation", meta=(BlueprintThreadSafe))
|
||||
void SetOffsetRootBoneRotationMode(EOffsetRootBoneMode NewRotationMode);
|
||||
|
||||
/**
|
||||
* Gets the current offset root bone rotation mode.
|
||||
* 获取当前偏移根骨骼旋转模式。
|
||||
* @return The current rotation mode. 当前旋转模式。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, BlueprintNativeEvent, Category="GMS|Animation", meta=(BlueprintThreadSafe))
|
||||
EOffsetRootBoneMode GetOffsetRootBoneRotationMode() const;
|
||||
|
||||
/**
|
||||
* Gets the current offset root bone translation mode.
|
||||
* 获取当前偏移根骨骼平移模式。
|
||||
* @return The current translation mode. 当前平移模式。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, BlueprintNativeEvent, Category="GMS|Animation", meta=(BlueprintThreadSafe))
|
||||
EOffsetRootBoneMode GetOffsetRootBoneTranslationMode() const;
|
||||
|
||||
/**
|
||||
* Sets the offset root bone translation mode.
|
||||
* 设置偏移根骨骼平移模式。
|
||||
* @param NewTranslationMode The new translation mode. 新的平移模式。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GMS|Animation", meta=(BlueprintThreadSafe))
|
||||
void SetOffsetRootBoneTranslationMode(EOffsetRootBoneMode NewTranslationMode);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Called when the locomotion mode changes.
|
||||
* 运动模式更改时调用。
|
||||
* @param Prev The previous locomotion mode. 之前的运动模式。
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category="GMS|Animation")
|
||||
void OnLocomotionModeChanged(const FGameplayTag& Prev);
|
||||
|
||||
/**
|
||||
* Called when the rotation mode changes.
|
||||
* 旋转模式更改时调用。
|
||||
* @param Prev The previous rotation mode. 之前的旋转模式。
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category="GMS|Animation")
|
||||
void OnRotationModeChanged(const FGameplayTag& Prev);
|
||||
|
||||
/**
|
||||
* Called when the movement set changes.
|
||||
* 运动集更改时调用。
|
||||
* @param Prev The previous movement set. 之前的运动集。
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category="GMS|Animation")
|
||||
void OnMovementSetChanged(const FGameplayTag& Prev);
|
||||
|
||||
/**
|
||||
* Called when the movement state changes.
|
||||
* 运动状态更改时调用。
|
||||
* @param Prev The previous movement state. 之前的运动状态。
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category="GMS|Animation")
|
||||
void OnMovementStateChanged(const FGameplayTag& Prev);
|
||||
|
||||
/**
|
||||
* Called when the overlay mode changes.
|
||||
* 叠层模式更改时调用。
|
||||
* @param Prev The previous overlay mode. 之前的叠层模式。
|
||||
*/
|
||||
UFUNCTION(BlueprintNativeEvent, Category="GMS|Animation")
|
||||
void OnOverlayModeChanged(const FGameplayTag& Prev);
|
||||
|
||||
/**
|
||||
* Refreshes Trajectory-related data.
|
||||
* 刷新Trajectory相关数据。
|
||||
* @param DeltaTime Time since last update. 自上次更新以来的时间。
|
||||
*/
|
||||
virtual void RefreshTrajectoryState(float DeltaTime);
|
||||
|
||||
/**
|
||||
* Refreshes view-related data.
|
||||
* 刷新视图相关数据。
|
||||
* @param DeltaTime Time since last update. 自上次更新以来的时间。
|
||||
*/
|
||||
virtual void RefreshView(float DeltaTime);
|
||||
|
||||
/**
|
||||
* Refreshes locomotion data.
|
||||
* 刷新运动数据。
|
||||
* @param DeltaTime Time since last update. 自上次更新以来的时间。
|
||||
*/
|
||||
virtual void RefreshLocomotion(const float DeltaTime);
|
||||
|
||||
/**
|
||||
* Refreshes block state.
|
||||
* 刷新阻塞状态。
|
||||
*/
|
||||
virtual void RefreshBlock();
|
||||
|
||||
/**
|
||||
* Gather information from game world.
|
||||
* 从游戏世界获取信息。
|
||||
*/
|
||||
virtual void RefreshStateOnGameThread();
|
||||
|
||||
/**
|
||||
* Refreshes animation node relevance tags on the game thread.
|
||||
* 在游戏线程上刷新动画节点相关性标签。
|
||||
*/
|
||||
virtual void RefreshRelevanceOnGameThread();
|
||||
|
||||
/**
|
||||
* Refreshes grounded state data.
|
||||
* 刷新地面状态数据。
|
||||
*/
|
||||
virtual void RefreshGrounded();
|
||||
|
||||
/**
|
||||
* Refreshes lean data for grounded state.
|
||||
* 刷新地面状态的倾斜数据。
|
||||
*/
|
||||
virtual void RefreshGroundedLean();
|
||||
|
||||
/**
|
||||
* Gets the relative acceleration amount for leaning.
|
||||
* 获取用于倾斜的相对加速度量。
|
||||
* @return 2D vector of relative acceleration. 相对加速度的2D向量。
|
||||
*/
|
||||
virtual FVector2f GetRelativeAccelerationAmount() const;
|
||||
|
||||
/**
|
||||
* Refreshes in-air state data.
|
||||
* 刷新空中状态数据。
|
||||
*/
|
||||
virtual void RefreshInAir();
|
||||
|
||||
/**
|
||||
* Refreshes ground prediction data.
|
||||
* 刷新地面预测数据。
|
||||
*/
|
||||
virtual void RefreshGroundPrediction();
|
||||
|
||||
/**
|
||||
* Refreshes lean data for in-air state.
|
||||
* 刷新空中状态的倾斜数据。
|
||||
*/
|
||||
virtual void RefreshInAirLean();
|
||||
|
||||
/**
|
||||
* Refreshes the offset root bone state.
|
||||
* 刷新偏移根骨骼状态。
|
||||
* @param Context Animation update context. 动画更新上下文。
|
||||
* @param Node Animation node reference. 动画节点引用。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category="GMS|Animation", meta=(BlueprintThreadSafe))
|
||||
void RefreshOffsetRootBone(UPARAM(ref)
|
||||
FAnimUpdateContext& Context, UPARAM(ref)
|
||||
FAnimNodeReference& Node);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Gets the clamped curve value (0 to 1) for a given curve name.
|
||||
* 获取给定曲线名称的限制曲线值(0到1)。
|
||||
* @param CurveName The name of the curve. 曲线名称。
|
||||
* @return The clamped curve value. 限制的曲线值。
|
||||
*/
|
||||
float GetCurveValueClamped01(const FName& CurveName) const;
|
||||
|
||||
/**
|
||||
* Gets the named blend profile.
|
||||
* 获取命名的混合配置文件。
|
||||
* @param BlendProfileName The name of the blend profile. 混合配置文件名称。
|
||||
* @return The blend profile. 混合配置文件。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="GMS|Animation", meta=(BlueprintThreadSafe))
|
||||
UBlendProfile* GetNamedBlendProfile(const FName& BlendProfileName) const;
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="GMS|Animation", meta=(BlueprintThreadSafe))
|
||||
FGameplayTagContainer GetAggregatedTags() const;
|
||||
|
||||
/**
|
||||
* Gets the yaw value for aim offset.
|
||||
* 获取瞄准偏移的偏航值。
|
||||
* @return The aim offset yaw value. 瞄准偏移偏航值。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="GMS|Animation", meta=(BlueprintThreadSafe))
|
||||
float GetAOYawValue() const;
|
||||
|
||||
/**
|
||||
* Selects a cardinal direction based on an angle.
|
||||
* 根据角度选择主要方向。
|
||||
* @param Angle The angle to evaluate. 要评估的角度。
|
||||
* @param DeadZone The dead zone for direction changes. 方向变化的死区。
|
||||
* @param CurrentDirection The current direction. 当前方向。
|
||||
* @param bUseCurrentDirection Whether to consider the current direction. 是否考虑当前方向。
|
||||
* @return The selected cardinal direction. 选择的主要方向。
|
||||
*/
|
||||
EGMS_MovementDirection SelectCardinalDirectionFromAngle(float Angle, float DeadZone, EGMS_MovementDirection CurrentDirection, bool bUseCurrentDirection) const;
|
||||
|
||||
/**
|
||||
* Selects an octagonal direction based on an angle.
|
||||
* 根据角度选择八方向。
|
||||
* @param Angle The angle to evaluate. 要评估的角度。
|
||||
* @param DeadZone The dead zone for direction changes. 方向变化的死区。
|
||||
* @param CurrentDirection The current direction. 当前方向。
|
||||
* @param bUseCurrentDirection Whether to consider the current direction. 是否考虑当前方向。
|
||||
* @return The selected octagonal direction. 选择的八方向。
|
||||
*/
|
||||
EGMS_MovementDirection_8Way SelectOctagonalDirectionFromAngle(float Angle, float DeadZone, EGMS_MovementDirection_8Way CurrentDirection, bool bUseCurrentDirection) const;
|
||||
|
||||
/**
|
||||
* Gets the opposite cardinal direction.
|
||||
* 获取相反的主要方向。
|
||||
* @param CurrentDirection The current direction. 当前方向。
|
||||
* @return The opposite cardinal direction. 相反的主要方向。
|
||||
*/
|
||||
EGMS_MovementDirection GetOppositeCardinalDirection(EGMS_MovementDirection CurrentDirection) const;
|
||||
|
||||
/**
|
||||
* Checks if any core state has changed (movement set, state, locomotion, rotation, or overlay mode).
|
||||
* 检查是否有核心状态更改(运动集、状态、运动、旋转或叠层模式)。
|
||||
* @return True if any core state has changed. 如果任何核心状态更改则返回true。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="GMS|Animation", meta=(BlueprintThreadSafe))
|
||||
bool HasCoreStateChanges() const;
|
||||
|
||||
/**
|
||||
* Checks if specified core states have changed.
|
||||
* 检查指定的核心状态是否更改。
|
||||
* @param bCheckLocomotionMode Check locomotion mode changes. 检查运动模式更改。
|
||||
* @param bCheckMovementSet Check movement set changes. 检查运动集更改。
|
||||
* @param bCheckRotationMode Check rotation mode changes. 检查旋转模式更改。
|
||||
* @param bCheckMovementState Check movement state changes. 检查运动状态更改。
|
||||
* @param bCheckOverlayMode Check overlay mode changes. 检查叠层模式更改。
|
||||
* @return True if any specified state has changed. 如果任何指定状态更改则返回true。
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="GMS|Animation", meta=(BlueprintThreadSafe))
|
||||
bool CheckCoreStateChanges(bool bCheckLocomotionMode, bool bCheckMovementSet, bool bCheckRotationMode, bool bCheckMovementState, bool bCheckOverlayMode) const;
|
||||
|
||||
/**
|
||||
* Animation state name to gameplay tag mappings.
|
||||
* 动画状态名称到游戏标签的映射。
|
||||
* @note Used to check if a state node is active via NodeRelevantTags. 用于通过NodeRelevantTags检查状态节点是否活跃。
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category="Settings", meta=(TitleProperty="Tag"))
|
||||
TArray<FGMS_AnimStateNameToTag> AnimStateNameToTagMapping;
|
||||
|
||||
/**
|
||||
* Current locomotion mode.
|
||||
* 当前运动模式。
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category="Settings", meta=(Categories="GMS.LocomotionMode"))
|
||||
FGameplayTag LocomotionMode{GMS_MovementModeTags::Grounded};
|
||||
|
||||
/**
|
||||
* Container for locomotion mode (for chooser only).
|
||||
* 运动模式的容器(仅用于选择器)。
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category="Settings", meta=(Categories="GMS.LocomotionMode"))
|
||||
FGameplayTagContainer LocomotionModeContainer;
|
||||
|
||||
/**
|
||||
* Current rotation mode.
|
||||
* 当前旋转模式。
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category="Settings", meta=(Categories="GMS.RotationMode"))
|
||||
FGameplayTag RotationMode{GMS_RotationModeTags::ViewDirection};
|
||||
|
||||
/**
|
||||
* Container for rotation mode (for chooser only).
|
||||
* 旋转模式的容器(仅用于选择器)。
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category="Settings", meta=(Categories="GMS.RotationMode"))
|
||||
FGameplayTagContainer RotationModeContainer;
|
||||
|
||||
/**
|
||||
* Current movement state.
|
||||
* 当前运动状态。
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category="Settings", meta=(Categories="GMS.MovementState"))
|
||||
FGameplayTag MovementState{GMS_MovementStateTags::Jog};
|
||||
|
||||
/**
|
||||
* Container for movement state (for chooser only).
|
||||
* 运动状态的容器(仅用于选择器)。
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category="Settings", meta=(Categories="GMS.MovementState"))
|
||||
FGameplayTagContainer MovementStateContainer;
|
||||
|
||||
/**
|
||||
* Current movement set.
|
||||
* 当前运动集。
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category="Settings", meta=(Categories="GMS.MovementSet"))
|
||||
FGameplayTag MovementSet;
|
||||
|
||||
/**
|
||||
* Container for movement set (for chooser only).
|
||||
* 运动集的容器(仅用于选择器)。
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category="Settings", meta=(Categories="GMS.MovementSet"))
|
||||
FGameplayTagContainer MovementSetContainer;
|
||||
|
||||
/**
|
||||
* Current overlay mode.
|
||||
* 当前叠层模式。
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category="Settings", meta=(Categories="GMS.OverlayMode"))
|
||||
FGameplayTag OverlayMode{GMS_OverlayModeTags::Default};
|
||||
|
||||
/**
|
||||
* Container for overlay mode (for chooser only).
|
||||
* 叠层模式的容器(仅用于选择器)。
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category="Settings", meta=(Categories="GMS.OverlayMode"))
|
||||
FGameplayTagContainer OverlayModeContainer;
|
||||
|
||||
/**
|
||||
* Enables ground prediction.
|
||||
* 启用地面预测。
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category="Settings")
|
||||
bool bEnableGroundPrediction{false};
|
||||
|
||||
/**
|
||||
* General animation settings.
|
||||
* 通用动画设置。
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category="Settings")
|
||||
FGMS_AnimDataSetting_General GeneralSetting;
|
||||
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Settings", meta=(BlueprintThreadSafe))
|
||||
TObjectPtr<const UGMS_MovementControlSetting_Default> ControlSetting{nullptr};
|
||||
|
||||
|
||||
UPROPERTY(Transient)
|
||||
FGMS_MovementBaseState MovementBase;
|
||||
|
||||
/**
|
||||
* Locomotion state for the game thread.
|
||||
* 游戏线程的运动状态。
|
||||
*/
|
||||
UPROPERTY(Transient)
|
||||
FGMS_LocomotionState GameLocomotionState;
|
||||
|
||||
/**
|
||||
* Root bone animation state.
|
||||
* 根骨骼动画状态。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="State")
|
||||
FGMS_AnimState_Root RootState;
|
||||
|
||||
|
||||
/**
|
||||
* Locomotion animation state.
|
||||
* 运动动画状态。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="State")
|
||||
FGMS_AnimState_Locomotion LocomotionState;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="State")
|
||||
FGMS_AnimState_Trajectory TrajectoryState;
|
||||
|
||||
/**
|
||||
* View-related animation state.
|
||||
* 视图相关动画状态。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="State")
|
||||
FGMS_AnimState_View ViewState;
|
||||
|
||||
/**
|
||||
* Lean animation state.
|
||||
* 倾斜动画状态。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="State")
|
||||
FGMS_AnimState_Lean LeanState;
|
||||
|
||||
/**
|
||||
* Movement intent vector.
|
||||
* 运动意图向量。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="State", meta=(DisplayName="Movement Intent"))
|
||||
FVector MovementIntent;
|
||||
|
||||
/**
|
||||
* In-air animation state.
|
||||
* 空中动画状态。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="State")
|
||||
FGMS_AnimState_InAir InAirState;
|
||||
|
||||
/**
|
||||
* Tags owned by the animation instance.
|
||||
* 动画实例拥有的标签。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="State")
|
||||
FGameplayTagContainer OwnedTags;
|
||||
|
||||
/**
|
||||
* Tags indicating active animation nodes.
|
||||
* 指示活跃动画节点的标签。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="State")
|
||||
FGameplayTagContainer NodeRelevanceTags;
|
||||
|
||||
/**
|
||||
* Indicates if any montage is playing.
|
||||
* 指示是否有蒙太奇在播放。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="State")
|
||||
bool bAnyMontagePlaying{false};
|
||||
|
||||
/**
|
||||
* Indicates if the movement state has changed.
|
||||
* 指示运动状态是否更改。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="State")
|
||||
bool bMovementStateChanged{false};
|
||||
|
||||
/**
|
||||
* Indicates if the locomotion mode has changed.
|
||||
* 指示运动模式是否更改。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="State")
|
||||
bool bLocomotionModeChanged{false};
|
||||
|
||||
/**
|
||||
* Indicates if the movement set has changed.
|
||||
* 指示运动集是否更改。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="State")
|
||||
bool bMovementSetChanged{false};
|
||||
|
||||
/**
|
||||
* Indicates if the rotation mode has changed.
|
||||
* 指示旋转模式是否更改。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="State")
|
||||
bool bRotationModeChanged{false};
|
||||
|
||||
/**
|
||||
* Indicates if the overlay mode has changed.
|
||||
* 指示叠层模式是否更改。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="State")
|
||||
bool bOverlayModeChanged{false};
|
||||
|
||||
/**
|
||||
* Indicates if movement is blocked.
|
||||
* 指示移动是否被阻塞。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="State", meta=(DisplayName="Movement Blocked"))
|
||||
bool bBlocked{false};
|
||||
|
||||
/**
|
||||
* Indicates if the character has just landed.
|
||||
* 指示角色是否刚刚着陆。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="State")
|
||||
bool bJustLanded{false};
|
||||
|
||||
/**
|
||||
* Indicates if this is the first update.
|
||||
* 指示这是否是第一次更新。
|
||||
*/
|
||||
bool bFirstUpdate = true;
|
||||
|
||||
UPROPERTY()
|
||||
TScriptInterface<IPoseSearchTrajectoryPredictorInterface> TrajectoryPredictor;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Reference to the movement system component.
|
||||
* 运动系统组件的引用。
|
||||
*/
|
||||
UPROPERTY()
|
||||
TObjectPtr<UGMS_MovementSystemComponent> MovementSystem;
|
||||
|
||||
|
||||
/**
|
||||
* Reference to the owning pawn.
|
||||
* 拥有Pawn的引用。
|
||||
*/
|
||||
UPROPERTY(Transient)
|
||||
TObjectPtr<APawn> PawnOwner;
|
||||
|
||||
/**
|
||||
* Runtime mappings of animation state names to tags.
|
||||
* 动画状态名称到标签的运行时映射。
|
||||
*/
|
||||
UPROPERTY()
|
||||
TMap<TObjectPtr<UAnimInstance>, FGMS_AnimStateNameToTagWrapper> RuntimeAnimStateNameToTagMappings;
|
||||
|
||||
/**
|
||||
* Timer handle for initial updates.
|
||||
* 初始更新的计时器句柄。
|
||||
*/
|
||||
FTimerHandle InitialTimerHandle;
|
||||
|
||||
/**
|
||||
* Animation layer instance for states.
|
||||
* 状态动画层实例。
|
||||
*/
|
||||
UPROPERTY(VisibleInstanceOnly, Category="AnimLayers", meta=(ShowInnerProperties))
|
||||
TObjectPtr<UGMS_AnimLayer> StateLayerInstance;
|
||||
|
||||
/**
|
||||
* Animation layer instance for overlays.
|
||||
* 叠层动画层实例。
|
||||
*/
|
||||
UPROPERTY(VisibleInstanceOnly, Category="AnimLayers", meta=(ShowInnerProperties))
|
||||
TObjectPtr<UGMS_AnimLayer> OverlayLayerInstance;
|
||||
|
||||
/**
|
||||
* Animation layer instance for view.
|
||||
* 视图动画层实例。
|
||||
*/
|
||||
UPROPERTY(VisibleInstanceOnly, Category="AnimLayers", meta=(ShowInnerProperties))
|
||||
TObjectPtr<UGMS_AnimLayer> ViewLayerInstance;
|
||||
|
||||
/**
|
||||
* Animation layer instance for additive animations.
|
||||
* 附加动画层实例。
|
||||
*/
|
||||
UPROPERTY(VisibleInstanceOnly, Category="AnimLayers", meta=(ShowInnerProperties))
|
||||
TObjectPtr<UGMS_AnimLayer> AdditiveLayerInstance;
|
||||
|
||||
/**
|
||||
* Animation layer instance for skeletal controls.
|
||||
* 骨骼控制动画层实例。
|
||||
*/
|
||||
UPROPERTY(VisibleInstanceOnly, Category="AnimLayers", meta=(ShowInnerProperties))
|
||||
TObjectPtr<UGMS_AnimLayer> SkeletonControlsLayerInstance;
|
||||
};
|
||||
@@ -0,0 +1,57 @@
|
||||
// // Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
//
|
||||
// #pragma once
|
||||
//
|
||||
// #include "CoreMinimal.h"
|
||||
// #include "GameFramework/CharacterMovementComponent.h"
|
||||
// #include "Locomotions/GMS_LocomotionStructLibrary.h"
|
||||
// #include "GMS_CharacterMovementComponent.generated.h"
|
||||
//
|
||||
// class UGMS_CharacterMovementSystemComponent;
|
||||
// class UGMS_CMC_MovementMode;
|
||||
//
|
||||
// UCLASS(ClassGroup=(GMS), Blueprintable)
|
||||
// class GENERICMOVEMENTSYSTEM_API UGMS_CharacterMovementComponent : public UCharacterMovementComponent
|
||||
// {
|
||||
// GENERATED_BODY()
|
||||
//
|
||||
// friend UGMS_CMC_MovementMode;
|
||||
//
|
||||
// public:
|
||||
// // Sets default values for this component's properties
|
||||
// UGMS_CharacterMovementComponent(const FObjectInitializer& ObjectInitializer);
|
||||
//
|
||||
// protected:
|
||||
// virtual void InitializeComponent() override;
|
||||
// virtual void SetUpdatedComponent(USceneComponent* NewUpdatedComponent) override;
|
||||
// virtual bool HasValidData() const override;
|
||||
//
|
||||
// virtual void TickCharacterPose(float DeltaTime) override;
|
||||
// virtual void PhysicsRotation(float DeltaTime) override;
|
||||
//
|
||||
// void GMS_TurnToDesiredRotation(const FRotator& CurrentRotation, FRotator DesiredRotation, FRotator DeltaRot);
|
||||
//
|
||||
// void GMS_TurnToDesiredRotationWithRotationRate(const FRotator& CurrentRotation, FRotator DesiredRotation, FRotator DeltaRot);
|
||||
//
|
||||
//
|
||||
// UFUNCTION(BlueprintNativeEvent, Category="GMS|Movement", meta=(DisplayName="Physics Rotation"))
|
||||
// void GMS_PhysicsRotation(float DeltaTime);
|
||||
//
|
||||
// UFUNCTION(BlueprintNativeEvent, Category="GMS|Movement", meta=(DisplayName="Compute Orient To Desired Movement Rotation"))
|
||||
// FRotator GMS_ComputeOrientToDesiredMovementRotation(const FRotator& CurrentRotation, float DeltaTime, FRotator& DeltaRotation) const;
|
||||
//
|
||||
// UFUNCTION(BlueprintNativeEvent, Category="GMS|Movement", meta=(DisplayName="Compute Orient To Desired View Rotation"))
|
||||
// FRotator GMS_ComputeOrientToDesiredViewRotation(const FRotator& CurrentRotation, float DeltaTime, FRotator& DeltaRotation) const;
|
||||
//
|
||||
// UFUNCTION(BlueprintNativeEvent, Category="GMS|Movement", meta=(DisplayName="Compute Orient To Desired View Rotation"))
|
||||
// bool HasAnimationRotationDeltaYawAngle(float DeltaTime, float& OutDeltaYawAngle) const;
|
||||
//
|
||||
// /**
|
||||
// * Using the default physic rotation of CMC, instead of GMS one.
|
||||
// */
|
||||
// UPROPERTY(Category="Character Movement (Rotation Settings)", EditAnywhere, BlueprintReadWrite)
|
||||
// bool bUseNativeRotation{false};
|
||||
//
|
||||
// UPROPERTY(Transient, DuplicateTransient)
|
||||
// TObjectPtr<UGMS_CharacterMovementSystemComponent> MovementSystem;
|
||||
// };
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "DefaultMovementSet/Modes/FlyingMode.h"
|
||||
#include "GMS_FlyingMode.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_FlyingMode : public UFlyingMode
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "MovementMode.h"
|
||||
#include "UObject/Object.h"
|
||||
#include "GMS_MoverSettingObjectLibrary.generated.h"
|
||||
|
||||
|
||||
/**
|
||||
* CommonLegacyMovementSettings: collection of settings that are shared between several of the legacy movement modes
|
||||
*/
|
||||
UCLASS(BlueprintType)
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_MoverGroundedMovementSettings : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
@@ -0,0 +1,159 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "MoverTypes.h"
|
||||
#include "GMS_MoverStructLibrary.generated.h"
|
||||
|
||||
/**
|
||||
* Data block containing extended movement actions inputs used by Generic Movement System.
|
||||
* 包含GMS所用到的运动行为输入定义的数据块。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_MoverActionInputs : public FMoverDataStructBase
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category = Mover)
|
||||
bool bIsDashJustPressed = false;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category = Mover)
|
||||
bool bIsAimPressed = false;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category = Mover)
|
||||
bool bIsVaultJustPressed = false;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category = Mover)
|
||||
bool bWantsToStartZiplining = false;
|
||||
|
||||
UPROPERTY(BlueprintReadWrite, Category = Mover)
|
||||
bool bWantsToBeCrouched = false;
|
||||
|
||||
// @return newly allocated copy of this FGMS_MoverActionInputs. Must be overridden by child classes
|
||||
virtual FMoverDataStructBase* Clone() const override
|
||||
{
|
||||
// TODO: ensure that this memory allocation jives with deletion method
|
||||
FGMS_MoverActionInputs* CopyPtr = new FGMS_MoverActionInputs(*this);
|
||||
return CopyPtr;
|
||||
}
|
||||
|
||||
virtual bool NetSerialize(FArchive& Ar, UPackageMap* Map, bool& bOutSuccess) override
|
||||
{
|
||||
Super::NetSerialize(Ar, Map, bOutSuccess);
|
||||
|
||||
Ar.SerializeBits(&bIsDashJustPressed, 1);
|
||||
Ar.SerializeBits(&bIsAimPressed, 1);
|
||||
Ar.SerializeBits(&bIsVaultJustPressed, 1);
|
||||
Ar.SerializeBits(&bWantsToStartZiplining, 1);
|
||||
Ar.SerializeBits(&bWantsToBeCrouched, 1);
|
||||
|
||||
bOutSuccess = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual UScriptStruct* GetScriptStruct() const override { return StaticStruct(); }
|
||||
|
||||
virtual void ToString(FAnsiStringBuilderBase& Out) const override
|
||||
{
|
||||
Super::ToString(Out);
|
||||
Out.Appendf("bIsDashJustPressed: %i\n", bIsDashJustPressed);
|
||||
Out.Appendf("bIsAimPressed: %i\n", bIsAimPressed);
|
||||
Out.Appendf("bIsVaultJustPressed: %i\n", bIsVaultJustPressed);
|
||||
Out.Appendf("bWantsToStartZiplining: %i\n", bWantsToStartZiplining);
|
||||
Out.Appendf("bWantsToBeCrouched: %i\n", bWantsToBeCrouched);
|
||||
}
|
||||
|
||||
virtual void AddReferencedObjects(FReferenceCollector& Collector) override { Super::AddReferencedObjects(Collector); }
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_MoverTagInputs : public FMoverDataStructBase
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
|
||||
/**
|
||||
* A container which you can query tag to indicate a input is requested.
|
||||
*/
|
||||
UPROPERTY(BlueprintReadWrite, Category = Mover)
|
||||
FGameplayTagContainer Tags;
|
||||
|
||||
// @return newly allocated copy of this FGMS_MoverActionInputs. Must be overridden by child classes
|
||||
virtual FMoverDataStructBase* Clone() const override
|
||||
{
|
||||
// TODO: ensure that this memory allocation jives with deletion method
|
||||
FGMS_MoverTagInputs* CopyPtr = new FGMS_MoverTagInputs(*this);
|
||||
return CopyPtr;
|
||||
}
|
||||
|
||||
virtual bool NetSerialize(FArchive& Ar, UPackageMap* Map, bool& bOutSuccess) override
|
||||
{
|
||||
bool bSuccess = Super::NetSerialize(Ar, Map, bOutSuccess);
|
||||
|
||||
Tags.NetSerialize(Ar, Map, bSuccess);
|
||||
return bSuccess;
|
||||
}
|
||||
|
||||
virtual UScriptStruct* GetScriptStruct() const override { return StaticStruct(); }
|
||||
|
||||
virtual void ToString(FAnsiStringBuilderBase& Out) const override
|
||||
{
|
||||
Super::ToString(Out);
|
||||
Out.Appendf("MoverTagInputs Tags[%s] \n", *Tags.ToString());
|
||||
}
|
||||
|
||||
virtual void AddReferencedObjects(FReferenceCollector& Collector) override { Super::AddReferencedObjects(Collector); }
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_MoverMovementControlInputs : public FMoverDataStructBase
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
|
||||
/**
|
||||
* 当前是什么运动集?影响角色动画表现方式。
|
||||
*/
|
||||
UPROPERTY(BlueprintReadWrite, Category = Mover)
|
||||
FGameplayTag DesiredMovementSet;
|
||||
|
||||
/**
|
||||
* 想以什么状态移动?影响角色移动参数
|
||||
*/
|
||||
UPROPERTY(BlueprintReadWrite, Category = Mover)
|
||||
FGameplayTag DesiredMovementState;
|
||||
|
||||
/**
|
||||
* 想以什么方式旋转?影响角色的朝向方式。
|
||||
*/
|
||||
UPROPERTY(BlueprintReadWrite, Category = Mover)
|
||||
FGameplayTag DesiredRotationMode;
|
||||
|
||||
// @return newly allocated copy of this FGMS_MoverActionInputs. Must be overridden by child classes
|
||||
virtual FMoverDataStructBase* Clone() const override
|
||||
{
|
||||
// TODO: ensure that this memory allocation jives with deletion method
|
||||
FGMS_MoverMovementControlInputs* CopyPtr = new FGMS_MoverMovementControlInputs(*this);
|
||||
return CopyPtr;
|
||||
}
|
||||
|
||||
virtual bool NetSerialize(FArchive& Ar, UPackageMap* Map, bool& bOutSuccess) override
|
||||
{
|
||||
bool bSuccess = Super::NetSerialize(Ar, Map, bOutSuccess);
|
||||
DesiredMovementSet.NetSerialize(Ar, Map, bSuccess);
|
||||
DesiredMovementState.NetSerialize(Ar, Map, bSuccess);
|
||||
DesiredRotationMode.NetSerialize(Ar, Map, bSuccess);
|
||||
return bSuccess;
|
||||
}
|
||||
|
||||
virtual UScriptStruct* GetScriptStruct() const override { return StaticStruct(); }
|
||||
|
||||
virtual void ToString(FAnsiStringBuilderBase& Out) const override
|
||||
{
|
||||
Super::ToString(Out);
|
||||
Out.Appendf("DesiredMovementSet [%s] \n", *DesiredMovementSet.ToString());
|
||||
Out.Appendf("DesiredMovementState [%s] \n", *DesiredMovementState.ToString());
|
||||
Out.Appendf("DesiredRotationMode [%s] \n", *DesiredRotationMode.ToString());
|
||||
}
|
||||
|
||||
virtual void AddReferencedObjects(FReferenceCollector& Collector) override { Super::AddReferencedObjects(Collector); }
|
||||
};
|
||||
@@ -0,0 +1,84 @@
|
||||
// // Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
//
|
||||
// #pragma once
|
||||
//
|
||||
// #include "CoreMinimal.h"
|
||||
// #include "MovementModifier.h"
|
||||
// #include "UObject/Object.h"
|
||||
// #include "GMS_MovementStateModifer.generated.h"
|
||||
//
|
||||
//
|
||||
// USTRUCT(BlueprintType)
|
||||
// struct FGMS_MovementStateModifierEntry
|
||||
// {
|
||||
// GENERATED_BODY()
|
||||
//
|
||||
// /** Maximum speed in the movement plane */
|
||||
// UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="General", meta = (ClampMin = "0", UIMin = "0", ForceUnits = "cm/s"))
|
||||
// float MaxSpeed = 800.f;
|
||||
//
|
||||
// /** Default max linear rate of deceleration when there is no controlled input */
|
||||
// UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="General", meta = (ClampMin = "0", UIMin = "0", ForceUnits = "cm/s^2"))
|
||||
// float Deceleration = 4000.f;
|
||||
//
|
||||
// /** Default max linear rate of acceleration for controlled input. May be scaled based on magnitude of input. */
|
||||
// UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="General", meta = (ClampMin = "0", UIMin = "0", ForceUnits = "cm/s^2"))
|
||||
// float Acceleration = 4000.f;
|
||||
//
|
||||
// /** Maximum rate of turning rotation (degrees per second). Negative numbers indicate instant rotation and should cause rotation to snap instantly to desired direction. */
|
||||
// UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="General", meta = (ClampMin = "-1", UIMin = "0", ForceUnits = "degrees/s"))
|
||||
// float TurningRate = 500.f;
|
||||
//
|
||||
// /** Speeds velocity direction changes while turning, to reduce sliding */
|
||||
// UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="General", meta = (ClampMin = "0", UIMin = "0", ForceUnits = "Multiplier"))
|
||||
// float TurningBoost = 8.f;
|
||||
// };
|
||||
//
|
||||
//
|
||||
// /**
|
||||
// * States: Applies settings to the actor to make them go into different states like walk or jog, sprint, affects actor speed and acceleration/deceleration, turn etc,
|
||||
// */
|
||||
// USTRUCT(BlueprintType)
|
||||
// struct MOVER_API FGMS_MovementStateModifier : public FMovementModifierBase
|
||||
// {
|
||||
// GENERATED_BODY()
|
||||
//
|
||||
// FGMS_MovementStateModifier();
|
||||
//
|
||||
// virtual ~FGMS_MovementStateModifier() override
|
||||
// {
|
||||
// }
|
||||
//
|
||||
// UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Modifer")
|
||||
// bool bRevertMovementSettingsOnEnd{false};
|
||||
//
|
||||
// /** Fired when this modifier is activated. */
|
||||
// virtual void OnStart(UMoverComponent* MoverComp, const FMoverTimeStep& TimeStep, const FMoverSyncState& SyncState, const FMoverAuxStateContext& AuxState) override;
|
||||
//
|
||||
// /** Fired when this modifier is deactivated. */
|
||||
// virtual void OnEnd(UMoverComponent* MoverComp, const FMoverTimeStep& TimeStep, const FMoverSyncState& SyncState, const FMoverAuxStateContext& AuxState) override;
|
||||
//
|
||||
// /** Fired just before a Substep */
|
||||
// virtual void OnPreMovement(UMoverComponent* MoverComp, const FMoverTimeStep& TimeStep) override;
|
||||
//
|
||||
// /** Fired after a Substep */
|
||||
// virtual void OnPostMovement(UMoverComponent* MoverComp, const FMoverTimeStep& TimeStep, const FMoverSyncState& SyncState, const FMoverAuxStateContext& AuxState) override;
|
||||
//
|
||||
// // @return newly allocated copy of this FMovementModifier. Must be overridden by child classes
|
||||
// virtual FMovementModifierBase* Clone() const override;
|
||||
//
|
||||
// virtual void NetSerialize(FArchive& Ar) override;
|
||||
//
|
||||
// virtual UScriptStruct* GetScriptStruct() const override;
|
||||
//
|
||||
// virtual FString ToSimpleString() const override;
|
||||
//
|
||||
// virtual void AddReferencedObjects(class FReferenceCollector& Collector) override;
|
||||
//
|
||||
// protected:
|
||||
// // Applies any movement settings like acceleration or max speed changes
|
||||
// void ApplyMovementSettings(UMoverComponent* MoverComp);
|
||||
//
|
||||
// // Reverts any movement settings like acceleration or max speed changes
|
||||
// void RevertMovementSettings(UMoverComponent* MoverComp);
|
||||
// };
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "DefaultMovementSet/Modes/WalkingMode.h"
|
||||
#include "GMS_WalkingMode.generated.h"
|
||||
|
||||
class UGMS_MoverGroundedMovementSettings;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_WalkingMode : public UWalkingMode
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UGMS_WalkingMode(const FObjectInitializer& ObjectInitializer);
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/Interface.h"
|
||||
#include "GMS_ZiplineInterface.generated.h"
|
||||
|
||||
// This class does not need to be modified.
|
||||
UINTERFACE()
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_ZiplineInterface : public UInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class GENERICMOVEMENTSYSTEM_API IGMS_ZiplineInterface
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// Add interface functions to this class. This is the class that will be inherited to implement this interface.
|
||||
public:
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Zipline")
|
||||
USceneComponent* GetStartComponent();
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "Zipline")
|
||||
USceneComponent* GetEndComponent();
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Runtime/Launch/Resources/Version.h"
|
||||
#include "GMS_ZipliningMode.h"
|
||||
#include "MovementModeTransition.h"
|
||||
#include "GMS_ZiplineModeTransition.generated.h"
|
||||
|
||||
|
||||
/**
|
||||
* Transition that handles starting ziplining based on input. Character must be airborne to catch the
|
||||
* zipline, regardless of input.
|
||||
*/
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class UGMS_ZiplineStartTransition : public UBaseMovementModeTransition
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
#if ENGINE_MINOR_VERSION >= 6
|
||||
virtual FTransitionEvalResult Evaluate_Implementation(const FSimulationTickParams& Params) const override;
|
||||
#else
|
||||
virtual FTransitionEvalResult OnEvaluate(const FSimulationTickParams& Params) const override;
|
||||
#endif
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Ziplining")
|
||||
FName ZipliningModeName = ExtendedModeNames::Ziplining;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Ziplining")
|
||||
FGameplayTag ZipliningInputTag;
|
||||
};
|
||||
|
||||
/**
|
||||
* Transition that handles exiting ziplining based on input
|
||||
*/
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class UGMS_ZiplineEndTransition : public UBaseMovementModeTransition
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
#if ENGINE_MINOR_VERSION >= 6
|
||||
virtual FTransitionEvalResult Evaluate_Implementation(const FSimulationTickParams& Params) const override;
|
||||
virtual void Trigger_Implementation(const FSimulationTickParams& Params) override;
|
||||
#else
|
||||
virtual FTransitionEvalResult OnEvaluate(const FSimulationTickParams& Params) const override;
|
||||
virtual void OnTrigger(const FSimulationTickParams& Params) override;
|
||||
#endif
|
||||
|
||||
// Mode to enter when exiting the zipline
|
||||
UPROPERTY(EditAnywhere, Category = "Ziplining")
|
||||
FName AutoExitToMode = DefaultModeNames::Falling;
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Runtime/Launch/Resources/Version.h"
|
||||
#include "MovementMode.h"
|
||||
#include "GMS_ZipliningMode.generated.h"
|
||||
|
||||
namespace ExtendedModeNames
|
||||
{
|
||||
const FName Ziplining = TEXT("Ziplining");
|
||||
}
|
||||
|
||||
|
||||
// ZipliningMode: movement mode that traverses an actor implementing the IZipline interface
|
||||
UCLASS(Blueprintable, BlueprintType)
|
||||
class UGMS_ZipliningMode : public UBaseMovementMode
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
#if ENGINE_MINOR_VERSION >=6
|
||||
virtual void GenerateMove_Implementation(const FMoverTickStartData& StartState, const FMoverTimeStep& TimeStep, FProposedMove& OutProposedMove) const override;
|
||||
virtual void SimulationTick_Implementation(const FSimulationTickParams& Params, FMoverTickEndData& OutputState) override;
|
||||
#else
|
||||
virtual void OnGenerateMove(const FMoverTickStartData& StartState, const FMoverTimeStep& TimeStep, FProposedMove& OutProposedMove) const override;
|
||||
virtual void OnSimulationTick(const FSimulationTickParams& Params, FMoverTickEndData& OutputState) override;
|
||||
#endif
|
||||
|
||||
// Maximum speed
|
||||
UPROPERTY(EditAnywhere, Category = "Ziplining", meta = (ClampMin = "1", UIMin = "1", ForceUnits = "cm/s"))
|
||||
float MaxSpeed = 1000.0f;
|
||||
};
|
||||
|
||||
|
||||
// Data block containing ziplining state info, used while ZipliningMode is active
|
||||
USTRUCT()
|
||||
struct FGMS_ZipliningState : public FMoverDataStructBase
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
|
||||
TObjectPtr<AActor> ZiplineActor;
|
||||
bool bIsMovingAtoB;
|
||||
|
||||
FGMS_ZipliningState()
|
||||
: bIsMovingAtoB(true)
|
||||
{
|
||||
}
|
||||
|
||||
virtual FMoverDataStructBase* Clone() const override;
|
||||
virtual bool NetSerialize(FArchive& Ar, UPackageMap* Map, bool& bOutSuccess) override;
|
||||
virtual UScriptStruct* GetScriptStruct() const override { return StaticStruct(); }
|
||||
virtual void ToString(FAnsiStringBuilderBase& Out) const override;
|
||||
virtual bool ShouldReconcile(const FMoverDataStructBase& AuthorityState) const override;
|
||||
virtual void Interpolate(const FMoverDataStructBase& From, const FMoverDataStructBase& To, float Pct) override;
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Animation/AnimNodeBase.h"
|
||||
#include "GMS_AnimNode_CurvesBlend.generated.h"
|
||||
|
||||
UENUM(BlueprintType)
|
||||
enum class EGMS_CurvesBlendMode : uint8
|
||||
{
|
||||
// Blend poses using blend amount. Same as ECurveBlendOption::BlendByWeight.
|
||||
BlendByAmount,
|
||||
// Only set the value if the curves pose has the curve value. Same as ECurveBlendOption::Override.
|
||||
Combine,
|
||||
// Only set the value if the source pose doesn't have the curve value. Same as ECurveBlendOption::DoNotOverride.
|
||||
CombinePreserved,
|
||||
// Find the highest curve value from multiple poses and use that. Same as ECurveBlendOption::UseMaxValue.
|
||||
UseMaxValue,
|
||||
// Find the lowest curve value from multiple poses and use that. Same as ECurveBlendOption::UseMinValue.
|
||||
UseMinValue,
|
||||
// Completely override source pose. Same as ECurveBlendOption::UseBasePose.
|
||||
Override
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintInternalUseOnly)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimNode_CurvesBlend : public FAnimNode_Base
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Settings")
|
||||
FPoseLink SourcePose;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Settings")
|
||||
FPoseLink CurvesPose;
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
UPROPERTY(EditAnywhere, Category = "Settings", Meta = (ClampMin = 0, ClampMax = 1, FoldProperty, PinShownByDefault))
|
||||
float BlendAmount{1.0f};
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Settings", Meta = (FoldProperty))
|
||||
EGMS_CurvesBlendMode BlendMode{EGMS_CurvesBlendMode::BlendByAmount};
|
||||
#endif
|
||||
|
||||
public:
|
||||
virtual void Initialize_AnyThread(const FAnimationInitializeContext& Context) override;
|
||||
|
||||
virtual void CacheBones_AnyThread(const FAnimationCacheBonesContext& Context) override;
|
||||
|
||||
virtual void Update_AnyThread(const FAnimationUpdateContext& Context) override;
|
||||
|
||||
virtual void Evaluate_AnyThread(FPoseContext& Output) override;
|
||||
|
||||
virtual void GatherDebugData(FNodeDebugData& DebugData) override;
|
||||
|
||||
public:
|
||||
float GetBlendAmount() const;
|
||||
|
||||
EGMS_CurvesBlendMode GetBlendMode() const;
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GameplayTagContainer.h"
|
||||
#include "AnimNodes/AnimNode_BlendListBase.h"
|
||||
#include "GMS_AnimNode_GameplayTagsBlend.generated.h"
|
||||
|
||||
USTRUCT()
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimNode_GameplayTagsBlend : public FAnimNode_BlendListBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
#if WITH_EDITORONLY_DATA
|
||||
UPROPERTY(EditAnywhere, Category="Settings", Meta = (FoldProperty, PinShownByDefault))
|
||||
FGameplayTag ActiveTag;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="Settings", Meta = (FoldProperty))
|
||||
TArray<FGameplayTag> Tags;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual int32 GetActiveChildIndex() override;
|
||||
|
||||
public:
|
||||
const FGameplayTag& GetActiveTag() const;
|
||||
|
||||
const TArray<FGameplayTag>& GetTags() const;
|
||||
|
||||
#if WITH_EDITOR
|
||||
void RefreshPoses();
|
||||
#endif
|
||||
};
|
||||
@@ -0,0 +1,168 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/ObjectMacros.h"
|
||||
#include "Animation/AnimTypes.h"
|
||||
#include "Animation/AnimNodeBase.h"
|
||||
#include "Animation/AnimData/BoneMaskFilter.h"
|
||||
#include "Settings/GMS_SettingStructLibrary.h"
|
||||
#include "GMS_AnimNode_LayeredBoneBlend.generated.h"
|
||||
|
||||
|
||||
// Layered blend (per bone); has dynamic number of blendposes that can blend per different bone sets
|
||||
USTRUCT(BlueprintInternalUseOnly)
|
||||
struct FGMS_AnimNode_LayeredBoneBlend : public FAnimNode_Base
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
|
||||
public:
|
||||
/** The source pose */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Links)
|
||||
FPoseLink BasePose;
|
||||
|
||||
/** Each layer's blended pose */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, editfixedsize, Category=Links, meta=(BlueprintCompilerGeneratedDefaults))
|
||||
TArray<FPoseLink> BlendPoses;
|
||||
|
||||
/** Whether to use branch filters or a blend mask to specify an input pose per-bone influence */
|
||||
// UPROPERTY(EditAnywhere, Category = Config)
|
||||
// ELayeredBoneBlendMode BlendMode;
|
||||
|
||||
/**
|
||||
* The blend masks to use for our layer inputs. Allows the use of per-bone alphas.
|
||||
* Blend masks are used when BlendMode is BlendMask.
|
||||
*/
|
||||
// UPROPERTY(EditAnywhere, editfixedsize, Category=Config, meta=(UseAsBlendMask=true))
|
||||
// TArray<TObjectPtr<UBlendProfile>> BlendMasks;
|
||||
|
||||
/**
|
||||
* Configuration for the parts of the skeleton to blend for each layer. Allows
|
||||
* certain parts of the tree to be blended out or omitted from the pose.
|
||||
* LayerSetup is used when BlendMode is BranchFilter.
|
||||
*/
|
||||
UPROPERTY()
|
||||
TArray<FInputBlendPose> LayerSetup;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta=(PinShownByDefault), Category=GMS)
|
||||
FGMS_InputBlendPose ExternalLayerSetup;
|
||||
|
||||
/** The weights of each layer */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, editfixedsize, Category=GMS, meta=(BlueprintCompilerGeneratedDefaults, PinShownByDefault))
|
||||
TArray<float> BlendWeights;
|
||||
|
||||
/** Whether to blend bone rotations in mesh space or in local space */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Config, meta=(PinShownByDefault))
|
||||
bool bMeshSpaceRotationBlend;
|
||||
|
||||
/** Whether to blend bone scales in mesh space or in local space */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Config)
|
||||
bool bMeshSpaceScaleBlend;
|
||||
|
||||
/** How to blend the layers together */
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Config)
|
||||
TEnumAsByte<enum ECurveBlendOption::Type> CurveBlendOption;
|
||||
|
||||
/** Whether to incorporate the per-bone blend weight of the root bone when lending root motion */
|
||||
UPROPERTY(EditAnywhere, Category = Config)
|
||||
bool bBlendRootMotionBasedOnRootBone;
|
||||
|
||||
bool bHasRelevantPoses;
|
||||
|
||||
/*
|
||||
* Max LOD that this node is allowed to run
|
||||
* For example if you have LODThreshold to be 2, it will run until LOD 2 (based on 0 index)
|
||||
* when the component LOD becomes 3, it will stop update/evaluate
|
||||
* currently transition would be issue and that has to be re-visited
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Performance, meta = (DisplayName = "LOD Threshold"))
|
||||
int32 LODThreshold;
|
||||
|
||||
protected:
|
||||
// Per-bone weights for the skeleton. Serialized as these are only relative to the skeleton, but can potentially
|
||||
// be regenerated at runtime if the GUIDs dont match
|
||||
UPROPERTY()
|
||||
TArray<FPerBoneBlendWeight> PerBoneBlendWeights;
|
||||
|
||||
// Guids for skeleton used to determine whether the PerBoneBlendWeights need rebuilding
|
||||
UPROPERTY()
|
||||
FGuid SkeletonGuid;
|
||||
|
||||
// Guid for virtual bones used to determine whether the PerBoneBlendWeights need rebuilding
|
||||
UPROPERTY()
|
||||
FGuid VirtualBoneGuid;
|
||||
|
||||
// transient data to handle weight and target weight
|
||||
// this array changes based on required bones
|
||||
TArray<FPerBoneBlendWeight> DesiredBoneBlendWeights;
|
||||
TArray<FPerBoneBlendWeight> CurrentBoneBlendWeights;
|
||||
|
||||
// Per-curve source pose index
|
||||
TBaseBlendedCurve<FDefaultAllocator, UE::Anim::FCurveElementIndexed> CurvePoseSourceIndices;
|
||||
|
||||
// Serial number of the required bones container
|
||||
uint16 RequiredBonesSerialNumber;
|
||||
|
||||
public:
|
||||
FGMS_AnimNode_LayeredBoneBlend()
|
||||
: bMeshSpaceRotationBlend(false)
|
||||
, bMeshSpaceScaleBlend(false)
|
||||
, CurveBlendOption(ECurveBlendOption::Override)
|
||||
, bBlendRootMotionBasedOnRootBone(true)
|
||||
, bHasRelevantPoses(false)
|
||||
, LODThreshold(INDEX_NONE)
|
||||
, RequiredBonesSerialNumber(0)
|
||||
{
|
||||
}
|
||||
|
||||
// FAnimNode_Base interface
|
||||
GENERICMOVEMENTSYSTEM_API virtual void Initialize_AnyThread(const FAnimationInitializeContext& Context) override;
|
||||
GENERICMOVEMENTSYSTEM_API virtual void CacheBones_AnyThread(const FAnimationCacheBonesContext& Context) override;
|
||||
GENERICMOVEMENTSYSTEM_API virtual void Update_AnyThread(const FAnimationUpdateContext& Context) override;
|
||||
GENERICMOVEMENTSYSTEM_API virtual void Evaluate_AnyThread(FPoseContext& Output) override;
|
||||
GENERICMOVEMENTSYSTEM_API virtual void GatherDebugData(FNodeDebugData& DebugData) override;
|
||||
virtual int32 GetLODThreshold() const override { return LODThreshold; }
|
||||
// End of FAnimNode_Base interface
|
||||
|
||||
void AddPose()
|
||||
{
|
||||
BlendWeights.Add(1.f);
|
||||
BlendPoses.AddDefaulted();
|
||||
LayerSetup.AddDefaulted();
|
||||
}
|
||||
|
||||
void RemovePose(int32 PoseIndex)
|
||||
{
|
||||
BlendWeights.RemoveAt(PoseIndex);
|
||||
BlendPoses.RemoveAt(PoseIndex);
|
||||
|
||||
if (LayerSetup.IsValidIndex(PoseIndex))
|
||||
{
|
||||
LayerSetup.RemoveAt(PoseIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// Invalidate the cached per-bone blend weights from the skeleton
|
||||
void InvalidatePerBoneBlendWeights()
|
||||
{
|
||||
RequiredBonesSerialNumber = 0;
|
||||
SkeletonGuid = FGuid();
|
||||
VirtualBoneGuid = FGuid();
|
||||
}
|
||||
|
||||
// Invalidates the cached bone data so it is recalculated the next time this node is updated
|
||||
void InvalidateCachedBoneData() { RequiredBonesSerialNumber = 0; }
|
||||
|
||||
public:
|
||||
// Rebuild cache per bone blend weights from the skeleton
|
||||
GENERICMOVEMENTSYSTEM_API void RebuildPerBoneBlendWeights(const USkeleton* InSkeleton);
|
||||
|
||||
// Check whether per-bone blend weights are valid according to the skeleton (GUID check)
|
||||
GENERICMOVEMENTSYSTEM_API bool ArePerBoneBlendWeightsValid(const USkeleton* InSkeleton) const;
|
||||
|
||||
// Update cached data if required
|
||||
GENERICMOVEMENTSYSTEM_API void UpdateCachedBoneData(const FBoneContainer& RequiredBones, const USkeleton* Skeleton);
|
||||
|
||||
friend class UAnimGraphNode_LayeredBoneBlend;
|
||||
};
|
||||
@@ -0,0 +1,205 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BoneControllers/AnimNode_OrientationWarping.h"
|
||||
#include "BoneControllers/BoneControllerTypes.h"
|
||||
#include "BoneControllers/AnimNode_SkeletalControlBase.h"
|
||||
#include "Settings/GMS_SettingStructLibrary.h"
|
||||
#include "GMS_AnimNode_OrientationWarping.generated.h"
|
||||
|
||||
struct FAnimationInitializeContext;
|
||||
struct FComponentSpacePoseContext;
|
||||
struct FNodeDebugData;
|
||||
|
||||
USTRUCT(BlueprintInternalUseOnly)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimNode_OrientationWarping : public FAnimNode_SkeletalControlBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// Orientation warping evaluation mode (Graph or Manual)
|
||||
UPROPERTY(EditAnywhere,BlueprintReadWrite, Category=Evaluation, meta=(PinShownByDefault))
|
||||
EWarpingEvaluationMode Mode = EWarpingEvaluationMode::Manual;
|
||||
|
||||
// The desired orientation angle (in degrees) to warp by relative to the specified RotationAxis
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Evaluation, meta=(PinShownByDefault))
|
||||
float OrientationAngle = 0.f;
|
||||
|
||||
// The character locomotion angle (in degrees) relative to the specified RotationAxis
|
||||
// This will be used in the following equation for computing the orientation angle: [Orientation = RotationBetween(RootMotionDirection, LocomotionDirection)]
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Evaluation, meta=(PinShownByDefault))
|
||||
float LocomotionAngle = 0.f;
|
||||
|
||||
// The character movement direction vector in world space
|
||||
// This will be used to compute LocomotionAngle automatically
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Evaluation, meta=(PinShownByDefault))
|
||||
FVector LocomotionDirection = { 0.f, 0.f, 0.f };
|
||||
|
||||
// Minimum root motion speed required to apply orientation warping
|
||||
// This is useful to prevent unnatural re-orientation when the animation has a portion with no root motion (i.e starts/stops/idles)
|
||||
// When this value is greater than 0, it's recommended to enable interpolation with RotationInterpSpeed > 0
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Evaluation, meta = (ClampMin = "0.0", PinHiddenByDefault))
|
||||
float MinRootMotionSpeedThreshold = 10.0f;
|
||||
|
||||
// Specifies an angle threshold to prevent erroneous over-rotation of the character, disabled with a value of 0
|
||||
//
|
||||
// When the effective orientation warping angle is detected to be greater than this value (default: 90 degrees) the locomotion direction will be inverted prior to warping
|
||||
// This will be used in the following equation: [Orientation = RotationBetween(RootMotionDirection, -LocomotionDirection)]
|
||||
//
|
||||
// Example: Playing a forward running animation while the motion is going backward
|
||||
// Rather than orientation warping by 180 degrees, the system will warp by 0 degrees
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Evaluation, meta=(PinHiddenByDefault), meta=(ClampMin="0.0", ClampMax="180.0"))
|
||||
float LocomotionAngleDeltaThreshold = 90.f;
|
||||
|
||||
// Spine bone definitions
|
||||
// Used to counter rotate the body in order to keep the character facing forward
|
||||
// The amount of counter rotation applied is driven by DistributedBoneOrientationAlpha
|
||||
// UPROPERTY(EditAnywhere, Category="Settings") 由ExternalBoneReference代替代替。
|
||||
UPROPERTY()
|
||||
TArray<FBoneReference> SpineBones;
|
||||
|
||||
// IK Foot Root Bone definition
|
||||
// UPROPERTY(EditAnywhere, Category="Settings", meta=(DisplayName="IK Foot Root Bone")) 由ExternalBoneReference代替。
|
||||
UPROPERTY()
|
||||
FBoneReference IKFootRootBone;
|
||||
|
||||
// IK Foot definitions
|
||||
// UPROPERTY(EditAnywhere, Category="Settings", meta=(DisplayName="IK Foot Bones")) 由ExternalBoneReference代替。
|
||||
UPROPERTY()
|
||||
TArray<FBoneReference> IKFootBones;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category= Settings, meta=(PinShownByDefault))
|
||||
FGMS_OrientationWarpingBoneReference ExternalBoneReference;
|
||||
|
||||
// Rotation axis used when rotating the character body
|
||||
UPROPERTY(EditAnywhere, Category=Settings)
|
||||
TEnumAsByte<EAxis::Type> RotationAxis = EAxis::Z;
|
||||
|
||||
// Specifies how much rotation is applied to the character body versus IK feet
|
||||
UPROPERTY(EditAnywhere, Category=Settings, meta=(ClampMin="0.0", ClampMax="1.0", PinHiddenByDefault))
|
||||
float DistributedBoneOrientationAlpha = 0.5f;
|
||||
|
||||
// Specifies the interpolation speed (in Alpha per second) towards reaching the final warped rotation angle
|
||||
// A value of 0 will cause instantaneous rotation, while a greater value will introduce smoothing
|
||||
UPROPERTY(EditAnywhere, Category=Settings, meta=(ClampMin="0.0"))
|
||||
float RotationInterpSpeed = 10.f;
|
||||
|
||||
// Max correction we're allowed to do per-second when using interpolation.
|
||||
// This minimizes pops when we have a large difference between current and target orientation.
|
||||
UPROPERTY(EditAnywhere, Category=Settings, meta=(ClampMin="0.0", EditCondition="RotationInterpSpeed > 0.0f"))
|
||||
float MaxCorrectionDegrees = 180.f;
|
||||
|
||||
// Don't compensate our interpolator when the instantaneous root motion delta is higher than this. This is likely a pivot.
|
||||
UPROPERTY(EditAnywhere, Category=Settings, meta=(ClampMin="0.0", EditCondition="RotationInterpSpeed > 0.0f"))
|
||||
float MaxRootMotionDeltaToCompensateDegrees = 45.f;
|
||||
|
||||
// Whether to counter compensate interpolation by the animated root motion angle change over time.
|
||||
// This helps to conserve the motion from our animation.
|
||||
// Disable this if your root motion is expected to be jittery, and you want orientation warping to smooth it out.
|
||||
UPROPERTY(EditAnywhere, Category=Settings, meta=(EditCondition="RotationInterpSpeed > 0.0f"))
|
||||
bool bCounterCompenstateInterpolationByRootMotion = true;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category=Experimental, meta=(PinHiddenByDefault))
|
||||
bool bScaleByGlobalBlendWeight = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category=Experimental, meta=(PinHiddenByDefault))
|
||||
bool bUseManualRootMotionVelocity = false;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Experimental, meta=(PinHiddenByDefault))
|
||||
FVector ManualRootMotionVelocity = FVector::ZeroVector;
|
||||
|
||||
//RootBoneTransform is the same as CustomTransform as
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Evaluation, meta=(PinHiddenByDefault))
|
||||
EOrientationWarpingSpace WarpingSpace = EOrientationWarpingSpace::ComponentTransform;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Evaluation, meta=(PinHiddenByDefault))
|
||||
FTransform WarpingSpaceTransform;
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
// Scale all debug drawing visualization by a factor
|
||||
UPROPERTY(EditAnywhere, Category=Debug, meta=(ClampMin="0.0"))
|
||||
float DebugDrawScale = 1.f;
|
||||
|
||||
// Enable/Disable orientation warping debug drawing
|
||||
UPROPERTY(EditAnywhere, Category=Debug)
|
||||
bool bEnableDebugDraw = false;
|
||||
#endif
|
||||
|
||||
public:
|
||||
// FAnimNode_Base interface
|
||||
virtual void GatherDebugData(FNodeDebugData& DebugData) override;
|
||||
virtual void UpdateInternal(const FAnimationUpdateContext& Context) override;
|
||||
// End of FAnimNode_Base interface
|
||||
|
||||
// FAnimNode_SkeletalControlBase interface
|
||||
virtual void Initialize_AnyThread(const FAnimationInitializeContext& Context) override;
|
||||
virtual void EvaluateSkeletalControl_AnyThread(FComponentSpacePoseContext& Output, TArray<FBoneTransform>& OutBoneTransforms) override;
|
||||
virtual bool IsValidToEvaluate(const USkeleton* Skeleton, const FBoneContainer& RequiredBones) override;
|
||||
// End of FAnimNode_SkeletalControlBase interface
|
||||
|
||||
private:
|
||||
// FAnimNode_SkeletalControlBase interface
|
||||
virtual void InitializeBoneReferences(const FBoneContainer& RequiredBones) override;
|
||||
// End of FAnimNode_SkeletalControlBase interface
|
||||
|
||||
struct FOrientationWarpingSpineBoneData
|
||||
{
|
||||
FCompactPoseBoneIndex BoneIndex;
|
||||
float Weight;
|
||||
|
||||
FOrientationWarpingSpineBoneData()
|
||||
: BoneIndex(INDEX_NONE)
|
||||
, Weight(0.f)
|
||||
{
|
||||
}
|
||||
|
||||
FOrientationWarpingSpineBoneData(FCompactPoseBoneIndex InBoneIndex)
|
||||
: BoneIndex(InBoneIndex)
|
||||
, Weight(0.f)
|
||||
{
|
||||
}
|
||||
|
||||
// Comparison Operator for Sorting
|
||||
struct FCompareBoneIndex
|
||||
{
|
||||
FORCEINLINE bool operator()(const FOrientationWarpingSpineBoneData& A, const FOrientationWarpingSpineBoneData& B) const
|
||||
{
|
||||
return A.BoneIndex < B.BoneIndex;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
struct FOrientationWarpingFootData
|
||||
{
|
||||
TArray<FCompactPoseBoneIndex> IKFootBoneIndexArray;
|
||||
FCompactPoseBoneIndex IKFootRootBoneIndex;
|
||||
|
||||
FOrientationWarpingFootData()
|
||||
: IKFootBoneIndexArray()
|
||||
, IKFootRootBoneIndex(INDEX_NONE)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Computed spine bone indices and alpha weights for the specified spine definition
|
||||
TArray<FOrientationWarpingSpineBoneData> SpineBoneDataArray;
|
||||
|
||||
// Computed IK bone indices for the specified foot definitions
|
||||
FOrientationWarpingFootData IKFootData;
|
||||
|
||||
// Internal current frame root motion delta direction
|
||||
FVector RootMotionDeltaDirection = FVector::ZeroVector;
|
||||
|
||||
// Internal orientation warping angle
|
||||
float ActualOrientationAngleRad = 0.f;
|
||||
float BlendWeight = 0.0f;
|
||||
|
||||
FGraphTraversalCounter UpdateCounter;
|
||||
bool bIsFirstUpdate = false;
|
||||
void Reset(const FAnimationBaseContext& Context);
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
// Whether we found a root motion delta attribute in the attribute stream on graph driven mode
|
||||
bool bFoundRootMotionAttribute = false;
|
||||
#endif
|
||||
};
|
||||
@@ -0,0 +1,78 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/Object.h"
|
||||
#include "GMS_SettingEnumLibrary.generated.h"
|
||||
|
||||
/**
|
||||
* Defines rotation behavior when the character is in the air.
|
||||
* 定义角色在空中的旋转行为。
|
||||
*/
|
||||
UENUM(BlueprintType)
|
||||
enum class EGMS_InAirRotationMode : uint8
|
||||
{
|
||||
// Rotate to velocity direction on jump. 在跳跃时旋转到速度方向。
|
||||
RotateToVelocityOnJump,
|
||||
// Maintain relative rotation. 保持相对旋转。
|
||||
KeepRelativeRotation,
|
||||
// Maintain world rotation. 保持世界旋转。
|
||||
KeepWorldRotation
|
||||
};
|
||||
|
||||
/**
|
||||
* Defines how turn-in-place animations are triggered.
|
||||
* 定义如何触发原地转向动画。
|
||||
*/
|
||||
UENUM(BlueprintType)
|
||||
enum class EGMS_TurnInPlacePlayMethod : uint8
|
||||
{
|
||||
Graph, // Trigger turn-in-place in animation graph. 在动画图中触发原地转向。
|
||||
Montage // Trigger turn-in-place as a dynamic slot montage. 作为动态槽蒙太奇触发原地转向。
|
||||
};
|
||||
|
||||
/**
|
||||
* Defines how overlay animations are played.
|
||||
* 定义如何播放覆盖动画。
|
||||
*/
|
||||
UENUM(BlueprintType)
|
||||
enum class EGMS_OverlayPlayMode : uint8
|
||||
{
|
||||
SequencePlayer, // Play as a sequence player. 作为序列播放器播放。
|
||||
SequenceEvaluator // Play as a sequence evaluator. 作为序列评估器播放。
|
||||
};
|
||||
|
||||
/**
|
||||
* Defines the blending mode for layered bone animations.
|
||||
* 定义分层骨骼动画的混合模式。
|
||||
*/
|
||||
UENUM(BlueprintType)
|
||||
enum class EGMS_LayeredBoneBlendMode : uint8
|
||||
{
|
||||
BranchFilter, // Use branch filter for blending. 使用分支过滤器进行混合。
|
||||
BlendMask // Use blend mask for blending. 使用混合蒙版进行混合。
|
||||
};
|
||||
|
||||
/**
|
||||
* Defines how velocity direction is determined.
|
||||
* 定义如何确定速度方向。
|
||||
*/
|
||||
UENUM(BlueprintType)
|
||||
enum class EGMS_VelocityDirectionMode_DEPRECATED : uint8
|
||||
{
|
||||
OrientToLastVelocityDirection, // Orient to the last velocity direction. 朝向最后的速度方向。
|
||||
OrientToInputDirection, // Orient to the input direction. 朝向输入方向。
|
||||
TurningCircle // Use a turning circle for orientation. 使用转向圆进行朝向。
|
||||
};
|
||||
|
||||
/**
|
||||
* Defines view direction modes.
|
||||
* 定义视图方向模式。
|
||||
*/
|
||||
UENUM(BlueprintType)
|
||||
enum class EGMS_ViewDirectionMode_DEPRECATED : uint8
|
||||
{
|
||||
Default, // Default view direction mode. 默认视图方向模式。
|
||||
Aiming // Aiming view direction mode. 瞄准视图方向模式。
|
||||
};
|
||||
@@ -0,0 +1,221 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GMS_SettingStructLibrary.h"
|
||||
#include "UObject/Object.h"
|
||||
#include "Engine/DataAsset.h"
|
||||
#include "Runtime/Launch/Resources/Version.h"
|
||||
#if ENGINE_MINOR_VERSION < 5
|
||||
#include "InstancedStruct.h"
|
||||
#else
|
||||
#include "StructUtils/InstancedStruct.h"
|
||||
#endif
|
||||
#include "Utility/GMS_Tags.h"
|
||||
#include "GMS_SettingObjectLibrary.generated.h"
|
||||
|
||||
#pragma region CommonSettings
|
||||
|
||||
class UGMS_AnimLayer;
|
||||
class UGMS_AnimLayerSetting;
|
||||
|
||||
/**
|
||||
* Defines multiple movement set settings for a character.
|
||||
* 定义角色的多个运动集设置。
|
||||
*/
|
||||
UCLASS(BlueprintType, Const)
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_MovementDefinition : public UDataAsset
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Map of gameplay tags to movement set settings.
|
||||
* 游戏标签到运动集设置的映射。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS", meta=(ForceInlineRow))
|
||||
TMap<FGameplayTag, FGMS_MovementSetSetting> MovementSets;
|
||||
|
||||
#if WITH_EDITOR
|
||||
/**
|
||||
* Called before saving the asset in the editor.
|
||||
* 在编辑器中保存资产前调用。
|
||||
* @param SaveContext The save context. 保存上下文。
|
||||
*/
|
||||
virtual void PreSave(FObjectPreSaveContext SaveContext) override;
|
||||
|
||||
/**
|
||||
* Validates data in the editor.
|
||||
* 在编辑器中验证数据。
|
||||
* @param Context The validation context. 验证上下文。
|
||||
* @return The validation result. 验证结果。
|
||||
*/
|
||||
virtual EDataValidationResult IsDataValid(class FDataValidationContext& Context) const override;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores animation graph-specific settings, one per unique skeleton.
|
||||
* 存储动画图特定的设置,每个唯一骨架一个。
|
||||
*/
|
||||
UCLASS()
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_AnimGraphSetting : public UDataAsset
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Maps animation layer settings to their corresponding animation layer implementations.
|
||||
* 将动画层设置映射到对应的动画层实现。
|
||||
* @note Add custom animation layer settings/implementations to this mapping. 将自定义动画层设置/实现添加到此映射。
|
||||
*/
|
||||
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Settings")
|
||||
TMap<TSubclassOf<UGMS_AnimLayerSetting>, TSubclassOf<UGMS_AnimLayer>> AnimLayerSettingToInstanceMapping;
|
||||
|
||||
/**
|
||||
* Settings for orientation warping.
|
||||
* 朝向扭曲的设置。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Settings")
|
||||
FGMS_OrientationWarpingSettings OrientationWarping;
|
||||
};
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region ControlSettings
|
||||
|
||||
/**
|
||||
* Default Movement control settings.
|
||||
* 默认运动控制设置。
|
||||
*/
|
||||
UCLASS(BlueprintType, Blueprintable, Const, DisplayName="GMS Movement Control Setting")
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_MovementControlSetting_Default : public UDataAsset
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
/**
|
||||
* Matches a speed to a movement state tag based on a threshold.
|
||||
* 根据阈值将速度匹配到运动状态标签。
|
||||
* @param Speed The current speed. 当前速度。
|
||||
* @param Threshold The speed threshold. 速度阈值。
|
||||
* @return The matching gameplay tag. 匹配的游戏标签。
|
||||
*/
|
||||
FGameplayTag MatchStateTagBySpeed(float Speed, float Threshold) const;
|
||||
|
||||
/**
|
||||
* Gets a movement state setting by index.
|
||||
* 通过索引获取运动状态设置。
|
||||
* @param Index The index of the state. 状态索引。
|
||||
* @param OutSetting The output movement state setting. 输出的运动状态设置。
|
||||
* @return True if found. 如果找到返回true。
|
||||
*/
|
||||
bool GetStateByIndex(const int32& Index, FGMS_MovementStateSetting& OutSetting) const;
|
||||
|
||||
/**
|
||||
* Gets a movement state setting by speed level.
|
||||
* 通过速度级别获取运动状态设置。
|
||||
* @param Level The speed level. 速度级别。
|
||||
* @param OutSetting The output movement state setting. 输出的运动状态设置。
|
||||
* @return True if found. 如果找到返回true。
|
||||
*/
|
||||
bool GetStateBySpeedLevel(const int32& Level, FGMS_MovementStateSetting& OutSetting) const;
|
||||
|
||||
/**
|
||||
* Gets a movement state setting by tag.
|
||||
* 通过标签获取运动状态设置。
|
||||
* @param Tag The gameplay tag. 游戏标签。
|
||||
* @param OutSetting The output movement state setting. 输出的运动状态设置。
|
||||
* @return True if found. 如果找到返回true。
|
||||
*/
|
||||
bool GetStateByTag(const FGameplayTag& Tag, FGMS_MovementStateSetting& OutSetting) const;
|
||||
|
||||
const FGMS_MovementStateSetting* GetMovementStateSetting(const FGameplayTag& Tag) const;
|
||||
|
||||
const FGMS_MovementStateSetting* GetMovementStateSetting(const FGameplayTag& Tag, bool bHasFallback) const;
|
||||
|
||||
/**
|
||||
* Speed threshold for determining movement.
|
||||
* 确定移动的速度阈值。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Control", Meta = (ClampMin = 0, ForceUnits = "cm/s"))
|
||||
float MovingSpeedThreshold{50.0f};
|
||||
|
||||
/**
|
||||
* Array of movement states, sorted by speed.
|
||||
* 按速度排序的运动状态数组。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Control", meta=(TitleProperty="EditorFriendlyName"))
|
||||
TArray<FGMS_MovementStateSetting> MovementStates;
|
||||
|
||||
/**
|
||||
* Setting for velocity based rotation mode.
|
||||
* 基于速率的旋转模式设置。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="RotationControl", meta=(ExcludeBaseStruct))
|
||||
TInstancedStruct<FGMS_VelocityDirectionSetting_Base> VelocityDirectionSetting;
|
||||
|
||||
/**
|
||||
* Setting for view based rotation mode.
|
||||
* 基于视角的旋转模式设置。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="RotationControl")
|
||||
TInstancedStruct<FGMS_ViewDirectionSetting_Base> ViewDirectionSetting;
|
||||
|
||||
/**
|
||||
* Rotation mode when in air.
|
||||
* 空中时的旋转模式。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="RotationControl")
|
||||
EGMS_InAirRotationMode InAirRotationMode{EGMS_InAirRotationMode::KeepRelativeRotation};
|
||||
|
||||
/**
|
||||
* Interpolation speed for in-air rotation.
|
||||
* 空中旋转的插值速度。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="RotationControl", meta=(ClampMin=0))
|
||||
float InAirRotationInterpolationSpeed = 5.0f;
|
||||
|
||||
/**
|
||||
* Maps speed levels to movement state indices.
|
||||
* 将速度级别映射到运动状态索引。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="Advanced", meta=(EditCondition=false, ForceInlineRow))
|
||||
TMap<int32, int32> SpeedLevelToArrayIndex;
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
|
||||
float MigrateRotationInterpolationSpeed(float Old);
|
||||
/**
|
||||
* Called before saving the asset in the editor.
|
||||
* 在编辑器中保存资产前调用。
|
||||
* @param SaveContext The save context. 保存上下文。
|
||||
*/
|
||||
virtual void PreSave(FObjectPreSaveContext SaveContext) override;
|
||||
|
||||
/**
|
||||
* Validates data in the editor.
|
||||
* 在编辑器中验证数据。
|
||||
* @param Context The validation context. 验证上下文。
|
||||
* @return The validation result. 验证结果。
|
||||
*/
|
||||
virtual EDataValidationResult IsDataValid(class FDataValidationContext& Context) const override;
|
||||
#endif
|
||||
};
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region AnimSettings
|
||||
|
||||
/**
|
||||
* Base class for custom movement set user settings.
|
||||
* 自定义运动集用户设置的基类。
|
||||
* @note Subclass to add custom settings without modifying movement set settings. 子类化以添加自定义设置,而无需修改运动集设置。
|
||||
*/
|
||||
UCLASS(BlueprintType, Blueprintable, Abstract, Const, EditInlineNew, DefaultToInstanced)
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_MovementSetUserSetting : public UObject
|
||||
{
|
||||
GENERATED_BODY()
|
||||
};
|
||||
#pragma endregion
|
||||
@@ -0,0 +1,871 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GMS_SettingEnumLibrary.h"
|
||||
#include "Animation/AnimData/BoneMaskFilter.h"
|
||||
#include "UObject/Object.h"
|
||||
#include "Curves/CurveFloat.h"
|
||||
#include "Engine/EngineTypes.h"
|
||||
#include "BoneControllers/BoneControllerTypes.h"
|
||||
#include "Utility/GMS_Tags.h"
|
||||
#include "GMS_SettingStructLibrary.generated.h"
|
||||
|
||||
class UGMS_MovementSetUserSetting;
|
||||
class UGMS_AnimLayerSetting_SkeletalControls;
|
||||
class UGMS_MovementControlSetting_Default;
|
||||
class UGMS_AnimLayerSetting_Additive;
|
||||
class UGMS_CharacterMovementSetting;
|
||||
class UGMS_CharacterRotationSetting;
|
||||
class UGMS_AnimLayerSetting_View;
|
||||
class UGMS_AnimLayerSetting_Overlay;
|
||||
class UGMS_AnimLayerSetting_StateOverlays;
|
||||
class UGMS_AnimLayerSetting_States;
|
||||
|
||||
/**
|
||||
* Stores bone references for orientation warping.
|
||||
* 存储用于朝向扭曲的骨骼引用。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_OrientationWarpingBoneReference
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
|
||||
/**
|
||||
* Spine bones used to counter-rotate the body to keep facing forward.
|
||||
* 用于反向旋转身体以保持向前朝向的脊椎骨骼。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
TArray<FBoneReference> SpineBones;
|
||||
|
||||
/**
|
||||
* IK foot root bone definition.
|
||||
* IK脚根骨骼定义。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
FBoneReference IKFootRootBone;
|
||||
|
||||
/**
|
||||
* IK foot bone definitions.
|
||||
* IK脚骨骼定义。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
TArray<FBoneReference> IKFootBones;
|
||||
};
|
||||
|
||||
/**
|
||||
* Settings for the orientation warping node in the animation graph.
|
||||
* 动画图中朝向扭曲节点的设置。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_OrientationWarpingSettings
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Evaluation mode for orientation warping (Graph or Manual).
|
||||
* 朝向扭曲的评估模式(Graph或Manual)。
|
||||
* @note Use Manual mode for animations without root motion. 对于无根运动的动画使用Manual模式。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
EWarpingEvaluationMode Mode = EWarpingEvaluationMode::Graph;
|
||||
|
||||
/**
|
||||
* Bone references for orientation warping.
|
||||
* 朝向扭曲的骨骼引用。
|
||||
*/
|
||||
UPROPERTY(BlueprintReadOnly, EditAnywhere, Category="GMS")
|
||||
FGMS_OrientationWarpingBoneReference BoneReference;
|
||||
|
||||
/**
|
||||
* Specifies how much rotation is applied to the body versus IK feet.
|
||||
* 指定身体与IK脚的旋转分配比例。
|
||||
*/
|
||||
UPROPERTY(BlueprintReadOnly, EditAnywhere, Category="GMS", meta=(ClampMin="0.0", ClampMax="1.0"))
|
||||
float DistributedBoneOrientationAlpha = 0.75f;
|
||||
|
||||
/**
|
||||
* Interpolation speed for reaching the final warped rotation angle (alpha per second).
|
||||
* 达到最终扭曲旋转角度的插值速度(每秒alpha)。
|
||||
* @note 0 means instantaneous rotation; higher values introduce smoothing. 0表示瞬时旋转;较高值引入平滑。
|
||||
*/
|
||||
UPROPERTY(BlueprintReadOnly, EditAnywhere, Category="GMS", meta=(ClampMin="0.0"))
|
||||
float RotationInterpSpeed = 10.f;
|
||||
};
|
||||
|
||||
/**
|
||||
* Bone references for stride warping.
|
||||
* 步幅适配的骨骼引用。
|
||||
*/
|
||||
USTRUCT()
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_StrideWarpingBoneReference
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Pelvis bone reference.
|
||||
* 骨盆骨骼引用。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
FBoneReference PelvisBone;
|
||||
|
||||
/**
|
||||
* IK foot root bone reference.
|
||||
* IK脚根骨骼引用。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
FBoneReference IKFootRootBone;
|
||||
};
|
||||
|
||||
/**
|
||||
* Foot bone definitions for stride warping.
|
||||
* 步幅适配的脚骨骼定义。
|
||||
*/
|
||||
USTRUCT()
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_StrideWarpingFootDefinition
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* IK foot bone.
|
||||
* IK脚骨骼。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
FBoneReference IKFootBone;
|
||||
|
||||
/**
|
||||
* FK foot bone.
|
||||
* FK脚骨骼。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
FBoneReference FKFootBone;
|
||||
|
||||
/**
|
||||
* Thigh bone.
|
||||
* 大腿骨骼。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="GMS")
|
||||
FBoneReference ThighBone;
|
||||
};
|
||||
|
||||
/**
|
||||
* Settings for the stride warping node in the animation graph.
|
||||
* 动画图中步幅适配节点的设置。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_StrideWarpingSettings
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Enables or disables stride warping.
|
||||
* 启用或禁用步幅适配。
|
||||
* @note Disable for non-humanoid characters. 对于非人形角色禁用。
|
||||
*/
|
||||
UPROPERTY(BlueprintReadOnly, EditAnywhere, Category="GMS")
|
||||
bool bEnabled{true};
|
||||
|
||||
/**
|
||||
* Start time for blending in stride warping.
|
||||
* 步幅适配混入的开始时间。
|
||||
* @note For animations with turns, set to when the character moves in a straight line. 对于带转身的动画,设置为角色直线移动的时刻。
|
||||
*/
|
||||
UPROPERTY(BlueprintReadOnly, EditAnywhere, Category="GMS", meta=(ClampMin=0))
|
||||
float BlendInStartOffset{0.15f};
|
||||
|
||||
/**
|
||||
* Duration for fully blending in stride warping.
|
||||
* 步幅适配完全混入的持续时间。
|
||||
*/
|
||||
UPROPERTY(BlueprintReadOnly, EditAnywhere, Category="GMS")
|
||||
float BlendInDurationScaled{0.2f};
|
||||
};
|
||||
|
||||
/**
|
||||
* Settings for the steering node in the animation graph.
|
||||
* 动画图中Steering节点的设置。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_SteeringSettings
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
* 默认构造函数。
|
||||
*/
|
||||
FGMS_SteeringSettings()
|
||||
{
|
||||
bEnabled = true;
|
||||
ProceduralTargetTime = 0.2f;
|
||||
AnimatedTargetTime = 0.5f;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with parameters.
|
||||
* 带参数的构造函数。
|
||||
* @param bInEnabled Whether steering is enabled. 是否启用Steering。
|
||||
* @param InProceduralTargetTime Procedural target time. 程序化目标时间。
|
||||
* @param InAnimatedTargetTime Animated target time. 动画目标时间。
|
||||
*/
|
||||
FGMS_SteeringSettings(bool bInEnabled, float InProceduralTargetTime, float InAnimatedTargetTime)
|
||||
{
|
||||
bEnabled = bInEnabled;
|
||||
ProceduralTargetTime = InProceduralTargetTime;
|
||||
AnimatedTargetTime = InAnimatedTargetTime;
|
||||
};
|
||||
|
||||
/**
|
||||
* Enables or disables steering.
|
||||
* 启用或禁用Steering。
|
||||
*/
|
||||
UPROPERTY(BlueprintReadOnly, EditAnywhere, Category="GMS")
|
||||
bool bEnabled{true};
|
||||
|
||||
/**
|
||||
* Time to reach target orientation for animations without root motion rotation.
|
||||
* 无根运动旋转动画达到目标朝向的时间。
|
||||
* @note Large values disable procedural turns. 大值禁用程序化转身。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
float ProceduralTargetTime = 0.2f;
|
||||
|
||||
/**
|
||||
* Time to reach target orientation for animations with root motion rotation.
|
||||
* 有根运动旋转动画达到目标朝向的时间。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
float AnimatedTargetTime = 0.5f;
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrapper for branch filters used in blueprint.
|
||||
* 用于蓝图的分支过滤器包装器。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_InputBlendPose
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
|
||||
/**
|
||||
* Array of bone branch filters.
|
||||
* 骨骼分支过滤器数组。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category=Filter)
|
||||
TArray<FBranchFilter> BranchFilters;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* General settings for the main animation instance.
|
||||
* 主动画实例的通用设置。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_AnimDataSetting_General
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Allows the pawn's root bone to rotate independently of the actor's rotation.
|
||||
* 允许Pawn的根骨骼独立于Actor旋转进行旋转。
|
||||
* @note Global setting; animation layers should only change root rotation mode if enabled. 全局设置;动画层仅在启用时更改根旋转模式。
|
||||
* @details Enables complex rotational animations like turn-in-place via root motion. 通过根运动支持复杂的旋转动画,如原地转身。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Common")
|
||||
bool bEnableOffsetRootBoneRotation{true};
|
||||
|
||||
/**
|
||||
* Controls the speed of blending out root bone rotation offset.
|
||||
* 控制根骨骼旋转偏移混出的速度。
|
||||
* @note Closer to 0 is faster; 0 means instant blend out. 越接近0越快;0表示立即混出。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Common", Meta = (ClampMin = 0, ClampMax = 1, EditCondition="bEnableOffsetRootBoneRotation"))
|
||||
float RootBoneRotationHalfLife{0.2};
|
||||
|
||||
/**
|
||||
* Allows the pawn's root bone to translate independently of the actor's location.
|
||||
* 允许Pawn的根骨骼独立于Actor位置进行平移。
|
||||
* @note Global setting; animation layers should only change root translation mode if enabled. 全局设置;动画层仅在启用时更改根平移模式。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Common")
|
||||
bool bEnableOffsetRootBoneTranslation{true};
|
||||
|
||||
/**
|
||||
* Controls the speed of blending out root bone translation offset.
|
||||
* 控制根骨骼位移偏移混出的速度。
|
||||
* @note Closer to 0 is faster; 0 means instant blend out. 越接近0越快;0表示立即混出。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Common", Meta = (ClampMin = 0, ClampMax = 1, EditCondition="bEnableOffsetRootBoneTranslation"))
|
||||
float RootBoneTranslationHalfLife{0.2};
|
||||
|
||||
/**
|
||||
* Controls the speed of lean interpolation in grounded or in-air states.
|
||||
* 控制地面或空中状态下倾斜插值的速度。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Common", Meta = (ClampMin = 0))
|
||||
float LeanInterpolationSpeed{4.0f};
|
||||
|
||||
/**
|
||||
* Speed threshold for determining movement in animation states.
|
||||
* 动画状态中确定移动的速度阈值。
|
||||
*/
|
||||
// UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Common", Meta = (ClampMin = 0, ForceUnits = "cm/s"))
|
||||
// float MovingSmoothSpeedThreshold{150.0f};
|
||||
|
||||
/**
|
||||
* Curve mapping vertical velocity to lean amount in air.
|
||||
* 空中垂直速度到倾斜量的曲线映射。
|
||||
* @note If empty, lean state is not refreshed in air. 如果为空,空中不刷新倾斜状态。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="InAir")
|
||||
TObjectPtr<UCurveFloat> InAirLeanAmountCurve{nullptr};
|
||||
|
||||
/**
|
||||
* Collision channel for ground prediction sweep.
|
||||
* 地面预测扫描的碰撞通道。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="InAir")
|
||||
TEnumAsByte<ECollisionChannel> GroundPredictionSweepChannel{ECC_Visibility};
|
||||
|
||||
/**
|
||||
* Response channels for ground prediction.
|
||||
* 地面预测的响应通道。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="InAir")
|
||||
TArray<TEnumAsByte<ECollisionChannel>> GroundPredictionResponseChannels;
|
||||
|
||||
/**
|
||||
* Collision response container for ground prediction sweep.
|
||||
* 地面预测扫描的碰撞响应容器。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="InAir")
|
||||
FCollisionResponseContainer GroundPredictionSweepResponses{ECR_Ignore};
|
||||
|
||||
#if WITH_EDITOR
|
||||
/**
|
||||
* Handles property changes in the editor.
|
||||
* 处理编辑器中的属性更改。
|
||||
* @param PropertyChangedEvent The property change event. 属性更改事件。
|
||||
*/
|
||||
void PostEditChangeProperty(const FPropertyChangedEvent& PropertyChangedEvent);
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
#pragma region Rotation Mode Settings
|
||||
|
||||
USTRUCT(BlueprintType, meta=(Hidden))
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_RotationModeSetting
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Determines if turn-in-place animation follows actor rotation or drives it.
|
||||
* 确定原地转身动画是跟随Actor旋转还是驱动Actor旋转。
|
||||
* @note If enabled, animation catches up with actor rotation; if disabled, it drives rotation. 如果启用,动画跟随Actor旋转;如果禁用,动画驱动旋转。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
bool bEnableRotationWhenNotMoving{true};
|
||||
};
|
||||
|
||||
/**
|
||||
* Setting for view direction based rotation mode.
|
||||
* 基于视角方向的旋转模式设置
|
||||
*/
|
||||
USTRUCT(BlueprintType, meta=(Hidden))
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_ViewDirectionSetting_Base : public FGMS_RotationModeSetting
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Primary rotation interpolation speed for character's rotation.
|
||||
* 角色的主要旋转插值速度。
|
||||
* @details Smaller value means faster InterpolationSpeed. 值越小,插值速度越大。
|
||||
* @note <=0 disables smoothing. <=0禁用平滑。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", meta=(ClampMin=0))
|
||||
float RotationInterpolationSpeed = 0.1f;
|
||||
|
||||
/**
|
||||
* Secondary(Extras) rotation interpolation speed for character's rotation.
|
||||
* 角色的次要(额外)旋转插值速度。
|
||||
* @note <=0 disables smoothing <=0禁用平滑。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", meta=(ClampMin=0))
|
||||
float TargetYawAngleRotationSpeed = 800.0f;
|
||||
};
|
||||
|
||||
/**
|
||||
* Setting for deprecated view direction based rotation mode.
|
||||
* 基于弃用的视角方向的旋转模式设置
|
||||
*/
|
||||
USTRUCT(BlueprintType, meta=(Hidden))
|
||||
struct FGMS_ViewDirectionSetting : public FGMS_ViewDirectionSetting_Base
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* View direction mode (Default or Aiming).
|
||||
* 视图方向模式(默认或瞄准)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
EGMS_ViewDirectionMode_DEPRECATED DirectionMode{EGMS_ViewDirectionMode_DEPRECATED::Default};
|
||||
|
||||
/**
|
||||
* Determines if turn-in-place animation follows actor rotation or drives it.
|
||||
* 确定原地转身动画是跟随Actor旋转还是驱动Actor旋转。
|
||||
* @note If enabled, animation catches up with actor rotation; if disabled, it drives rotation. 如果启用,动画跟随Actor旋转;如果禁用,动画驱动旋转。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
bool bRotateToViewDirectionWhileNotMoving{true};
|
||||
|
||||
/**
|
||||
* Minimum angle limit between character and camera orientation in aiming mode.
|
||||
* 瞄准模式下角色与相机朝向的最小夹角限制。
|
||||
* @note Ensures character rotation keeps up with fast camera movement. 确保角色旋转跟上快速相机移动。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", meta=(EditCondition="DirectionMode == EGMS_ViewDirectionMode_DEPRECATED::Aiming", EditConditionHides))
|
||||
float MinAimingYawAngleLimit{90.0f};
|
||||
};
|
||||
|
||||
/**
|
||||
* Setting for default view direction based rotation mode.
|
||||
* 基于默认的视角方向的旋转模式设置
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_ViewDirectionSetting_Default : public FGMS_ViewDirectionSetting_Base
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Primary rotation interpolation speed curve for character's rotation.
|
||||
* 角色的主要旋转插值速度曲线。
|
||||
* @details This curve maps the speed level of each movement state to InterpolationSpeed, allowing different rotation rate on different movement state. 此曲线将每个运动状态的速度等级映射为插值速度,允许不同运动状态下有不同的旋转速度。
|
||||
* @note Only used when character is moving and have higher priority than "RotationInterpolationSpeed". 仅在角色移动时使用,比“RotationInterpolationSpeed”优先级更高。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", meta=(ClampMin=0))
|
||||
TObjectPtr<UCurveFloat> RotationInterpolationSpeedCurve{nullptr};
|
||||
};
|
||||
|
||||
/**
|
||||
* Setting for Aiming view direction based rotation mode.
|
||||
* 基于瞄准视角方向的旋转模式设置
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_ViewDirectionSetting_Aiming : public FGMS_ViewDirectionSetting_Base
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Minimum angle limit between character and camera orientation in aiming mode.
|
||||
* 瞄准模式下角色与相机朝向的最小夹角限制。
|
||||
* @note Ensures character rotation keeps up with fast camera movement. 确保角色旋转跟上快速相机移动。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
float MinAimingYawAngleLimit{90.0f};
|
||||
};
|
||||
|
||||
/**
|
||||
* Setting for velocity direction based rotation mode.
|
||||
* 基于速率方向的旋转模式设置
|
||||
* @note Velocity can come from input ,acceleration, or last movement velocity;
|
||||
*/
|
||||
USTRUCT(BlueprintType, meta=(Hidden))
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_VelocityDirectionSetting_Base : public FGMS_RotationModeSetting
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
// If checked, the character will rotate relative to the object it is standing on in the velocity
|
||||
// direction rotation mode, otherwise the character will ignore that object and keep its world rotation.
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Settings")
|
||||
bool bInheritBaseRotation{false};
|
||||
};
|
||||
|
||||
/**
|
||||
* Default setting for velocity direction based rotation mode.
|
||||
* 基于默认速率方向的旋转模式设置
|
||||
*/
|
||||
USTRUCT(BlueprintType, meta=(Hidden))
|
||||
struct FGMS_VelocityDirectionSetting : public FGMS_VelocityDirectionSetting_Base
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Velocity direction mode (e.g., orient to velocity or input).
|
||||
* 速度方向模式(例如,朝向速度或输入)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
EGMS_VelocityDirectionMode_DEPRECATED DirectionMode{EGMS_VelocityDirectionMode_DEPRECATED::OrientToLastVelocityDirection};
|
||||
|
||||
/**
|
||||
* Rotation rate for turning circle mode (degrees per second).
|
||||
* 转向圆模式的旋转速率(度每秒)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", meta=(EditCondition="DirectionMode == EGMS_VelocityDirectionMode_DEPRECATED::TurningCircle", EditConditionHides))
|
||||
float TurningRate{30};
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_VelocityDirectionSetting_Default : public FGMS_VelocityDirectionSetting_Base
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GMS")
|
||||
bool bOrientateToMoveInputIntent = false;
|
||||
|
||||
/**
|
||||
* If true, the actor will continue orienting towards the last intended orientation (from input) even after movement intent input has ceased.
|
||||
* This makes the character finish orienting after a quick stick flick from the player. If false, character will not turn without input.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GMS")
|
||||
bool bMaintainLastInputOrientation = false;
|
||||
|
||||
/**
|
||||
* Primary rotation interpolation speed for character's rotation.
|
||||
* 角色的主要旋转插值速度。
|
||||
* @details Smaller value means faster InterpolationSpeed. 值越小,插值速度越大。
|
||||
* @note <=0 disables smoothing. <=0禁用平滑。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", meta=(ClampMin=0))
|
||||
float RotationInterpolationSpeed = 0.1f;
|
||||
|
||||
/**
|
||||
* Primary rotation interpolation speed curve for character's rotation.
|
||||
* 角色的主要旋转插值速度曲线。
|
||||
* @details This curve maps the speed level of each movement state to InterpolationSpeed, allowing different rotation rate on different movement state. 此曲线将每个运动状态的速度等级映射为插值速度,允许不同运动状态下有不同的旋转速度。
|
||||
* @note Only used when character is moving and have higher priority than "RotationInterpolationSpeed". 仅在角色移动时使用,比“RotationInterpolationSpeed”优先级更高。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", meta=(ClampMin=0))
|
||||
TObjectPtr<UCurveFloat> RotationInterpolationSpeedCurve{nullptr};
|
||||
|
||||
/**
|
||||
* Secondary(Extras) moothing for character's rotation speed.
|
||||
* 角色旋转速度的次平滑。
|
||||
* @note <=0 disables smoothing <=0禁用平滑。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", meta=(ClampMin=0))
|
||||
float TargetYawAngleRotationSpeed = 800.0f;
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_VelocityDirectionSetting_RateBased : public FGMS_VelocityDirectionSetting_Base
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Rotation rate for turning(degrees per second).
|
||||
* 转向圆模式的旋转速率(度每秒)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
float TurnRate{30};
|
||||
|
||||
/**
|
||||
* Rotation rate for turning at different speed.
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", meta=(ClampMin=0))
|
||||
TObjectPtr<UCurveFloat> TurnRateSpeedCurve{nullptr};
|
||||
};
|
||||
|
||||
#pragma endregion
|
||||
|
||||
/**
|
||||
* Settings for in-air movement.
|
||||
* 空中移动的设置。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_InAirSetting
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Curve mapping vertical velocity to lean amount.
|
||||
* 垂直速度到倾斜量的曲线映射。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TObjectPtr<UCurveFloat> LeanAmountCurve{nullptr};
|
||||
|
||||
/**
|
||||
* Collision channel for ground prediction sweep.
|
||||
* 地面预测扫描的碰撞通道。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TEnumAsByte<ECollisionChannel> GroundPredictionSweepChannel{ECC_Visibility};
|
||||
|
||||
/**
|
||||
* Response channels for ground prediction.
|
||||
* 地面预测的响应通道。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TArray<TEnumAsByte<ECollisionChannel>> GroundPredictionResponseChannels;
|
||||
|
||||
/**
|
||||
* Collision response container for ground prediction sweep.
|
||||
* 地面预测扫描的碰撞响应容器。
|
||||
*/
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
FCollisionResponseContainer GroundPredictionSweepResponses{ECR_Ignore};
|
||||
};
|
||||
|
||||
#pragma region Movement State Settings
|
||||
|
||||
/**
|
||||
* Settings for a single movement state (e.g., walk, jog, sprint).
|
||||
* 单一运动状态的设置(例如,走、慢跑、冲刺)。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_MovementStateSetting
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Gameplay tag identifying the movement state.
|
||||
* 标识运动状态的游戏标签。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", meta=(Categories="GMS.MovementState"))
|
||||
FGameplayTag Tag;
|
||||
|
||||
/**
|
||||
* Speed level of the movement state; <0 indicates reverse movement.
|
||||
* 运动状态的速度级别;<0表示反向移动。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
int32 SpeedLevel = INDEX_NONE;
|
||||
|
||||
/**
|
||||
* Speed of the movement state.
|
||||
* 运动状态的速度。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS", Meta = (ClampMin = 0, ForceUnits = "cm/s"))
|
||||
float Speed{375.0f};
|
||||
|
||||
/**
|
||||
* Acceleration of the movement state.
|
||||
* 运动状态的加速度。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
float Acceleration = 800.f;
|
||||
|
||||
/**
|
||||
* Deceleration of the movement state.
|
||||
* 运动状态的减速度。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
float BrakingDeceleration = 1024.f;
|
||||
|
||||
/**
|
||||
* Allowed rotation modes for this movement state.
|
||||
* 此运动状态允许的旋转模式。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="GMS")
|
||||
TArray<FGameplayTag> AllowedRotationModes{GMS_RotationModeTags::VelocityDirection, GMS_RotationModeTags::ViewDirection};
|
||||
|
||||
/**
|
||||
* Equality operator for comparing with another movement state setting.
|
||||
* 与另一个运动状态设置比较的相等运算符。
|
||||
* @param Lhs Left-hand side setting. 左侧设置。
|
||||
* @param RHS Right-hand side setting. 右侧设置。
|
||||
* @return True if tags match. 如果标签匹配返回true。
|
||||
*/
|
||||
friend bool operator==(const FGMS_MovementStateSetting& Lhs, const FGMS_MovementStateSetting& RHS)
|
||||
{
|
||||
return Lhs.Tag == RHS.Tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inequality operator for comparing with another movement state setting.
|
||||
* 与另一个运动状态设置比较的不等运算符。
|
||||
* @param Lhs Left-hand side setting. 左侧设置。
|
||||
* @param RHS Right-hand side setting. 右侧设置。
|
||||
* @return True if tags do not match. 如果标签不匹配返回true。
|
||||
*/
|
||||
friend bool operator!=(const FGMS_MovementStateSetting& Lhs, const FGMS_MovementStateSetting& RHS)
|
||||
{
|
||||
return !(Lhs == RHS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Equality operator for comparing with a gameplay tag.
|
||||
* 与游戏标签比较的相等运算符。
|
||||
* @param Other The gameplay tag to compare. 要比较的游戏标签。
|
||||
* @return True if tags match. 如果标签匹配返回true。
|
||||
*/
|
||||
bool operator==(const FGameplayTag& Other) const
|
||||
{
|
||||
return Tag == Other;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inequality operator for comparing with a gameplay tag.
|
||||
* 与游戏标签比较的不等运算符。
|
||||
* @param Other The gameplay tag to compare. 要比较的游戏标签。
|
||||
* @return True if tags do not match. 如果标签不匹配返回true。
|
||||
*/
|
||||
bool operator!=(const FGameplayTag& Other) const
|
||||
{
|
||||
return Tag != Other;
|
||||
}
|
||||
|
||||
#if WITH_EDITORONLY_DATA
|
||||
|
||||
/**
|
||||
* Velocity direction settings for this movement state.
|
||||
* 此运动状态的速度方向设置。
|
||||
*/
|
||||
UE_DEPRECATED(1.5, "Settings for rotation mode was decoupd from movementstate, see Movement Control Setting.")
|
||||
UPROPERTY(VisibleAnywhere, Category="Deprecated", meta=(EditCondition=false, EditConditionHides))
|
||||
FGMS_VelocityDirectionSetting VelocityDirectionSetting;
|
||||
|
||||
/**
|
||||
* View direction settings for this movement state.
|
||||
* 此运动状态的视图方向设置。
|
||||
*/
|
||||
UE_DEPRECATED(1.5, "Settings for rotation mode was decoupd from movementstate, see Movement Control Setting.")
|
||||
UPROPERTY(VisibleAnywhere, Category="Deprecated", meta=(EditCondition=false, EditConditionHides))
|
||||
FGMS_ViewDirectionSetting ViewDirectionSetting;
|
||||
|
||||
/**
|
||||
* Primary smoothing for character's rotation speed.
|
||||
* 角色旋转的主平滑速度。
|
||||
* @note <=0 disables smoothing. <=0禁用平滑。
|
||||
*/
|
||||
UE_DEPRECATED(1.5, "Settings for rotation mode was decoupd from movementstate, see Movement Control Setting.")
|
||||
UPROPERTY(VisibleAnywhere, Category="Deprecated", meta=(ClampMin=0, EditCondition=false, EditConditionHides))
|
||||
float RotationInterpolationSpeed = 12.0f;
|
||||
|
||||
/**
|
||||
* Speed for smoothing SmoothTargetYawAngle to TargetYawAngle.
|
||||
* 将SmoothTargetYawAngle平滑到TargetYawAngle的速度。
|
||||
* @note <=0 disables smoothing (instant transition). <=0禁用平滑(瞬时过渡)。
|
||||
*/
|
||||
UE_DEPRECATED(1.5, "Settings for rotation mode was decoupd from movementstate, see Movement Control Setting.")
|
||||
UPROPERTY(VisibleAnywhere, Category="Deprecated", meta=(ClampMin=0, EditCondition=false, EditConditionHides))
|
||||
float TargetYawAngleRotationSpeed = 800.0f;
|
||||
|
||||
/**
|
||||
* Editor-friendly name for the movement state.
|
||||
* 运动状态的编辑器友好名称。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, Category="Settings", meta=(EditCondition=false, EditConditionHides))
|
||||
FString EditorFriendlyName;
|
||||
#endif
|
||||
};
|
||||
|
||||
#pragma endregion
|
||||
|
||||
/**
|
||||
* Defines control and animation settings for a movement set.
|
||||
* 定义运动集的控制和动画设置。
|
||||
*/
|
||||
USTRUCT(BlueprintType)
|
||||
struct GENERICMOVEMENTSYSTEM_API FGMS_MovementSetSetting
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
/**
|
||||
* Default movement control setting.
|
||||
* 默认运动控制设置。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Control")
|
||||
TObjectPtr<UGMS_MovementControlSetting_Default> ControlSetting;
|
||||
|
||||
/**
|
||||
* Enables per-overlay mode control settings.
|
||||
* 启用按叠层模式的控制设置。
|
||||
* @note If no control setting is found for an overlay mode, the default is used. 如果未找到叠层模式的控制设置,则使用默认设置。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Control")
|
||||
bool bControlSettingPerOverlayMode{false};
|
||||
|
||||
/**
|
||||
* Maps overlay modes to specific control settings.
|
||||
* 将叠层模式映射到特定控制设置。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Control", meta=(Categories="GMS.OverlayMode", EditCondition="bControlSettingPerOverlayMode"))
|
||||
TMap<FGameplayTag, TObjectPtr<UGMS_MovementControlSetting_Default>> ControlSettings;
|
||||
|
||||
/**
|
||||
* General animation settings shared across the movement set.
|
||||
* 运动集共享的通用动画设置。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Animation")
|
||||
FGMS_AnimDataSetting_General AnimDataSetting_General;
|
||||
|
||||
/**
|
||||
* Determines if instanced states settings are used.
|
||||
* 确定是否使用实例化的状态设置。
|
||||
* @note Uncheck if multiple movement sets share the same states layer setting. 如果多个运动集共享相同的状态层设置,则取消勾选。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Animation")
|
||||
bool bUseInstancedStatesSetting{true};
|
||||
|
||||
/**
|
||||
* Settings for the states animation layer (instanced).
|
||||
* 状态动画层的设置(实例化)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Instanced, Category="Animation", meta = (EditCondition="bUseInstancedStatesSetting", EditConditionHides, DisplayName="Anim Layer Setting (States)"))
|
||||
TObjectPtr<UGMS_AnimLayerSetting_States> AnimLayerSetting_States;
|
||||
|
||||
/**
|
||||
* Settings for the states animation layer (non-instanced).
|
||||
* 状态动画层的设置(非实例化)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Animation",
|
||||
meta = (EditCondition="!bUseInstancedStatesSetting", EditConditionHides, DisplayName="Anim Layer Setting (States)"))
|
||||
TObjectPtr<UGMS_AnimLayerSetting_States> DA_AnimLayerSetting_States;
|
||||
|
||||
/**
|
||||
* Determines if instanced overlay settings are used.
|
||||
* 确定是否使用实例化的叠层设置。
|
||||
* @note Uncheck if multiple movement sets share the same overlay layer setting. 如果多个运动集共享相同的叠层设置,则取消勾选。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Animation")
|
||||
bool bUseInstancedOverlaySetting{true};
|
||||
|
||||
/**
|
||||
* Settings for the overlay animation layer (non-instanced).
|
||||
* 叠层动画层的设置(非实例化)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category="Animation",
|
||||
meta = (EditCondition="!bUseInstancedOverlaySetting", EditConditionHides, DisplayName="Anim Layer Setting (Overlay)"))
|
||||
TObjectPtr<UGMS_AnimLayerSetting_Overlay> DA_AnimLayerSetting_Overlay;
|
||||
|
||||
/**
|
||||
* Settings for the overlay animation layer (instanced).
|
||||
* 叠层动画层的设置(实例化)。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Instanced, Category="Animation",
|
||||
meta = (EditCondition="bUseInstancedOverlaySetting", EditConditionHides, DisplayName="Anim Layer Setting (Overlay)"))
|
||||
TObjectPtr<UGMS_AnimLayerSetting_Overlay> AnimLayerSetting_Overlay;
|
||||
|
||||
/**
|
||||
* Settings for the view animation layer.
|
||||
* 视图动画层的设置。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Instanced, Category="Animation", meta = (DisplayName="Anim Layer Setting (View)"))
|
||||
TObjectPtr<UGMS_AnimLayerSetting_View> AnimLayerSetting_View;
|
||||
|
||||
/**
|
||||
* Settings for the additive animation layer.
|
||||
* 附加动画层的设置。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Instanced, Category="Animation", meta = (DisplayName="Anim Layer Setting (Additive)"))
|
||||
TObjectPtr<UGMS_AnimLayerSetting_Additive> AnimLayerSetting_Additive;
|
||||
|
||||
/**
|
||||
* Settings for the skeletal controls animation layer.
|
||||
* 骨骼控制动画层的设置。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Instanced, Category="Animation", meta = (DisplayName="Anim Layer Setting (SkeletalControls)"))
|
||||
TObjectPtr<UGMS_AnimLayerSetting_SkeletalControls> AnimLayerSetting_SkeletalControls;
|
||||
|
||||
/**
|
||||
* Custom user settings for the movement set.
|
||||
* 运动集的自定义用户设置。
|
||||
* @note Subclass UGMS_MovementSetUserSetting and consume in animation layer blueprints. 子类化UGMS_MovementSetUserSetting并在动画层蓝图中使用。
|
||||
* @example Access via GetMovementSystemComponent()->GetMovementSetSetting()->GetMovementSetUserSetting(SettingClass). 通过GetMovementSystemComponent()->GetMovementSetSetting()->GetMovementSetUserSetting(SettingClass)访问。
|
||||
*/
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Instanced, Category="Extension", meta=(AllowAbstract=false))
|
||||
TArray<TObjectPtr<UGMS_MovementSetUserSetting>> UserSettings;
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "GMS_Constants.generated.h"
|
||||
|
||||
UCLASS(Meta = (BlueprintThreadSafe))
|
||||
class GENERICMOVEMENTSYSTEM_API UGMS_Constants : public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="GMS|Constants|Animation Slots", Meta = (ReturnDisplayName = "Slot Name"))
|
||||
static const FName& TurnInPlaceSlotName();
|
||||
|
||||
// Other Animation Curves
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="GMS|Constants|Animation Curves", Meta = (ReturnDisplayName = "Curve Name"))
|
||||
static const FName& RotationYawSpeedCurveName();
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="GMS|Constants|Animation Curves", Meta = (ReturnDisplayName = "Curve Name"))
|
||||
static const FName& RotationYawOffsetCurveName();
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="GMS|Constants|Animation Curves", Meta = (ReturnDisplayName = "Curve Name"))
|
||||
static const FName& AllowTurnInPlaceCurveName();
|
||||
|
||||
UFUNCTION(BlueprintCallable, BlueprintPure, Category="GMS|Constants|Animation Curves", Meta = (ReturnDisplayName = "Curve Name"))
|
||||
static const FName& AllowAimingCurveName();
|
||||
};
|
||||
|
||||
inline const FName& UGMS_Constants::TurnInPlaceSlotName()
|
||||
{
|
||||
static const FName Name{TEXTVIEW("TurnInPlace")};
|
||||
return Name;
|
||||
}
|
||||
|
||||
inline const FName& UGMS_Constants::RotationYawSpeedCurveName()
|
||||
{
|
||||
static const FName Name{TEXTVIEW("RotationYawSpeed")};
|
||||
return Name;
|
||||
}
|
||||
|
||||
inline const FName& UGMS_Constants::RotationYawOffsetCurveName()
|
||||
{
|
||||
static const FName Name{TEXTVIEW("RotationYawOffset")};
|
||||
return Name;
|
||||
}
|
||||
|
||||
inline const FName& UGMS_Constants::AllowTurnInPlaceCurveName()
|
||||
{
|
||||
static const FName Name{TEXTVIEW("AllowTurnInPlace")};
|
||||
return Name;
|
||||
}
|
||||
|
||||
inline const FName& UGMS_Constants::AllowAimingCurveName()
|
||||
{
|
||||
static const FName Name{TEXTVIEW("AllowAiming")};
|
||||
return Name;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2025 https://yuewu.dev/en All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Logging/LogMacros.h"
|
||||
|
||||
namespace GMSLog
|
||||
{
|
||||
GENERICMOVEMENTSYSTEM_API extern const FName MessageLogName;
|
||||
}
|
||||
|
||||
DECLARE_STATS_GROUP(TEXT("GMS"), STATGROUP_GMS, STATCAT_Advanced)
|
||||
|
||||
FString GetGMSLogContextString(const UObject* ContextObject = nullptr);
|
||||
|
||||
GENERICMOVEMENTSYSTEM_API DECLARE_LOG_CATEGORY_EXTERN(LogGMS, Log, All)
|
||||
|
||||
GENERICMOVEMENTSYSTEM_API DECLARE_LOG_CATEGORY_EXTERN(LogGMS_Animation, Log, All)
|
||||
|
||||
#define GMS_LOG(Verbosity, Format, ...) \
|
||||
{ \
|
||||
UE_LOG(LogGMS, Verbosity, TEXT("%S: %s"),__FUNCTION__, *FString::Printf(TEXT(Format), ##__VA_ARGS__)) \
|
||||
}
|
||||
|
||||
#define GMS_ANIMATION_LOG(Verbosity, Format, ...) \
|
||||
{ \
|
||||
UE_LOG(LogGMS_Animation, Verbosity, TEXT("%S: %s"),__FUNCTION__, *FString::Printf(TEXT(Format), ##__VA_ARGS__)) \
|
||||
}
|
||||
|
||||
#define GMS_CLOG(Verbosity, Format, ...) \
|
||||
{ \
|
||||
UE_LOG(LogGMS, Verbosity, TEXT("%S: ctx(%s) %s"),__FUNCTION__, *GetGMSLogContextString(this), *FString::Printf(TEXT(Format), ##__VA_ARGS__)) \
|
||||
}
|
||||
|
||||
#define GMS_ANIMATION_CLOG(Verbosity, Format, ...) \
|
||||
{ \
|
||||
UE_LOG(LogGMS_Animation, Verbosity, TEXT("%S: ctx(%s) %s"),__FUNCTION__, *GetGMSLogContextString(this), *FString::Printf(TEXT(Format), ##__VA_ARGS__)) \
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user