본문으로 건너뛰기

Controlling Graph Playback Flow

Joint provides a robust system for managing playback flow within its node-based architecture.

Playback Flow controlling is one part of the core design of Joint, allowing users to easily and intuitively control the flow of execution of base nodes.

Selecting Next Nodes to Play

How Does It Work?

When a base node (node that can be placed directly on the graph by itself (e.g, foundation)) is ended, Joint Actor will check for the next node to be played.

img_2.png

And how it determines the next node is the interesting part: First, Joint Actor will call SelectNextNodes function on the base node (in this example, foundation node).

Let's see how SelectNextNodes is implemented on the foundation node: it tries to call SelectNextNodes on every sub nodes first, and if any of the sub nodes returns non-empty array of next nodes, it will use that as the next nodes to be played.

But if none of the sub nodes returns any valid next nodes, it will return its own Next Node property as the next node to be played.

TArray<UJointNodeBase*> UJN_Foundation::SelectNextNodes_Implementation(AJointActor* InHostingJointInstance)
{
TArray<UJointNodeBase*> Nodes = Super::SelectNextNodes_Implementation(InHostingJointInstance);

if(!Nodes.IsEmpty()) return Nodes;

return NextNode;
}

SelectNextNodes's default implementation on the base class UJointNodeBase is to iterate through every sub nodes and call SelectNextNodes on them, and if any of them returns non-empty array of next nodes, it will use that as the next nodes to be played.

노트

to say, it's like a depth-first search on the node tree until it finds one that returns something, and returning that something as the next nodes to be played.

TArray<UJointNodeBase*> UJointNodeBase::SelectNextNodes_Implementation(
AJointActor* InHostingJointInstance)
{
for (UJointNodeBase* SubNode : SubNodes)
{
if (SubNode == nullptr) continue;

if(!SubNode->IsNodeBegunPlay()) continue;

//Test through the sub nodes and if it has something then use that.
TArray<UJointNodeBase*> Nodes = SubNode->SelectNextNodes(InHostingJointInstance);
if (!Nodes.IsEmpty()) return Nodes;
}

return TArray<UJointNodeBase*>();
}

Customizing Next Node Selection

And what this means is that you can override the default behavior of SelectNextNodes on your fragments and return whatever nodes you want as the next nodes to be played.

UDF_Branch node is a good example of this behavior: it checks the condition and returns either the TrueNode or FalseNode as the next node to be played.

img_3.png

See how the UDF_Branch node implements SelectNextNodes to return either the TrueNode or FalseNode based on the condition check.

img_1.png

노트

It's important to notice that only the first node that returns non-empty array can determine the next nodes to be played. So you have to keep in mind the order of your nodes when you want to customize the next node selection.

It will traverse the nodes in DFS (Depth-First Search) order in the node tree from the base node.

It might depend on the situation and your desired usages with the fragment, but for the most of the time you might don't want a node that has not begun play to affect the next node selection, so checking if the sub node is begun play and returning an empty array if it hasn't is important to avoid unwanted behaviors.

Please notice that the 2 examples above also implement this check!

Changing the starting point of graph on the runtime

When you play a Joint manager, the Joint instance will pick up the first node on the Start Node list and play the Joint from there.

You can manually change this property on the runtime before you start off the Joint to change the point where you want to start your Joint from.

img.png

노트

Notice the array must take base nodes only.