question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Using Odin Inspector with xNode

See original GitHub issue

Below is what you would have to do if you wanted to use Odin to draw the properties of your nodes:

  1. Make NodeEditorBase inherit from OdinEditor public abstract class NodeEditorBase<T, A, K> : OdinEditor where A : Attribute, NodeEditorBase<T, A, K>.INodeEditorAttrib where T : NodeEditorBase<T, A, K> where K : ScriptableObject

  2. Add a PropertyTree field to NodeEditorBase protected PropertyTree objectTree;

  3. Modify NodeEditor In OnBodyGUI() method, initialize the property tree right after serializedObject.Update

public virtual void OnBodyGUI() {
            
            serializedObject.Update();
            
            // Initialize the property tree. This should be done only once, not every frame.
            if (objectTree == null)
            {
                objectTree = PropertyTree.Create(serializedObject);
            }

At this point if you called objectTree.Draw() you would see Odin drawing your fields with their respective attributes. Problem is, xNode will be drawing them too so everything will be rendered twice. Ideally we want xNode to handle the drawing of input and output ports, and Odin to draw everything else.

Further down within OnBodyGUI you will notice a string array named excludes, which xNode uses to skip drawing of some fields. We’re going to make use of that.

  1. Move the string[] excludes = { "m_Script", "graph", "position", "ports" }; line into the class scope. These are things we don’t want drawn, neither by xNode, nor by Odin. We also want to tell Odin to not draw properties marked with input and output attributes.

At this point the code should look something like this:

        // Do not draw, either with xNode or Odin
        string[] excludes = { "m_Script", "graph", "position", "ports" };
        // let xNode handle these
        string[] drawnbyXNode = {"input", "output"}; 

        public virtual void OnHeaderGUI() {
            GUILayout.Label(target.name, NodeEditorResources.styles.nodeHeader, GUILayout.Height(30));
        }
  1. Tell xNode what to draw: Below, in the while loop, we’re making xNode skip anything that is not either input or output.

Further down, DrawWithOdin is called to tell Odin which properties to draw. More on that in a bit.

At the very end, we’re calling window.Repaint(). This is because some Odin attributes may have foldout animations, or interactive controls that change the size of the node based on property states. Without repaint, the nodes, ports and connections may look off-position as node size changes.

 /// <summary> Draws standard field editors for all public fields </summary>
        public virtual void OnBodyGUI() {
            // Unity specifically requires this to save/update any serial object.
            // serializedObject.Update(); must go at the start of an inspector gui, and
            // serializedObject.ApplyModifiedProperties(); goes at the end.
            serializedObject.Update();
            
            // Initialize the property tree. This should be done only once, not every frame.
            if (objectTree == null)
            {
                objectTree = PropertyTree.Create(serializedObject);
            }
            

            // Iterate through serialized properties and draw them like the Inspector (But with ports)
            SerializedProperty iterator = serializedObject.GetIterator();
            //Debug.Log(serializedObject.GetType());
            bool enterChildren = true;
            EditorGUIUtility.labelWidth = 84;
            while (iterator.NextVisible(enterChildren)) {
                enterChildren = false;
                if (drawnbyXNode.Contains(iterator.name)) NodeEditorGUILayout.PropertyField(iterator, true);
            }

            // Iterate through dynamic ports and draw them in the order in which they are serialized
            foreach (XNode.NodePort dynamicPort in target.DynamicPorts) {
                if (NodeEditorGUILayout.IsDynamicPortListPort(dynamicPort)) continue;
                NodeEditorGUILayout.PortField(dynamicPort);
            }
          
            #if ODIN_INSPECTOR
            DrawWithOdin();
            #endif
            
            serializedObject.ApplyModifiedProperties();
            
            // Call repaint so that the graph window elements respond properly to layout changes coming from Odin
            window.Repaint();

        }
  1. Tell Odin what to draw: This is the DrawWithOdin method we called above. It iterates over the PropertyTree object, and draws anything that is not covered by xNode and anything in the excludes array.
        public void DrawWithOdin()
        {
            InspectorUtilities.BeginDrawPropertyTree(objectTree, true);
            objectTree.EnumerateTree(includeChildren: false).ForEach(p =>
            {
                if (!drawnbyXNode.Contains(p.Name) && !excludes.Contains(p.Name))
                {                    
                    p.Draw();
                }
            });
            InspectorUtilities.EndDrawPropertyTree(objectTree);
        }
  1. Create a Node class and try out some Odin attributes Create one of these nodes (or your own) in the node graph and play around with the fields.
using System.Collections;
using System.Collections.Generic;
using Sirenix.OdinInspector;
using UnityEngine;
using XNode;

[CreateNodeMenu("FlowNode")]
public class FlowNode : Node
{
    [SerializeField, PreviewField(100, ObjectFieldAlignment.Left), HideLabel] private Sprite sprite;
    [SerializeField, Input] private FlowNode input;
    [SerializeField, Output] private FlowNode output;
    [SerializeField] private bool someBool;
    [SerializeField, TextArea(5, 5), ShowIf("someBool", animate: true)] private string text;
    
    [Sirenix.OdinInspector.Button]
    private void TestFunc()
    {
        
    }

    public override object GetValue(NodePort port)
    {
        return this;
    }
}

Hopefully it should look something like this: image

Enjoy!

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:8
  • Comments:25 (10 by maintainers)

github_iconTop GitHub Comments

1reaction
Siccitycommented, Oct 15, 2019

Hi,

I would be interested to use this. This link doesn’t seem to work: https://github.com/Siccity/xNode/tree/experimental/odin-inspector-support

And I don’t find experimental branch for it too.

Thanks 😃

Support for Odin Inspector has been merged into the master branch.

1reaction
TorVestergaardcommented, Aug 6, 2019

Looks like you guys are missing a window.Repaint(); call in the if (GUIHelper.RepaintRequested) clause as well - currently it’s not triggering any repaints, it looks like.

Read more comments on GitHub >

github_iconTop Results From Across the Web

NodeInfo | API Documentation | Odin Inspector for Unity
Odin Inspector is a plugin for Unity that lets you enjoy all the workflow benefits of having a powerful, customized and user-friendly editor,...
Read more >
How to Create a Custom Inspector with Odin? - YouTube
Create your first Unity custom inspector without a custom inspector script! Do it all with just a handful of attributes from Odin Inspector....
Read more >
xNode - A general purpose node editor | Page 5
My main concern with evaluation was that when using editor nodes (regular nodes), I observed the entire graph is evaluated multiple times when...
Read more >
com.github.siccity.xnode
For full Odin support, consider using KAJed82's fork ... Custom node inspector code is very similar to regular custom inspector code; Supported from...
Read more >
How do you use Odin Inspector? : r/Unity3D
Just type "using Sirinex.OdinInspector" up the top. Now go to a method and type [Button] above it. Now if you view your component...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found