Legacy/Animation Nodes, Code for How to Create Your Own

From UE4: Community Wiki
Jump to: navigation, search

Overview

Author: Rama (talk)

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!

Rama (talk)