diff --git a/Examples/Nodes/BaseNode.cs b/Examples/Nodes/BaseNode.cs
index 6ee4621..bd34ef1 100644
--- a/Examples/Nodes/BaseNode.cs
+++ b/Examples/Nodes/BaseNode.cs
@@ -4,7 +4,12 @@
public class BaseNode : Node {
public bool concat;
-
+ [TextArea]
+ public string SomeString;
+ [Header("New stuff")]
+ public Color col;
+ public AnimationCurve anim;
+ public Vector3 vec;
protected override void Init() {
inputs = new NodePort[2];
inputs[0] = CreateNodeInput("IntInput", typeof(int));
diff --git a/Examples/Nodes/Editor.meta b/Examples/Nodes/Editor.meta
new file mode 100644
index 0000000..40beab9
--- /dev/null
+++ b/Examples/Nodes/Editor.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 0d2300267781fed46a6d964565309cbf
+folderAsset: yes
+timeCreated: 1505987971
+licenseType: Free
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Examples/Nodes/Editor/MathNodeEditor.cs b/Examples/Nodes/Editor/MathNodeEditor.cs
new file mode 100644
index 0000000..6dbf0f2
--- /dev/null
+++ b/Examples/Nodes/Editor/MathNodeEditor.cs
@@ -0,0 +1,12 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+[CustomNodeEditor(typeof(MathNode))]
+public class AddNodeEditor : NodeEditor {
+
+ public override void OnNodeGUI() {
+ GUILayout.Label("YEAH CUSTOM");
+ base.OnNodeGUI();
+ }
+}
diff --git a/Examples/Nodes/Editor/MathNodeEditor.cs.meta b/Examples/Nodes/Editor/MathNodeEditor.cs.meta
new file mode 100644
index 0000000..57c1490
--- /dev/null
+++ b/Examples/Nodes/Editor/MathNodeEditor.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: c4cc83d08877562419d86874dd3587e2
+timeCreated: 1505987983
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Examples/Nodes/AddNode.cs b/Examples/Nodes/MathNode.cs
similarity index 70%
rename from Examples/Nodes/AddNode.cs
rename to Examples/Nodes/MathNode.cs
index fc6de57..adf593c 100644
--- a/Examples/Nodes/AddNode.cs
+++ b/Examples/Nodes/MathNode.cs
@@ -1,9 +1,11 @@
using UnityEngine;
[System.Serializable]
-public class AddNode : Node {
+public class MathNode : Node {
public int someValue;
+ public enum MathType { Add, Subtract, Multiply, Divide}
+ public MathType mathType = MathType.Add;
protected override void Init() {
inputs = new NodePort[2];
diff --git a/Examples/Nodes/AddNode.cs.meta b/Examples/Nodes/MathNode.cs.meta
similarity index 100%
rename from Examples/Nodes/AddNode.cs.meta
rename to Examples/Nodes/MathNode.cs.meta
diff --git a/Scripts/Editor/NodeEditor.cs b/Scripts/Editor/NodeEditor.cs
index 22a73f5..8ef0317 100644
--- a/Scripts/Editor/NodeEditor.cs
+++ b/Scripts/Editor/NodeEditor.cs
@@ -2,11 +2,102 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
+using System.Reflection;
+using System.Linq;
+using System;
/// Base class to derive custom Node editors from. Use this to create your own custom inspectors and editors for your nodes.
-public class NodeEditor : Editor {
-
- public override void OnInspectorGUI() {
- base.OnInspectorGUI();
+[CustomNodeEditor(typeof(Node))]
+public class NodeEditor {
+
+ public Node target;
+
+ public virtual void OnNodeGUI() {
+ DrawDefaultNodeGUI();
+ }
+
+ public void DrawDefaultNodeGUI() {
+ FieldInfo[] fields = GetInspectorFields(target);
+ for (int i = 0; i < fields.Length; i++) {
+ Type fieldType = fields[i].FieldType;
+ string fieldName = fields[i].Name;
+ object fieldValue = fields[i].GetValue(target);
+ object[] fieldAttribs = fields[i].GetCustomAttributes(false);
+
+ HeaderAttribute headerAttrib;
+ if (GetAttrib(fieldAttribs, out headerAttrib)) {
+ EditorGUILayout.LabelField(headerAttrib.header);
+ }
+
+ EditorGUI.BeginChangeCheck();
+ if (fieldType == typeof(int)) {
+ fieldValue = EditorGUILayout.IntField(fieldName, (int)fieldValue);
+ }
+ else if (fieldType == typeof(bool)) {
+ fieldValue = EditorGUILayout.Toggle(fieldName, (bool)fieldValue);
+ }
+ else if (fieldType.IsEnum) {
+ fieldValue = EditorGUILayout.EnumPopup(fieldName, (Enum)fieldValue);
+ }
+ else if (fieldType == typeof(string)) {
+ TextAreaAttribute textAreaAttrib;
+ if (GetAttrib(fieldAttribs, out textAreaAttrib)) {
+ fieldValue = EditorGUILayout.TextArea(fieldValue != null ? (string)fieldValue : "");
+ }
+ else
+ fieldValue = EditorGUILayout.TextField(fieldName, fieldValue != null ? (string)fieldValue : "");
+ }
+ else if (fieldType == typeof(Rect)) {
+ if (fieldName == "position") continue;
+ fieldValue = EditorGUILayout.RectField(fieldName, (Rect)fieldValue);
+ }
+ else if (fieldType == typeof(float)) {
+ fieldValue = EditorGUILayout.FloatField(fieldName, (float)fieldValue);
+ }
+ else if (fieldType == typeof(Vector2)) {
+ fieldValue = EditorGUILayout.Vector2Field(fieldName, (Vector2)fieldValue);
+ }
+ else if (fieldType == typeof(Vector3)) {
+ fieldValue = EditorGUILayout.Vector2Field(fieldName, (Vector3)fieldValue);
+ }
+ else if (fieldType == typeof(Vector4)) {
+ fieldValue = EditorGUILayout.Vector2Field(fieldName, (Vector4)fieldValue);
+ }
+ else if (fieldType == typeof(Color)) {
+ fieldValue = EditorGUILayout.ColorField(fieldName, (Color)fieldValue);
+ }
+ else if (fieldType == typeof(AnimationCurve)) {
+ fieldValue = EditorGUILayout.CurveField(fieldName, fieldValue != null ? (AnimationCurve)fieldValue : new AnimationCurve());
+ }
+ else if (fieldType.IsSubclassOf(typeof(UnityEngine.Object)) || fieldType == typeof(UnityEngine.Object)) {
+ fieldValue = EditorGUILayout.ObjectField(fieldName, (UnityEngine.Object)fieldValue, fieldType, true);
+ }
+
+ if (EditorGUI.EndChangeCheck()) {
+ fields[i].SetValue(target, fieldValue);
+ }
+ }
+ }
+ private static FieldInfo[] GetInspectorFields(Node node) {
+ return node.GetType().GetFields().Where(f => f.IsPublic).ToArray();
+ }
+
+ private static bool GetAttrib(object[] attribs, out T attribOut) where T : Attribute {
+ for (int i = 0; i < attribs.Length; i++) {
+ if (attribs[i].GetType() == typeof(T)) {
+ attribOut = attribs[i] as T;
+ return true;
+ }
+ }
+ attribOut = null;
+ return false;
+ }
+}
+
+[AttributeUsage(AttributeTargets.Class)]
+public class CustomNodeEditorAttribute : Attribute {
+ public Type inspectedType;
+ public CustomNodeEditorAttribute(Type inspectedType) {
+ this.inspectedType = inspectedType;
}
}
diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs
index 70db270..e036aa9 100644
--- a/Scripts/Editor/NodeEditorGUI.cs
+++ b/Scripts/Editor/NodeEditorGUI.cs
@@ -168,7 +168,9 @@ public partial class NodeEditorWindow {
GUILayout.EndHorizontal();
- // GUI
+ NodeEditor nodeEditor = GetNodeEditor(node.GetType());
+ nodeEditor.target = node;
+ nodeEditor.OnNodeGUI();
GUILayout.EndArea();
diff --git a/Scripts/Editor/NodeEditorReflection.cs b/Scripts/Editor/NodeEditorReflection.cs
index 1334bba..ce7e7e8 100644
--- a/Scripts/Editor/NodeEditorReflection.cs
+++ b/Scripts/Editor/NodeEditorReflection.cs
@@ -1,20 +1,50 @@
-using System.Reflection;
+using System.Collections.Generic;
+using System.Reflection;
using System.Linq;
using System;
+using UnityEngine;
/// Contains reflection-related info
public partial class NodeEditorWindow {
+ private static Dictionary customNodeEditor;
public static Type[] nodeTypes { get { return _nodeTypes != null ? _nodeTypes : _nodeTypes = GetNodeTypes(); } }
private static Type[] _nodeTypes;
+ public static NodeEditor GetNodeEditor(Type node) {
+ if (customNodeEditor == null) CacheCustomNodeEditors();
+ if (customNodeEditor.ContainsKey(node)) return customNodeEditor[node];
+ return customNodeEditor[typeof(Node)];
+ }
+
public static Type[] GetNodeTypes() {
//Get all classes deriving from Node via reflection
- Type derivedType = typeof(Node);
- Assembly assembly = Assembly.GetAssembly(derivedType);
- return assembly.GetTypes().Where(t =>
- t != derivedType &&
- derivedType.IsAssignableFrom(t)
+ return GetDerivedTypes(typeof(Node));
+ }
+
+ public static void CacheCustomNodeEditors(){
+ customNodeEditor = new Dictionary();
+ customNodeEditor.Add(typeof(Node), new NodeEditor());
+ //Get all classes deriving from NodeEditor via reflection
+ Type[] nodeEditors = GetDerivedTypes(typeof(NodeEditor));
+ for (int i = 0; i < nodeEditors.Length; i++) {
+ var attribs = nodeEditors[i].GetCustomAttributes(typeof(CustomNodeEditorAttribute), false);
+ if (attribs == null || attribs.Length == 0) continue;
+ CustomNodeEditorAttribute attrib = attribs[0] as CustomNodeEditorAttribute;
+ customNodeEditor.Add(attrib.inspectedType, Activator.CreateInstance(nodeEditors[i]) as NodeEditor);
+ }
+ }
+
+ public static Type[] GetDerivedTypes(Type baseType) {
+ //Get all classes deriving from baseType via reflection
+ Assembly assembly = Assembly.GetAssembly(baseType);
+ return assembly.GetTypes().Where(t =>
+ t != baseType &&
+ baseType.IsAssignableFrom(t)
).ToArray();
}
+
+ public static object ObjectFromType(Type type) {
+ return Activator.CreateInstance(type);
+ }
}
diff --git a/Scripts/Editor/NodeEditorToolbar.cs b/Scripts/Editor/NodeEditorToolbar.cs
index 2ea2857..19bc890 100644
--- a/Scripts/Editor/NodeEditorToolbar.cs
+++ b/Scripts/Editor/NodeEditorToolbar.cs
@@ -12,7 +12,7 @@ public partial class NodeEditorWindow {
if (DropdownButton("Edit", 50)) EditContextMenu();
if (DropdownButton("View", 50)) { }
if (DropdownButton("Settings", 70)) { }
- if (DropdownButton("Tools", 50)) { }
+ if (DropdownButton("Tools", 50)) ToolsContextMenu();
if (IsHoveringNode) {
GUILayout.Space(20);
string hoverInfo = hoveredNode.GetType().ToString();
@@ -44,4 +44,11 @@ public partial class NodeEditorWindow {
contextMenu.DropDown(new Rect(5f, 17f, 0f, 0f));
}
+
+ public void ToolsContextMenu() {
+ GenericMenu contextMenu = new GenericMenu();
+ contextMenu.AddItem(new GUIContent("Debug Custom Node Editors"), false, () => CacheCustomNodeEditors());
+
+ contextMenu.DropDown(new Rect(5f, 17f, 0f, 0f));
+ }
}
diff --git a/Scripts/Editor/NodeEditorWindow.cs b/Scripts/Editor/NodeEditorWindow.cs
index 13eaaa2..9e09791 100644
--- a/Scripts/Editor/NodeEditorWindow.cs
+++ b/Scripts/Editor/NodeEditorWindow.cs
@@ -43,13 +43,6 @@ public partial class NodeEditorWindow : EditorWindow {
GUI.DragWindow();
}
- /*public byte[] ProtoSerialize(T value) {
- using (var ms = new MemoryStream()) {
- ProtoBuf.Serializer.Serialize(ms, value);
- return ms.ToArray();
- }
- }*/
-
public Vector2 WindowToGridPosition(Vector2 windowPosition) {
return (windowPosition - (position.size * 0.5f) - (panOffset / zoom)) * zoom;
}
diff --git a/Scripts/NodePort.cs b/Scripts/NodePort.cs
index 0ac5359..2cea5de 100644
--- a/Scripts/NodePort.cs
+++ b/Scripts/NodePort.cs
@@ -41,8 +41,10 @@ public class NodePort :ISerializationCallbackReceiver{
}
public void Connect(NodePort port) {
+ if (connections == null) connections = new List();
+ if (port == null) { Debug.LogWarning("Cannot connect to null port"); return; }
if (port == this) { Debug.LogWarning("Attempting to connect port to self."); return; }
- if (connections.Contains(port)) { Debug.LogWarning("Port already connected."); return; }
+ if (connections.Contains(port)) { Debug.LogWarning("Port already connected. "); return; }
if (direction == port.direction) { Debug.LogWarning("Cannot connect two " + (direction == IO.Input ? "input" : "output") + " connections"); return; }
connections.Add(port);
port.connections.Add(this);