第一次提交

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

View File

@@ -0,0 +1,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",
});
}
}

View File

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

View File

@@ -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")

View File

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

View File

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

View File

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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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