{"id":1528,"date":"2025-05-08T12:02:59","date_gmt":"2025-05-08T12:02:59","guid":{"rendered":"https:\/\/www.portalsunited.com:443\/?post_type=docs&#038;p=1528"},"modified":"2025-06-10T14:04:56","modified_gmt":"2025-06-10T14:04:56","password":"","slug":"guide-to-implementing-custom-nodes","status":"publish","type":"docs","link":"https:\/\/www.portalsunited.com:443\/docs\/guide-to-implementing-custom-nodes\/","title":{"rendered":"World Builder &#8211; Guide to Implementing Custom Nodes"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\" id=\"guide-to-implementing-custom-nodes\">Introduction<\/h1>\n\n\n\n<p>Custom nodes allow you to extend the functionality of our node editor system with your own specialized logic. This guide walks you through the process of creating and implementing custom nodes that integrate seamlessly with the existing system.<\/p>\n\n\n\n<p>You can download our standard Nodes, which are currently installed in the World Builder and Portal Hopper <a href=\"https:\/\/www.portalsunited.com:443\/wp-content\/uploads\/2025\/06\/Node-Scripts.zip\">here<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"prerequisites\">Prerequisites<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Basic understanding of C# and Unity<\/li>\n\n\n\n<li>Access to the node system codebase<\/li>\n\n\n\n<li>Visual Studio or another C# IDE<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"the-anatomy-of-a-custom-node\">The Anatomy of a Custom Node<\/h2>\n\n\n\n<p>Every custom node in our system follows a consistent structure:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">MyNodes<\/span> <span class=\"hljs-comment\">\/\/ Your namespace here<\/span>\n{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">MyCustomNode<\/span> : <span class=\"hljs-title\">NodeBase<\/span>\n    {\n        <span class=\"hljs-comment\">\/\/ 1. Unique Identifier (required)<\/span>\n        <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">string<\/span> NODE_GUID =&gt; <span class=\"hljs-string\">\"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"<\/span>;\n\n        <span class=\"hljs-comment\">\/\/ 2. Display Name (required)<\/span>\n        <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">string<\/span> NODE_CLASS_NAME =&gt; <span class=\"hljs-string\">\"My Custom Node\"<\/span>;\n\n        <span class=\"hljs-comment\">\/\/ 3. GUID and Name Implementation<\/span>\n        <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">override<\/span> Guid NodeClassGuid =&gt; Guid.Parse(NODE_GUID);\n        <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">override<\/span> <span class=\"hljs-keyword\">string<\/span> NodeClassName =&gt; NODE_CLASS_NAME;\n\n        <span class=\"hljs-comment\">\/\/ 4. Input\/Output Type Definitions<\/span>\n        <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">static<\/span> List&lt;NodeFactory.VarType&gt; InputTypes =&gt; m_InputTypes;\n        <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">static<\/span> List&lt;NodeFactory.VarType&gt; OutputTypes =&gt; m_OutputTypes;\n\n        <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">static<\/span> List&lt;NodeFactory.VarType&gt; m_InputTypes = \n            <span class=\"hljs-keyword\">new<\/span> List&lt;NodeFactory.VarType&gt; { <span class=\"hljs-comment\">\/* Input types go here *\/<\/span> };\n\n        <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">static<\/span> List&lt;NodeFactory.VarType&gt; m_OutputTypes = \n            <span class=\"hljs-keyword\">new<\/span> List&lt;NodeFactory.VarType&gt; { <span class=\"hljs-comment\">\/* Output types go here *\/<\/span> };\n\n        <span class=\"hljs-comment\">\/\/ 5. Input\/Output References<\/span>\n        <span class=\"hljs-keyword\">private<\/span> InputBase m_SomeInput;\n        <span class=\"hljs-keyword\">private<\/span> FloatOutput m_SomeOutput;\n\n        <span class=\"hljs-comment\">\/\/ 6. Constructors (both required)<\/span>\n        <span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-title\">MyCustomNode<\/span>(<span class=\"hljs-params\">NodeTree nodeTree, Guid nodeInstanceGuid<\/span>) :\n            <span class=\"hljs-title\">base<\/span>(<span class=\"hljs-params\">nodeTree, nodeInstanceGuid, <span class=\"hljs-literal\">null<\/span>, m_InputTypes, m_OutputTypes<\/span>)\n        <\/span>{\n            <span class=\"hljs-comment\">\/\/ Constructor initialization<\/span>\n        }\n\n        <span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-title\">MyCustomNode<\/span>(<span class=\"hljs-params\">NodeTree nodeTree, NodeFile nodeFile, NodeFileEntry nodeFileEntry<\/span>) :\n            <span class=\"hljs-title\">base<\/span>(<span class=\"hljs-params\">nodeTree, nodeFileEntry.Identity, nodeFileEntry, m_InputTypes, m_OutputTypes<\/span>)\n        <\/span>{\n            <span class=\"hljs-comment\">\/\/ Load saved data if needed<\/span>\n        }\n\n        <span class=\"hljs-comment\">\/\/ 7. I\/O Setup Method<\/span>\n        <span class=\"hljs-function\"><span class=\"hljs-keyword\">protected<\/span> <span class=\"hljs-keyword\">override<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">MakeIO<\/span>(<span class=\"hljs-params\">NodeFileEntry nodeFileEntry<\/span>)\n        <\/span>{\n            <span class=\"hljs-comment\">\/\/ Configure inputs and outputs<\/span>\n        }\n\n        <span class=\"hljs-comment\">\/\/ 8. Node Functionality Methods<\/span>\n        <span class=\"hljs-comment\">\/\/ Your custom logic here<\/span>\n    }\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"step-by-step-implementation-guide\">Step-by-Step Implementation Guide<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-1-create-a-new-class-file\">Step 1: Create a New Class File<\/h3>\n\n\n\n<p>Create a new C# script file with your node name (e.g., <code>MyCustomNode.cs<\/code>).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-2-define-the-class-structure\">Step 2: Define the Class Structure<\/h3>\n\n\n\n<p>Your class should inherit from <code>NodeBase<\/code> and implement <code>INodeBase<\/code> if needed:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><span class=\"hljs-keyword\">using<\/span> System;\n<span class=\"hljs-keyword\">using<\/span> System.Collections.Generic;\n<span class=\"hljs-keyword\">using<\/span> WorldBuilder.Nodes;\n<span class=\"hljs-keyword\">using<\/span> WorldBuilder.Nodes.IO;\n<span class=\"hljs-keyword\">using<\/span> Worldbuilder.Nodes.FileIO;\n\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">MyNodes<\/span>\n{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">MyCustomNode<\/span> : <span class=\"hljs-title\">NodeBase<\/span>\n    {\n        <span class=\"hljs-comment\">\/\/ Implementation will go here<\/span>\n    }\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-3-generate-a-unique-guid\">Step 3: Generate a Unique GUID<\/h3>\n\n\n\n<p>Every node needs a unique identifier. Generate a GUID using one of these methods:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Option 1<\/strong>: Use Visual Studio&#8217;s Tools &gt; Create GUID<\/li>\n\n\n\n<li><strong>Option 2<\/strong>: Use an online GUID generator<\/li>\n\n\n\n<li><strong>Option 3<\/strong>: Run this C# code: <code>Console.WriteLine(Guid.NewGuid().ToString());<\/code><\/li>\n<\/ul>\n\n\n\n<p>Then add it to your node class:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">string<\/span> NODE_GUID =&gt; <span class=\"hljs-string\">\"b4fc3090-faf0-4417-88b9-92c04a986117\"<\/span>; <span class=\"hljs-comment\">\/\/ Your unique GUID<\/span>\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">string<\/span> NODE_CLASS_NAME =&gt; <span class=\"hljs-string\">\"My Custom Node\"<\/span>;\n\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">override<\/span> Guid NodeClassGuid =&gt; Guid.Parse(NODE_GUID);\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">override<\/span> <span class=\"hljs-keyword\">string<\/span> NodeClassName =&gt; NODE_CLASS_NAME;\n<\/code><\/pre>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><strong>IMPORTANT<\/strong>: Never reuse GUIDs between different node types. Each node class must have its own unique GUID.<\/p>\n<\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-4-define-input-and-output-types\">Step 4: Define Input and Output Types<\/h3>\n\n\n\n<p>Specify what kinds of data your node will accept and produce:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">List<\/span>&lt;NodeFactory.VarType&gt; InputTypes =&gt; m_InputTypes;\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">List<\/span>&lt;NodeFactory.VarType&gt; OutputTypes =&gt; m_OutputTypes;\n\n<span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">List<\/span>&lt;NodeFactory.VarType&gt; m_InputTypes =\n    <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-keyword\">List<\/span>&lt;NodeFactory.VarType&gt; { \n        NodeFactory.VarType.Flow,     <span class=\"hljs-comment\">\/\/ Execution flow input<\/span>\n        NodeFactory.VarType.<span class=\"hljs-keyword\">Float<\/span>     <span class=\"hljs-comment\">\/\/ Float value input<\/span>\n    };\n\n<span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">List<\/span>&lt;NodeFactory.VarType&gt; m_OutputTypes =\n    <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-keyword\">List<\/span>&lt;NodeFactory.VarType&gt; { \n        NodeFactory.VarType.Flow,     <span class=\"hljs-comment\">\/\/ Execution flow output<\/span>\n        NodeFactory.VarType.String    <span class=\"hljs-comment\">\/\/ String value output<\/span>\n    };\n<\/code><\/pre>\n\n\n\n<p>Common variable types:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>NodeFactory.VarType.Flow<\/code> &#8211; Execution flow<\/li>\n\n\n\n<li><code>NodeFactory.VarType.Float<\/code> &#8211; Floating point number<\/li>\n\n\n\n<li><code>NodeFactory.VarType.Int<\/code> &#8211; Integer<\/li>\n\n\n\n<li><code>NodeFactory.VarType.String<\/code> &#8211; Text string<\/li>\n\n\n\n<li><code>NodeFactory.VarType.Bool<\/code> &#8211; Boolean (true\/false)<\/li>\n\n\n\n<li><code>NodeFactory.VarType.GameObject<\/code> &#8211; Unity GameObject reference<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-5-create-input-output-references\">Step 5: Create Input\/Output References<\/h3>\n\n\n\n<p>Create member variables to store references to your inputs and outputs:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>private InputBase m_FlowInput<span class=\"hljs-comment\">;<\/span>\nprivate InputBase m_FloatInput<span class=\"hljs-comment\">;<\/span>\nprivate FloatOutput m_ResultOutput<span class=\"hljs-comment\">;<\/span>\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-6-implement-both-constructors\">Step 6: Implement Both Constructors<\/h3>\n\n\n\n<p>You need two constructors for your node:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><span class=\"hljs-comment\">\/\/ Constructor for new node instances<\/span>\n<span class=\"hljs-keyword\">public<\/span> MyCustomNode(NodeTree nodeTree, Guid nodeInstanceGuid) :\n    base(nodeTree, nodeInstanceGuid, <span class=\"hljs-keyword\">null<\/span>, m_InputTypes, m_OutputTypes)\n{\n    <span class=\"hljs-comment\">\/\/ Any initialization code<\/span>\n}\n\n<span class=\"hljs-comment\">\/\/ Constructor for loading existing nodes<\/span>\n<span class=\"hljs-keyword\">public<\/span> MyCustomNode(NodeTree nodeTree, NodeFile nodeFile, NodeFileEntry nodeFileEntry) :\n    base(nodeTree, nodeFileEntry.Identity, nodeFileEntry, m_InputTypes, m_OutputTypes)\n{\n    <span class=\"hljs-comment\">\/\/ Load any saved data from nodeFile if needed<\/span>\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-7-configure-inputs-and-outputs\">Step 7: Configure Inputs and Outputs<\/h3>\n\n\n\n<p>Override the <code>MakeIO<\/code> method to set up your inputs and outputs:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>protected override void MakeIO(NodeFileEntry nodeFileEntry)\n{\n    \/\/ Set up flow input (execution)\n    m_FlowInput = Inputs&#91;<span class=\"hljs-number\">0<\/span>]<span class=\"hljs-comment\">;<\/span>\n    m_FlowInput.DisplayName = <span class=\"hljs-string\">\"Execute\"<\/span><span class=\"hljs-comment\">;<\/span>\n    m_FlowInput.RunAction = () =&gt; YourActionMethod()<span class=\"hljs-comment\">;<\/span>\n\n    \/\/ Set up value input\n    m_FloatInput = Inputs&#91;<span class=\"hljs-number\">1<\/span>]<span class=\"hljs-comment\">;<\/span>\n    m_FloatInput.DisplayName = <span class=\"hljs-string\">\"Value\"<\/span><span class=\"hljs-comment\">;<\/span>\n\n    \/\/ Set up outputs\n    OutputBase flowOutput = Outputs&#91;<span class=\"hljs-number\">0<\/span>]<span class=\"hljs-comment\">;<\/span>\n    flowOutput.DisplayName = <span class=\"hljs-string\">\"Next\"<\/span><span class=\"hljs-comment\">;<\/span>\n\n    m_ResultOutput = Outputs&#91;<span class=\"hljs-number\">1<\/span>] as FloatOutput<span class=\"hljs-comment\">;<\/span>\n    m_ResultOutput.DisplayName = <span class=\"hljs-string\">\"Result\"<\/span><span class=\"hljs-comment\">;<\/span>\n    m_ResultOutput.SetGetValueFunc(() =&gt; CalculateResult())<span class=\"hljs-comment\">;<\/span>\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"step-8-implement-node-functionality\">Step 8: Implement Node Functionality<\/h3>\n\n\n\n<p>Add methods that implement your node&#8217;s actual behavior:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><span class=\"hljs-function\"><span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">YourActionMethod<\/span>(<span class=\"hljs-params\"><\/span>)\n<\/span>{\n    <span class=\"hljs-comment\">\/\/ Do something when the node is executed<\/span>\n    <span class=\"hljs-keyword\">float<\/span> <span class=\"hljs-keyword\">value<\/span> = m_FloatInput.GetFloat();\n\n    <span class=\"hljs-comment\">\/\/ Your logic here<\/span>\n\n    <span class=\"hljs-comment\">\/\/ Continue execution flow<\/span>\n    Run(); <span class=\"hljs-comment\">\/\/ This triggers the next node in the flow<\/span>\n}\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">float<\/span> <span class=\"hljs-title\">CalculateResult<\/span>(<span class=\"hljs-params\"><\/span>)\n<\/span>{\n    <span class=\"hljs-comment\">\/\/ Calculate and return a value<\/span>\n    <span class=\"hljs-keyword\">return<\/span> m_FloatInput.GetFloat() * <span class=\"hljs-number\">2.0<\/span>f;\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"example-node-implementation\">Example Node Implementation<\/h2>\n\n\n\n<p>Let&#8217;s create a simple node that multiplies a float value by 2:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><span class=\"hljs-keyword\">using<\/span> System;\n<span class=\"hljs-keyword\">using<\/span> System.Collections.Generic;\n<span class=\"hljs-keyword\">using<\/span> WorldBuilder.Nodes;\n<span class=\"hljs-keyword\">using<\/span> WorldBuilder.Nodes.IO;\n<span class=\"hljs-keyword\">using<\/span> Worldbuilder.Nodes.FileIO;\n\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">MyNodes<\/span>\n{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">MyDoubleValueNode<\/span> : <span class=\"hljs-title\">NodeBase<\/span>\n    {\n        <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">string<\/span> NODE_GUID =&gt; <span class=\"hljs-string\">\"2a7b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d\"<\/span>;\n        <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">string<\/span> NODE_CLASS_NAME =&gt; <span class=\"hljs-string\">\"Double Value\"<\/span>;\n\n        <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">override<\/span> Guid NodeClassGuid =&gt; Guid.Parse(NODE_GUID);\n        <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">override<\/span> <span class=\"hljs-keyword\">string<\/span> NodeClassName =&gt; NODE_CLASS_NAME;\n\n        <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">static<\/span> List&lt;NodeFactory.VarType&gt; InputTypes =&gt; m_InputTypes;\n        <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">static<\/span> List&lt;NodeFactory.VarType&gt; OutputTypes =&gt; m_OutputTypes;\n\n        <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">static<\/span> List&lt;NodeFactory.VarType&gt; m_InputTypes =\n            <span class=\"hljs-keyword\">new<\/span> List&lt;NodeFactory.VarType&gt; { NodeFactory.VarType.Float };\n\n        <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">static<\/span> List&lt;NodeFactory.VarType&gt; m_OutputTypes =\n            <span class=\"hljs-keyword\">new<\/span> List&lt;NodeFactory.VarType&gt; { NodeFactory.VarType.Float };\n\n        <span class=\"hljs-keyword\">private<\/span> InputBase m_ValueInput;\n        <span class=\"hljs-keyword\">private<\/span> FloatOutput m_ResultOutput;\n\n        <span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-title\">MyDoubleValueNode<\/span>(<span class=\"hljs-params\">NodeTree nodeTree, Guid nodeInstanceGuid<\/span>) :\n            <span class=\"hljs-title\">base<\/span>(<span class=\"hljs-params\">nodeTree, nodeInstanceGuid, <span class=\"hljs-literal\">null<\/span>, m_InputTypes, m_OutputTypes<\/span>)\n        <\/span>{\n        }\n\n        <span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-title\">MyDoubleValueNode<\/span>(<span class=\"hljs-params\">NodeTree nodeTree, NodeFile nodeFile, NodeFileEntry nodeFileEntry<\/span>) :\n            <span class=\"hljs-title\">base<\/span>(<span class=\"hljs-params\">nodeTree, nodeFileEntry.Identity, nodeFileEntry, m_InputTypes, m_OutputTypes<\/span>)\n        <\/span>{\n        }\n\n        <span class=\"hljs-function\"><span class=\"hljs-keyword\">protected<\/span> <span class=\"hljs-keyword\">override<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">MakeIO<\/span>(<span class=\"hljs-params\">NodeFileEntry nodeFileEntry<\/span>)\n        <\/span>{\n            m_ValueInput = Inputs&#91;<span class=\"hljs-number\">0<\/span>];\n            m_ValueInput.DisplayName = <span class=\"hljs-string\">\"Value\"<\/span>;\n\n            m_ResultOutput = Outputs&#91;<span class=\"hljs-number\">0<\/span>] <span class=\"hljs-keyword\">as<\/span> FloatOutput;\n            m_ResultOutput.DisplayName = <span class=\"hljs-string\">\"Doubled Value\"<\/span>;\n            m_ResultOutput.SetGetValueFunc(CalculateDouble);\n        }\n\n        <span class=\"hljs-function\"><span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">float<\/span> <span class=\"hljs-title\">CalculateDouble<\/span>(<span class=\"hljs-params\"><\/span>)\n        <\/span>{\n            <span class=\"hljs-keyword\">return<\/span> m_ValueInput.GetFloat() * <span class=\"hljs-number\">2.0<\/span>f;\n        }\n    }\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"common-node-types-and-their-implementation\">Common Node Types and Their Implementation<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"value-processing-node\">Value Processing Node<\/h3>\n\n\n\n<p>Like our example above, focuses on transforming values.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"flow-control-node\">Flow Control Node<\/h3>\n\n\n\n<p>Controls execution flow with multiple output paths:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><span class=\"hljs-comment\">\/\/ In MakeIO method<\/span>\nm_ConditionInput = Inputs&#91;<span class=\"hljs-number\">1<\/span>]; <span class=\"hljs-comment\">\/\/ Boolean input<\/span>\nOutputs&#91;<span class=\"hljs-number\">0<\/span>].DisplayName = <span class=\"hljs-string\">\"True\"<\/span>; <span class=\"hljs-comment\">\/\/ First flow output<\/span>\nOutputs&#91;<span class=\"hljs-number\">1<\/span>].DisplayName = <span class=\"hljs-string\">\"False\"<\/span>; <span class=\"hljs-comment\">\/\/ Second flow output<\/span>\n\n<span class=\"hljs-comment\">\/\/ In RunAction method<\/span>\nif (m_ConditionInput.GetBool())\n    RunAt(<span class=\"hljs-number\">0<\/span>); <span class=\"hljs-comment\">\/\/ Run the \"True\" path<\/span>\nelse\n    RunAt(<span class=\"hljs-number\">1<\/span>); <span class=\"hljs-comment\">\/\/ Run the \"False\" path<\/span>\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"event-handling-node\">Event Handling Node<\/h3>\n\n\n\n<p>Responds to system events:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><span class=\"hljs-comment\">\/\/ Subscribe to events in the constructor<\/span>\nEventManager.Instance.OnSomeEvent += HandleEvent;\n\n<span class=\"hljs-comment\">\/\/ Handle the event<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">HandleEvent<\/span>(<span class=\"hljs-params\">EventArgs args<\/span>)\n<\/span>{\n    <span class=\"hljs-comment\">\/\/ Do something and then continue flow<\/span>\n    Run();\n}\n\n<span class=\"hljs-comment\">\/\/ Clean up in Dispose method<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">override<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">Dispose<\/span>(<span class=\"hljs-params\"><\/span>)\n<\/span>{\n    EventManager.Instance.OnSomeEvent -= HandleEvent;\n    <span class=\"hljs-keyword\">base<\/span>.Dispose();\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"advanced-topics\">Advanced Topics<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"saving-custom-data\">Saving Custom Data<\/h3>\n\n\n\n<p>If your node needs to save additional data:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><span class=\"hljs-comment\">\/\/ Override GetExtraData<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">override<\/span> <span class=\"hljs-keyword\">string<\/span> <span class=\"hljs-title\">GetExtraData<\/span>(<span class=\"hljs-params\"><\/span>)\n<\/span>{\n    <span class=\"hljs-keyword\">return<\/span> JsonConvert.SerializeObject(myCustomData);\n}\n\n<span class=\"hljs-comment\">\/\/ Override SetExtraData<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">override<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">SetExtraData<\/span>(<span class=\"hljs-params\"><span class=\"hljs-keyword\">string<\/span> extraData<\/span>)\n<\/span>{\n    myCustomData = JsonConvert.DeserializeObject&lt;MyCustomDataType&gt;(extraData);\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"custom-node-editor-ui\">Custom Node Editor UI<\/h3>\n\n\n\n<p>For nodes that need custom interface elements:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><span class=\"hljs-function\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">override<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">InsertVisual<\/span>(<span class=\"hljs-params\">NodeTreeVisualizer visualizer, VisualElement root, Vector2 position<\/span>)\n<\/span>{\n    <span class=\"hljs-keyword\">base<\/span>.InsertVisual(visualizer, root, position);\n\n    <span class=\"hljs-comment\">\/\/ Add your custom UI elements<\/span>\n    IUIElementFactory elementFactory = NodeTree.ReferenceProvider.ElementFactory;\n    elementFactory.CreateButton(m_NodeBox, <span class=\"hljs-string\">\"My Button\"<\/span>, OnButtonClick);\n}\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">void<\/span> <span class=\"hljs-title\">OnButtonClick<\/span>(<span class=\"hljs-params\"><\/span>)\n<\/span>{\n    <span class=\"hljs-comment\">\/\/ Handle button click<\/span>\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"troubleshooting\">Troubleshooting<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"node-doesn-t-appear-in-editor\">Node Doesn&#8217;t Appear in Editor<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Ensure your namespace is correct<\/li>\n\n\n\n<li>Verify that the GUID is unique and properly formatted<\/li>\n\n\n\n<li>Check that your node class is public<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"node-inputs-outputs-don-t-work\">Node Inputs\/Outputs Don&#8217;t Work<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Make sure the index in <code>Inputs[x]<\/code> matches the order in m_InputTypes<\/li>\n\n\n\n<li>Verify that you&#8217;re using the correct getter method (GetFloat(), GetString(), etc.)<\/li>\n\n\n\n<li>Check that you&#8217;ve called Run() to continue execution flow<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"runtime-errors\">Runtime Errors<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Verify all inputs have proper null checks<\/li>\n\n\n\n<li>Ensure any saved data is properly deserialized<\/li>\n\n\n\n<li>Check for any event subscriptions that might not be unsubscribed<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"conclusion\">Conclusion<\/h2>\n\n\n\n<p>Following this guide, you should now be able to create custom nodes that extend the functionality of the node system. Remember to always generate unique GUIDs for each node type, and to properly set up inputs and outputs.<\/p>\n\n\n\n<p>Feel free to explore the example nodes provided in the documentation to see more complex implementations and patterns.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introduction Custom nodes allow you to extend the functionality of our node editor system with your own specialized logic. This guide walks you through the process of creating and implementing custom nodes that integrate seamlessly with the existing system. You can download our standard Nodes, which are currently installed in the World Builder and Portal&#8230;<\/p>\n","protected":false},"author":5,"featured_media":0,"comment_status":"open","ping_status":"closed","template":"","meta":{"_bbp_topic_count":0,"_bbp_reply_count":0,"_bbp_total_topic_count":0,"_bbp_total_reply_count":0,"_bbp_voice_count":0,"_bbp_anonymous_reply_count":0,"_bbp_topic_count_hidden":0,"_bbp_reply_count_hidden":0,"_bbp_forum_subforum_count":0,"_kad_post_transparent":"","_kad_post_title":"","_kad_post_layout":"","_kad_post_sidebar_id":"","_kad_post_content_style":"","_kad_post_vertical_padding":"","_kad_post_feature":"","_kad_post_feature_position":"","_kad_post_header":false,"_kad_post_footer":false,"footnotes":""},"doc_category":[16],"doc_tag":[],"class_list":["post-1528","docs","type-docs","status-publish","hentry","doc_category-world-builder-extensions"],"year_month":"2025-08","word_count":1378,"total_views":0,"reactions":{"happy":0,"normal":0,"sad":0},"author_info":{"name":"maximilian.winter","author_nicename":"maximilian-winter","author_url":"https:\/\/www.portalsunited.com:443\/author\/maximilian-winter\/"},"doc_category_info":[{"term_name":"World Builder - Extensions","term_url":"https:\/\/www.portalsunited.com:443\/docs-category\/world-builder-extensions\/"}],"doc_tag_info":[],"_links":{"self":[{"href":"https:\/\/www.portalsunited.com:443\/wp-json\/wp\/v2\/docs\/1528","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.portalsunited.com:443\/wp-json\/wp\/v2\/docs"}],"about":[{"href":"https:\/\/www.portalsunited.com:443\/wp-json\/wp\/v2\/types\/docs"}],"author":[{"embeddable":true,"href":"https:\/\/www.portalsunited.com:443\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/www.portalsunited.com:443\/wp-json\/wp\/v2\/comments?post=1528"}],"version-history":[{"count":3,"href":"https:\/\/www.portalsunited.com:443\/wp-json\/wp\/v2\/docs\/1528\/revisions"}],"predecessor-version":[{"id":1683,"href":"https:\/\/www.portalsunited.com:443\/wp-json\/wp\/v2\/docs\/1528\/revisions\/1683"}],"wp:attachment":[{"href":"https:\/\/www.portalsunited.com:443\/wp-json\/wp\/v2\/media?parent=1528"}],"wp:term":[{"taxonomy":"doc_category","embeddable":true,"href":"https:\/\/www.portalsunited.com:443\/wp-json\/wp\/v2\/doc_category?post=1528"},{"taxonomy":"doc_tag","embeddable":true,"href":"https:\/\/www.portalsunited.com:443\/wp-json\/wp\/v2\/doc_tag?post=1528"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}