Creating a Custom Animation Graph Node (4.20.3)

Hey guys,

I’ve had the need to create my own animation node for a little project I’m working on. Looking through multiple forum posts, repo’s etc, I’ve finally managed to get my own node working in 4.20.

https://forums.unrealengine.com/deve…de-c-templates – This helped me a lot for the basis, but had to change some things, due to things being depricated. This one still does the same thing, just creates a blank one, consider this a more up-to-date version.

I’m just posting this here if others are struggling with the same thing, as there is a significant lack of documentation with custom animation graph nodes.

NOTE: I’ve only set this up as C++ classes within my project, I haven’t tested this as being a seperate plugin. Feel free to try and leave comments below if it worked/didn

MyAnimNode.h



// Fill out your copyright notice in the Description page of Project Settings.
#pragma once

#include "CoreMinimal.h"
#include "Animation/AnimNodeBase.h"
#include "Hi5AnimNode.generated.h"

USTRUCT(BlueprintType)
struct FHi5AnimNode : public FAnimNode_Base
{
    GENERATED_BODY()
        UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Links)
        FPoseLink IKinemaPose;

        // FAnimNode_Base interface
        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;
        // End of FAnimNode_Base interface

    FHi5AnimNode();
};

MyAnimNode.cpp



// Fill out your copyright notice in the Description page of Project Settings.

#include "Hi5AnimNode.h"

FHi5AnimNode::FHi5AnimNode()
{
}

void FHi5AnimNode::Initialize_AnyThread(const FAnimationInitializeContext & Context)
{
    IKinemaPose.Initialize(Context);
}

void FHi5AnimNode::CacheBones_AnyThread(const FAnimationCacheBonesContext & Context)
{
    IKinemaPose.CacheBones(Context);
}

void FHi5AnimNode::Update_AnyThread(const FAnimationUpdateContext & Context)
{
    EvaluateGraphExposedInputs.Execute(Context);
    IKinemaPose.Update(Context);
}

void FHi5AnimNode::Evaluate_AnyThread(FPoseContext & Output)
{
    IKinemaPose.Evaluate(Output);
}

void FHi5AnimNode::GatherDebugData(FNodeDebugData & DebugData)
{
    FString DebugLine = DebugData.GetNodeName(this);


    DebugData.AddDebugItem(DebugLine);

    IKinemaPose.GatherDebugData(DebugData);
}


MyAnimGraphNode.h



// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "Hi5AnimNode.h"
#include "CoreMinimal.h"
#include "AnimGraph/Classes/AnimGraphNode_Base.h"
#include "Hi5AnimGraphNode.generated.h"
/**
 *
 */
UCLASS()
class UHi5AnimGraphNode : public UAnimGraphNode_Base
{
    GENERATED_BODY()

public:

    UPROPERTY(EditAnywhere, Category = "Settings")
        FHi5AnimNode Node;

    //~ Begin UEdGraphNode Interface.
    virtual FLinearColor GetNodeTitleColor() const override;
    virtual FText GetTooltipText() const override;
    virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
    //~ End UEdGraphNode Interface.

    //~ Begin UAnimGraphNode_Base Interface
    virtual FString GetNodeCategory() const override;
    //~ End UAnimGraphNode_Base Interface

    UHi5AnimGraphNode(const FObjectInitializer& ObjectInitializer);
};


MyAnimGraphNode.cpp



// Fill out your copyright notice in the Description page of Project Settings.

#include "Hi5AnimGraphNode.h"
#include "HI5TESTING.h"

#define LOCTEXT_NAMESPACE "A3Nodes"

UHi5AnimGraphNode::UHi5AnimGraphNode(const FObjectInitializer& ObjectInitializer)
    :Super(ObjectInitializer)
{
}

FLinearColor UHi5AnimGraphNode::GetNodeTitleColor() const
{
    return FLinearColor::Green;
}

FText UHi5AnimGraphNode::GetTooltipText() const
{
    return LOCTEXT("Hi5BlendNode", "Hi5BlendNode");
}

FText UHi5AnimGraphNode::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
    return LOCTEXT("Hi5BlendNode", "Hi5BlendNode");
}

FString UHi5AnimGraphNode::GetNodeCategory() const
{
    return TEXT("Hi5BlendNode");
}

#undef LOCTEXT_NAMESPACE


MYPROJECT.Build.cs



// Fill out your copyright notice in the Description page of Project Settings.

using UnrealBuildTool;

public class HI5TESTING : ModuleRules
{
    public HI5TESTING(ReadOnlyTargetRules Target) : base(Target)
    {
        PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;

        PublicDependencyModuleNames.AddRange(new string] { "Core", "CoreUObject", "Engine", "InputCore", "AnimGraph", "AnimGraphRunTime", "BlueprintGraph" });

        PrivateDependencyModuleNames.AddRange(new string] { "AnimGraph", "AnimGraphRunTime", "BlueprintGraph" });

        // Uncomment if you are using Slate UI
        // PrivateDependencyModuleNames.AddRange(new string] { "Slate", "SlateCore" });

        // Uncomment if you are using online features
        // PrivateDependencyModuleNames.Add("OnlineSubsystem");

        // To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
    }
}


3 Likes

Freaking sweet, I just came upon this problem last week when I saw UE4’s IK was lackluster. Great timing and awesome work! :cool:

You safe my life man thank you.

1 Like

Would love to see an example of use. Like getting the anim sequence triggering the node and exporting something out of it. Am kind of behind on fully understanding this :slight_smile:

Deprecated again, in MyAnimNode.cpp


EvaluateGraphExposedInputs.Execute(Context);

is now


GetEvaluateGraphExposedInputs().Execute(Context);

.