Custom AnimGraph Node C++ Templates

Greetings, I’ve made two templates (UE 4.15) that should serve as an example for Creating Custom AnimGraph Nodes, both inside a regular c++ project and inside a Plugin.

Project Template: https://github.com/Hethger/UE4_AnimGraphNode_Project_Template

Plugin Template: https://github.com/Hethger/UE4_CustomAnimGraphNode_Plugin_Template

The AnimGraph Node itself has the minimum code that a node needs to work, so it outputs the same pose that is feeded into it.

MyAnimGraphNode.JPG

I post this as there is no complete guide that goes in depth about creating Custom AnimGraph Nodes. Theres enough information about making the functionality inside the node’s source files. But what lacks is an oficial explanation of the structure that has to be built around them to properly work outside the editor.

Through trial and error, research, and help from the community, I finally elaborated the procedure by which I made this templates and with which I also incorporate Custom AnimGraph Nodes into my own plugin: the Mirror Animation System.

I can only attest for this packaging and working in windows, as I can’t test this in other platforms. Although a strange specific thing is that the plugin needs to have IOS blacklisted to package the Plugin on it’s own, but not when Packaging the whole project.

I have a dream, that maybe with the infinite power of the community, this and other small voids of information about this topic will come to and end, marching to a future were no one will ever have to suffer in agony doing AnimGraph Nodes, ever again.

5 Likes

Why didn’t you do this last week? :confused:
I was in need of this soooo badly last week, I ended up figuring out (after long two hours) how to make them (and customize widget cosmetics) by myself :wink:

This is my final result at the moment:

Anyways, is always good to have samples, making a wiki page would be cool too!

I have a bit of a problem with your template on 4.16. While everything compiles correctly and your node shows up in the context menu just fine, the nodes are actually never saved. They will work in game and everything, but once I reboot the editor, they are just gone.

When placing the node
73cdc7f7ffc53b0fd1a9815c2da54d331c7dc5d3.jpeg

On reboot
7c24d1963469e35c0b9f4d5871f96fd74162874c.jpeg

The message I get on reboot
cd48e0d27da6dc480fde48eb620cd9b7f3ad850b.jpeg

I should note that for testing sake, I simply just moved your plugin into the plugin folder and didn’t change anything (primarily to see if my earlier attempts of using your template for my own version weren’t botched in some way).

Edit: I just tested an older backup project with a Unreal Engine 4.15.2, and get the exact same error, leading me to believe that there may be some other settings I may have to change. Would you happen to have any ideas Hethger?

Edit 2: Got the same result on an new fresh project.

No, I am still getting the exact same error, even after following all the steps you described. I should note I have also tried this with a fresh new project, and am getting the exact same errors. Makes me believe there is something somewhere that I haven’t properly set yet (maybe something in the settings), though it would be strange because other editor extending plugins I am using don’t have any problems whatsoever.

On another node; if I use your example node, it will all be removed upon reboot anyway so removing them is not necessary. When I use a bit more “intensive” node in terms of functionality, well, then upon reboot it will without fail throw an access violation error and crash before the editor is even started.

Those errors look very much like what you’d get if the plugin modules were not loaded. Are you able to use the plugin (for example, replace the custom node), immediately after seeing those messages? If so, I wonder if it could be related to module loading order - seems like perhaps somehow the blueprint is being loaded before the plugin modules. I’d maybe try playing around with the LoadingPhase setting of the module references within your uplugin file. See here for reference.

It worked! Changing it to predefault stops the violation errors and the nodes from disappearing. Now I can finally continue moving all the nodes I had made before into the plugin. Thanks!

This definitely works, but changing the module load order will create a conflict if you’re using component visualizers (these require a PostEngineInit load order). Outside of doing something silly like creating separate modules for the visualizers and the graph nodes, what would be the solution to have both?

Nothing silly about creating separate modules for them, they’re totally unrelated after all. In fact I have a feeling the graph nodes should really be in a Developer module anyway, and the visualizers in an Editor module. This is definitely the case for blueprint nodes in order for things to work in standalone mode; not 100% sure it’s the same for anim nodes but likely is.

This post is very relevant even till today . I am struggling to get my plugin to package for so many days with horribly low information regarding it . This should hopefully make me understand where i did wrong.

Hethger, a god among men.

The project would not compile unless I made a few changes since the editor has updated somewhat since this was posted. I’d do a pull request, but I’m still learning how to git.
I haven’t tried a packaged project with these changes, however I had to perform these for the project to even build. I am also using a completely empty project.

REQUIRED(?) CHANGES:

MyGame.Target.cs



using UnrealBuildTool;
using System.Collections.Generic;

public class MyGameTarget : TargetRules {
     public MyGameTarget(TargetInfo Target) {
          Type = TargetType.Game;

          // SetupBinaries() function is deprecated, we can just do this instead
          // We also no longer need to check if we are within the editor
          // Don't know why, possibly 'Type = TargetType.Editor' in 'MyGameEditor.Target.cs' takes care of this
          ExtraModuleNames.AddRange(new string] { "MyGame" });
     }
} 

MyGameEditor.Target.cs



using UnrealBuildTool;
using System.Collections.Generic;

public class MyGameTarget : TargetRules {
     public MyGameTarget(TargetInfo Target) {
          Type = TargetType.Editor;

          // SetupBinaries() function is deprecated, we can just do this instead
          ExtraModuleNames.AddRange(new string] { "MyGame"});
     }
} 

MyGame.cpp



#include "MyGame.h"
#include "Modules/ModuleManager.h"

class FDevProjectModule : public FDefaultGameModuleImpl {
#if WITH_EDITOR
    virtual void StartupModule() override {
        // Note the addition of AnimGraphRunTime      
        FModuleManager::Get().LoadModule(TEXT("BlueprintGraph"));
        FModuleManager::Get().LoadModule(TEXT("AnimGraph"));
        FModuleManager::Get().LoadModule(TEXT("AnimGraphRunTime"));
        FModuleManager::Get().LoadModule(TEXT("MyGameEditor"));
    }
#endif
};

IMPLEMENT_PRIMARY_GAME_MODULE(FDefaultGameModuleImpl, MyGame, "MyGame");


MyAnimNode.h



#pragma once

#include "Animation/AnimNodeBase.h"
#include "MyAnimNode.generated.h"

USTRUCT(BlueprintType)
struct MYGAME_API FMyAnimNode : public FAnimNode_Base {
    GENERATED_BODY()

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Links)
    FPoseLink BasePose;

public:
    MyAnimNode();

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

};

MyAnimNode.cpp



#include "MyAnimNode.h"

#include "MyGame.h"
#include "Animation/AnimInstanceProxy.h"


FMyAnimNode::FMyAnimNode() {

}

void FMyAnimNode::Initialize_AnyThread(const FAnimationInitializeContext & Context) {
    Super::Initialize_AnyThread(Context);

    BasePose.Initialize(Context);
}

void FMyAnimNode::CacheBones_AnyThread(const FAnimationCacheBonesContext & Context) {
    BasePose.CacheBones(Context);
}

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

void FMyAnimNode::Evaluate_AnyThread(FPoseContext & Output) {
    // Evaluate the input
    BasePose.Evaluate(Output);
}

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


    DebugData.AddDebugItem(DebugLine);

    BasePose.GatherDebugData(DebugData);
}



To get SkeletonControlBase nodes working I had to also add the ‘AnimGraphRuntime’ public module in ‘MyGame.build.cs’, otherwise you’ll get drowned by linker errors.