Skip to content

Runtime Nodes

CoffeeVampir3 edited this page Apr 11, 2021 · 24 revisions

public abstract class RuntimeNode

For Api see Runtime Node Api

Runtime Node

Runtime Node's are a blueprint for a particular node type, for example if we create a simple Runtime Node like so:

    [RegisterTo(typeof(DialogueGraphBlueprint), "Dialogue Node")]
    public class DialogueNode : RuntimeNode
    {
        [In(false, Capacity.Multi), SerializeField]
        public ValuePort<string> stringInput = new ValuePort<string>();
        [Out(true, Capacity.Multi), SerializeField]
        public ValuePort<string> stringOutput = new ValuePort<string>();
    }

Always helpful to have a visualization of what this will look like in a graph:

Runtime Node Example

Each attribute has it's own documentation, but we'll briefly cover what each does. RegisterTo(Blueprint Type, "Search Path/Search Name") lets us register our node to a parent blueprint (This totally works with inheritence for the curious.) Additionally, we need to give our node a search path for the node search window. You can have any number of directories defined, for example "Cool Nodes/Sweet Nodes/Dialogue Node" would also be totally fine as a path here. An example of what a path of "Dialogue Node" looks like in the search window:

Search Example

[In(false, Capacity.Multi), SerializeField] In defines a port as... well, an input. Here we also see this false refers to the backing value being visible, in our case we said false so it's not going to be visible. Additionally, we said Capacity.Multi which means this port may connect to any number of other ports. Out is the same as in, but instead of in, it's out.

Evaluation

protected override RuntimeNode OnEvaluate(int contextId)

All Runtime Nodes should implement an OnEvaluate function. This is called when the node becomes the active one in the graph. From here, you can really do whatever you want. I mean, it's your graph. But, for example, what if we wanted to read some input dialogue and write it out as a debug message?

    [RegisterTo(typeof(DialogueGraphBlueprint), "Dialogue Node")]
    public class DialogueNode : RuntimeNode
    {
        //Dont show backing value, port can have any number of connections
        [In(false, Capacity.Multi), SerializeField]
        public ValuePort<string> stringInput = new ValuePort<string>();
        [Out(true, Capacity.Multi), SerializeField]
        public ValuePort<string> stringOutput = new ValuePort<string>();

        protected override RuntimeNode OnEvaluate(Context evContext)
        {
            if (!stringInput.IsLinked()) 
                return null; //We have no inputs! Whoops.

            //For each input
            foreach (var link in stringInput.Links)
            {
                //Try getting the value of our link,
                if (stringInput.TryGetValue(link, out var value))
                {
                    //Then debug it!
                    Debug.Log("Value of link: " + value);
                }
            }

            //Afterwards, our node probably wants to go to the next one, if their isin't one
            //though, we're stuck!
            if (!stringOutput.IsLinked()) return null;
            //Otherwise, lets walk to a random node that we're connected to, the connection
            //says multi so we might as well use it!
            var index = (Random.Range(0, stringOutput.Links.Count));
            return stringOutput.Links[index].Node;
        }
    }

The comments should give you a pretty good hint, but lets break it down. stringInput.IsLinked() this will be true if stringInput has any valid links. Similarly, link.TryGetValue<string>(out var value) returns true if the link has a valid connection value which is a string, and if it does it out's it as value which we then Debug.Log. Cool!

Next, we need to move to a different node in the graph, so we check if the output port is connected, just like we did for the input. If it's connected, we simply return a random link's node.

Auto Layout and Etc

Value Ports aren't the only kind of port you can define, here's how you would create a dynamic value port which can create as many ports as you want:

[RegisterTo(typeof(DialogueGraphBlueprint), "Dialogue Node")]
public class DialogueNode : RuntimeNode
{
    [In(Capacity.Multi), SerializeField] 
    public ValuePort<string> stringIn = new ValuePort<string>();
    [Out(Capacity.Single), SerializeField] 
    public DynamicValuePort<string> stringOut = new DynamicValuePort<string>();

example

Static Data is very easily added to nodes, the Auto Layout feature automagically try to create an inspector for any serializable type you define on a node. For example:

[RegisterTo(typeof(DialogueGraphBlueprint), "Dialogue Node")]
public class DialogueNode : RuntimeNode
{
    [In(Capacity.Multi), SerializeField] 
    public ValuePort<string> stringIn = new ValuePort<string>();
    [Out(Capacity.Single), SerializeField] 
    public DynamicValuePort<string> stringOut = new DynamicValuePort<string>();
    [SerializeField]
    public string testDialogue = "";

example

A very important note is that runtime nodes are shared between graph instances. What does that mean? If you have a graph blueprint which is shared by multiple Graph Evaluators, and you want mutable data for each local graph instance, you should use the Blackboard system.