Hi, I’ve made a custom anim node but now I need get data OUT of it but Unreal devs really went out of their way to make it impossible to get any sort of data outside of an anim node.
I’ve tried overriding “CreateOutputPins()” but there’s no way to connect the pin to a property. I’m at wit’s end here
As far as I know, there is no way to create working AnimNode outputs. To get data from AnimNode you can set curve values to FPoseContext Output an than read their values in AnimInstance or any AnimNode.
Interesting. I didn’t know that was an option. Unfortunately the amount of data I was planning on taking out involved booleans and vectors which don’t translate well into curves.
I’ve worked out a possible solution:
In the “Blueprint Initialize Animation” event, through the use of the “Construct Object from Class” node, create an instance of a custom UCLASS object and save it in a Blueprint variable.
In the Anim node, we add an input that takes an instance of our UCLASS object. Get the previously created variable and connect it here.
We can now call the methods in our UCLASS from both the Anim graph and the event graph, which is awesome, but we gotta be careful to avoid race conditions since all of this is multithreaded and there’s probably a good reason why Unreal has these nodes sandboxed. I’m still overriding “CreateOutputPins()” so I can change how my input pin looks like, specifically making it look like a ref pin and hiding the “Select asset” dropdown.
Here’s my sample code if anyone’s interested:
// UFoobarInterface. Our custom UCLASS object
UCLASS(BlueprintType)
class FOOBAR_API UFoobarInterface : public UObject
{
GENERATED_BODY()
public:
UFoobarInterface(const FObjectInitializer& ObjectInitializer);
private:
FAnimNode_Foobar* animNode = nullptr;
public:
// Associate an anim node instance with this interface
void PairAnimNode(FAnimNode_Foobar* foobarNode);
};
void UFoobarInterface::PairAnimNode(FAnimNode_Foobar* foobarNode)
{
animNode = foobarNode;
}
// Anim node
USTRUCT(BlueprintInternalUseOnly)
struct FOOBAR_API FAnimNode_Foobar : public FAnimNode_Base
{
GENERATED_BODY()
/** Plug in an instance of a Foobar Object Reference. Use "CreateFoobarInterface()" to instance. */
UPROPERTY(EditAnywhere, BlueprintReadWrite, Transient, meta = (AlwaysAsPin))
UFoobarInterface* FoobarInterface;
private:
bool bFirstUpdate = true;
};
void FAnimNode_Foobar::Update_AnyThread(const FAnimationUpdateContext& Context)
{
DECLARE_SCOPE_HIERARCHICAL_COUNTER_ANIMNODE(Update_AnyThread)
GetEvaluateGraphExposedInputs().Execute(Context); // This lets us use the connected inputs.
if (bFirstUpdate)
{
bFirstUpdate = false;
// This object let's us communicate with the outside world!
if (FoobarInterface)
{
FoobarInterface->PairAnimNode(this);
// Now we can do whatever we want with the FoobarInterface.
}
}
}
// Anim graph node. Not necessary but looks better with these modifications.
void UFoobarGraphNode::CreateOutputPins()
{
Super::CreateOutputPins();
for (UEdGraphPin* Pin : Pins)
{
if (Pin->GetFName() == "FoobarInterface")
{
Pin->bDisplayAsMutableRef = true; // show a little diamond pin instead of circle
Pin->bDefaultValueIsIgnored = true; // don't let users put an asset here.
}
}
}
The programming model they used for the anim graph wouldn’t work great with non-pose outputs. E.g. the update/evaluate passes would introduce some ambiguity - when do the outputs actually become available on the output pins? You may want to look into using control rig.
If you want to quickly hack around it, you can take an input that refers to something else. E.g. take a struct with a pointer to a “scratch pad” where you write your variables. You could create a BP pure node than then exposes the scratch pad variables in the graph in turn. Obviously you’re then exposed to all the weirdness of evaluation order in the anim graph.
If you find yourself wanting to pass transforms down the graph, just use bones. Unreal lets you add virtual bones for a reason! Curves can work for most other things.
You’re fine reading/writing in the anim BP’s event graph in the “Event Blueprint Update Animation” event and the other normal anim BP events. The event graph update runs on the game thread before the parallel evaluation kicks off: the idea is that you’re copying info you need from the game into BP variables you’ll then read from in the anim graph. What’s dangerous is casting to your anim BP and writing to it from elsewhere, which could happen at the same time as the parallel eval. Triggering custom events in your anim BP event graph from elsewhere would also be dangerous.
Thanks for that suggestion with virtual bones, though I took a whack at it yesterday and realized that for IK effectors using virtual bones the vectors have to be in bone space but my vectors are in world space. On top of that, I imagine virtual bones are probably blended with inertialization so I’m gonna stick to using my custom UCLASS interface object (or “scratch pad” as you put it).
I don’t know if I understand the question correctly, but if you want to store some data in custom anim node, one way you can do is to call animpoxy and get your character class. Assume you have the data variable initialized in character class, you can simply assign a new value to it. That way you don’t have to add extra input or output to anim node.
Not a great idea to read direct from gameplay classes in an anim node: you’re usually running in a worker thread, while gameplay stuff continues to happen on the main thread. If something on the main thread writes to the character you’re reading from, you have a data race. You could end up reading garbage or partially-set values (it’s undefined behavior), and at the very least could see inconsistent state (e.g. a pointer you’re using could suddenly become null). It’s the kind of error that will pop up extremely rarely and be impossible to reproduce reliably.