Using Odin Inspector with xNode
See original GitHub issueBelow is what you would have to do if you wanted to use Odin to draw the properties of your nodes:
-
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
-
Add a PropertyTree field to NodeEditorBase
protected PropertyTree objectTree;
-
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.
- 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));
}
- 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();
}
- 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);
}
- 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:
Enjoy!
Issue Analytics
- State:
- Created 4 years ago
- Reactions:8
- Comments:25 (10 by maintainers)
Support for Odin Inspector has been merged into the master branch.
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.