Intro
The next few articles on this blog are going to go over creating an AnimNode that can be used in an AnimBP in UE4/UE5. I’ll try to keep it light and gloss over some of the more obvious parts of this system while diving into how the nodes actually work and do their thing.
There’s a few things you really need to know about and semi-understand before you jump into this though:
Bone Indexes
Read this link on how Unreal handles bone indexes. (Author’s twitter)
So for our purposes, the most important bone index will be FCompactPoseBoneIndex
. Basically, if a bone has a valid compact index, that means it is currently being used for pose evaluation. If the Index turns out to be INDEX_NONE
, that means the bone has been LODed out or something and we don’t want to operate on it (re: can’t operate on it. It will crash the engine. Wop wop.).
Spaces
We are going to operate completely in ComponentSpace, NOT LocalSpace. Think of ComponentSpace as WorldSpace if the origin was at the origin of the SkeletalMeshComponent.
What does this mean for us in practice? We cannot rely on local space child-parent relationships to help with our transformations. We need to mess with every bone as if its in world space, ‘cause it is.
FBoneReference
Read FBoneReference
in BoneContainer.h
. BoneRefs will be your best friend. They contain the name of the bone, its absolute (skeleton) index, its CompactPoseIndex, as well as the ability to evaluate if the bone is able to be used, and initialize it if not. They’re an easy way to transport all the data we will be using, and they’re very light-weight structs.
Class Overview
AnimNodes consist of two mandatory parts:
FAnimNode
class- This is the runtime element to the node. This is where all the pose evaluation actually happens.
- Will be derived from
FAnimNode_Base
orFAnimNode_SkeletalControlBase
. This article will assume the latter, as we will be making a node that operates in ComponentSpace, not LocalSpace. - MUST be placed into a Runtime module AND be exported (for instance, with a
MYMODULE_API
specifier). The graph node will need to access this stuff. - To your Runtime module, you must include the
AnimGraphRuntime
module as a public dependency.
FAnimGraphNode
class- This is the editor node that you place into the AnimBP graph. This simply displays the info of the
FAnimNode
, and well as some debug capabilities. - Will derive from
FAnimGraphNode_Base
orFAnimGraphNode_SkeletalControlBase
. Again, we will assume the latter. - MUST be placed into an UncookedOnly or Developer module. I have mine in an UncookedOnly K2Node-specific module.
- The docs say this class can be in an Editor module, and that would make sense, but when I did that I got a warning (not an error, but still). It might still work in an Editor module but I’m not sure.
- To your UncookedOnly/Developer module, you must include the
AnimGraph
module as a public dependency.
- This is the editor node that you place into the AnimBP graph. This simply displays the info of the
Where to start
Open up AnimNode_TwoBoneIK.h/.cpp
and AnimGraphNode_TwoBoneIK.h/.cpp
These are great classes to look at to get a feel for how these two parts interconnect, and how the runtime class will operate. Also, read FAnimNode_Base
. Read it. Seriously, read the whole thing. The comments on all the *_AnyThread
methods are incredibly helpful.
Design
So, what do we want this node to actually do?
It will take in the mesh’s pose in component space, and based on a curve and some other parameters, will scale the joints of a chain to the desired amount.
I can see a few applications for something like this, but mostly I’m doing it because it shows most aspects of what an anim node can do.
Runtime Class
So let’s start with our Runtime class first. Create two new files in your runtime module called AnimNode_GradualScaleJoints.h/.cpp
.
|
|
|
|
You’ll see a ton of boilerplate here. A few of these are pure virtual and need to be implemented, a few don’t. Fill out these methods in the .cpp so it will all compile, but leave it all blank for now. I will go over what all of these do in the next part when we get to order-of-operations talk.
We also went to set up the parameters we will need: the bone we want to start the scale chain at, and the curve that will drive the scale. We will add a few more parameters to this in the next part, but for now this will suffice.
This is all we need to create and view the...
Editor Class
Create two new files in your Uncooked/Developer module called AnimGraphNode_GradualScaleJoints.h/.cpp
. These are going to be super simple. The underlying native code does all the heavy lifting for us.
|
|
|
|
Notice the UPROPERTY
near the top: FAnimNode_GradualScaleJoints Node
. This is the runtime class. You don’t need to worry about exposing all of the class’s parameters to the graph node; it’s automagical.
The Draw
method is NOT needed. Used for debug, whenever you click on the node in the anim graph, the Draw
method will be called.
As long as you have everything set up correctly, you should be able to open an animBP and add the node to it:
In the next part, we’ll go over adding some needed utility functionality, the order of operations, and doing the actual transformations.