Animation Nodes, Code for How to Create Your Own
Contents
Overview
Dear Community,
In this tutorial I explain the very basics of making your own custom animation node.
My main goal is to help you establish the basic structure to then pursue your individual animation node goals on your own :)
This is not a tutorial for creating skeletal controllers, but rather animation blends of one kind or another :)
Complete Custom Animation Code Example
I have my entire source code for a custom animation node, Editor Graph Node included, here!
Custom Turn in Place Animation Node
Class to Extend AnimNode_Base
Here is the basic setup you need, .h and .cpp, for the node itself
This node is a struct that is wrapped in the editor animgraph node that is what you are used to seeing :)
.h
// Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "AnimNode_NameOfYourNode.generated.h"
USTRUCT()
struct FAnimNode_NameOfYourNode : public FAnimNode_Base
{
GENERATED_USTRUCT_BODY()
//FPoseLink - this can be any combination
//of other nodes, not just animation sequences
// so you could have an blend space leading into
//a layer blend per bone to just use the arm
// and then pass that into the PoseLink
/** Base Pose - This Can Be Entire Anim Graph Up To This Point, or Any Combination of Other Nodes*/
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Links)
FPoseLink BasePose;
/** Other Pose! - This Can Be Entire Anim Graph Up To This Point, or Any Combination of Other Nodes */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Links)
FPoseLink OtherPose;
/** Sample Property That Will Show Up as a Pin */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category=Links, meta=(PinShownByDefault))
float SampleFloat;
// FAnimNode_Base interface
public:
// FAnimNode_Base interface
virtual void Initialize(const FAnimationInitializeContext& Context) OVERRIDE;
virtual void CacheBones(const FAnimationCacheBonesContext & Context) OVERRIDE;
virtual void Update(const FAnimationUpdateContext & Context) OVERRIDE;
virtual void Evaluate(FPoseContext& Output) OVERRIDE;
// End of FAnimNode_Base interface
// Constructor
public:
FAnimNode_NameOfYourNode();
protected:
bool WorldIsGame;
AActor* OwningActor;
};
.cpp
// Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
#include "YourGame.h"
//#include "AnimationRuntime.h"
FAnimNode_NameOfYourNode::FAnimNode_NameOfYourNode()
: FAnimNode_Base()
, SampleFloat(128.333)
{
WorldIsGame = false;
}
void FAnimNode_NameOfYourNode::Initialize(const FAnimationInitializeContext & Context)
{
//Init the Inputs
BasePose.Initialize(Context);
OtherPose.Initialize(Context);
//Get the Actor Owner
OwningActor = Context.AnimInstance-> GetSkelMeshComponent()->GetOwner();
//Editor or Game?
UWorld * TheWorld = Context.AnimInstance->GetWorld();
if (!TheWorld) return;
//~
WorldIsGame = (TheWorld->WorldType == EWorldType::Game);
}
void FAnimNode_NameOfYourNode::CacheBones(const FAnimationCacheBonesContext & Context)
{
BasePose.CacheBones(Context);
OtherPose.CacheBones(Context);
}
void FAnimNode_NameOfYourNode::Update(const FAnimationUpdateContext & Context)
{
//***************************************
// Evaluate Graph, see AnimNode_Base, AnimNodeBase.h
EvaluateGraphExposedInputs.Execute(Context);
//***************************************
//EDITOR
//Editor mode? just use the base pose
if (!WorldIsGame)
{
//if your node depends on
//actual actor instance, can't do anything in editor
}
//GAME
//Actually in Game so the Owner Instance Should Exist
else
{
//Try Again if not found
if (!OwningActor) OwningActor =
Context.AnimInstance->GetSkelMeshComponent()->GetOwner();
//Not found
if (!OwningActor)
{
UE_LOG(LogAnimation, Warning,
TEXT("FAnimNode_NameOfYourNode::Update() Owning Actor was not found"));
return;
//~
}
//Do Stuff Based On Actor Owner
}
//~~
// Do Updates
//Try To Update As Few of the Inputs As You Can
//************************************************
// FPoseLinkBase::Update Active Pose - this is what makes
//the glowing line thing happen and animations loop
//***********************************************
BasePose.Update(Context);
}
void FAnimNode_NameOfYourNode::Evaluate(FPoseContext & Output)
{
// Return Base Pose, Un Modified
BasePose.Evaluate(Output);
//Evaluate is returning the Output to this function,
//which is returning the Output to the rest of the Anim Graph
//In this case, we are passing the Output out variable into the BasePose
//Basically saying, give us back the unmodified Base Pose
//i.e, the bulk of your anim tree.
}
Key Concepts of Animation Nodes
There is an initialize, an update, and an evaluate
Initialize = make sure to initialize the inputs!
BasePose.Initialize(Context);
OtherPose.Initialize(Context);
Animation Update
Make sure to actually "tick" the inputs of your choice!
BasePose.Update(Context);
Try to update as few of the inputs as you can, based on the logic of your anim node.
For example if you were blending between A and B and you were fully blended to B, you would not need to update A at all
But if you were at 0.5 in between you'd have to update both.
If you don't update any poses then the graph lines will not glow.
Animation Evaluate
Pass out the results of all your calculations and updates as the output to the rest of the Anim Graph
The Output variable is the entire output of all your anim nodes activities
BasePose.Evaluate(Output);
You can do much more complicated things than just evaluate the base or the other poses
The key data of Output is Output.Pose, which is an FA2Pose data, which is animation data that could be calculated any number of ways.
As long as you update Output.Pose you can do all sorts of things
- )
AnimGraph Node
Here is the basic .h and .cpp you need for making the in-editor anim graph visual representation of your node
.h
// Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "AnimGraphDefinitions.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "AnimGraphNode_NameOfYourNode.generated.h"
//Whole point of this is to be wrapper for node struct
// so it depends on it, and that node must compile first
// for type to be recognized
UCLASS(MinimalAPI, dependson=FAnimNode_NameOfYourNode)
class UAnimGraphNode_NameOfYourNode : public UAnimGraphNode_Base
{
GENERATED_UCLASS_BODY()
UPROPERTY(EditAnywhere, Category=Settings)
FAnimNode_NameOfYourNode Node;
public:
// UEdGraphNode interface
virtual FString GetNodeTitle(ENodeTitleType::Type TitleType) const OVERRIDE;
virtual FLinearColor GetNodeTitleColor() const OVERRIDE;
virtual FString GetNodeCategory() const OVERRIDE;
// End of UEdGraphNode interface
protected:
virtual FString GetControllerDescription() const;
};
.cpp
// Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
#include "YourGame.h"
/////////////////////////////////////////////////////
// UAnimGraphNode_NameOfYourNode
UAnimGraphNode_NameOfYourNode::UAnimGraphNode_NameOfYourNode(const FPostConstructInitializeProperties& PCIP)
: Super(PCIP)
{
}
//Title Color!
FLinearColor UAnimGraphNode_NameOfYourNode::GetNodeTitleColor() const
{
return FLinearColor(0,12,12,1);
}
//Node Category
FString UAnimGraphNode_NameOfYourNode::GetNodeCategory() const
{
return FString("Anim Node Category");
}
FString UAnimGraphNode_NameOfYourNode::GetControllerDescription() const
{
return TEXT(" Your Anim node Title ");
}
FString UAnimGraphNode_NameOfYourNode::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
FString Result = *GetControllerDescription();
Result += (TitleType == ENodeTitleType::ListView) ? TEXT("") : TEXT("\n");
return Result;
}
Summary
With these basics and reviewing the different animation data types you are well on your way to making your own animatoion node!
UE4 Source References
See these .h files for lots of important info
AnimNodeBase.h
AnimationAssets.h
AnimInstance.h
Enjoy!