diff --git a/Example/ExampleNodeGraph.asset b/Example/ExampleNodeGraph.asset
index d42f7ca..b09f280 100644
Binary files a/Example/ExampleNodeGraph.asset and b/Example/ExampleNodeGraph.asset differ
diff --git a/Example/ExampleNodeGraph.asset.meta b/Example/ExampleNodeGraph.asset.meta
index 6ec3d2f..12fe78e 100644
--- a/Example/ExampleNodeGraph.asset.meta
+++ b/Example/ExampleNodeGraph.asset.meta
@@ -1,6 +1,6 @@
fileFormatVersion: 2
-guid: e8c47bc953732464a9bf3a76273d99ef
-timeCreated: 1507916591
+guid: 614b670274b902e44bba59ba861eb1bd
+timeCreated: 1507988229
licenseType: Free
NativeFormatImporter:
mainObjectFileID: 11400000
diff --git a/Example/Nodes/ConstantValue.cs b/Example/Nodes/ConstantValue.cs
deleted file mode 100644
index b0b99ce..0000000
--- a/Example/Nodes/ConstantValue.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-[System.Serializable]
-public class ConstantValue : ExampleNodeBase {
- public float a;
- [Output] public float value;
-
- protected override void Init() {
- name = "Constant Value";
- }
-
- public override object GetValue(NodePort port) {
- return a;
- }
-}
diff --git a/Example/Nodes/DisplayValue.cs b/Example/Nodes/DisplayValue.cs
index d19dd21..ef82b56 100644
--- a/Example/Nodes/DisplayValue.cs
+++ b/Example/Nodes/DisplayValue.cs
@@ -1,6 +1,6 @@
using UnityEngine;
public class DisplayValue : ExampleNodeBase {
- [Input] public float value;
+ [Input(false)] public float value;
}
diff --git a/Example/Nodes/ExampleNodeBase.cs b/Example/Nodes/ExampleNodeBase.cs
index c47cff0..c7ad9ff 100644
--- a/Example/Nodes/ExampleNodeBase.cs
+++ b/Example/Nodes/ExampleNodeBase.cs
@@ -2,7 +2,7 @@
using System.Collections.Generic;
using UnityEngine;
-public class ExampleNodeBase : Node {
+public abstract class ExampleNodeBase : Node {
public float GetInputFloat(string fieldName) {
float result = 0f;
diff --git a/Example/Nodes/MathNode.cs b/Example/Nodes/MathNode.cs
index 475698d..fef8659 100644
--- a/Example/Nodes/MathNode.cs
+++ b/Example/Nodes/MathNode.cs
@@ -2,9 +2,9 @@
[System.Serializable]
public class MathNode : ExampleNodeBase {
- [Input] public float c;
- [Input] public float b;
- [Output] public float result;
+ [Input(true)] public float a;
+ [Input(true)] public float b;
+ [Output(false)] public float result;
public enum MathType { Add, Subtract, Multiply, Divide}
public MathType mathType = MathType.Add;
@@ -13,7 +13,7 @@ public class MathNode : ExampleNodeBase {
}
public override object GetValue(NodePort port) {
- float a = GetInputFloat("c");
+ float a = GetInputFloat("a");
float b = GetInputFloat("b");
switch(port.fieldName) {
diff --git a/Example/Nodes/TestNode.cs b/Example/Nodes/TestNode.cs
new file mode 100644
index 0000000..900ddf4
--- /dev/null
+++ b/Example/Nodes/TestNode.cs
@@ -0,0 +1,19 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+public class TestNode : Node {
+
+ public int i;
+ public float f;
+ public double d;
+ public long l;
+ public bool b;
+ public string s;
+ public Rect r;
+ public Vector2 v2;
+ public Vector3 v3;
+ public Vector4 v4;
+ public Color col;
+ public AnimationCurve a;
+}
diff --git a/Example/Nodes/ConstantValue.cs.meta b/Example/Nodes/TestNode.cs.meta
similarity index 76%
rename from Example/Nodes/ConstantValue.cs.meta
rename to Example/Nodes/TestNode.cs.meta
index e836841..93b01d2 100644
--- a/Example/Nodes/ConstantValue.cs.meta
+++ b/Example/Nodes/TestNode.cs.meta
@@ -1,6 +1,6 @@
fileFormatVersion: 2
-guid: 707240ce8955a0240a7c0c4177d83bf5
-timeCreated: 1505937586
+guid: 47bfd6281f41a10459bcb45bb4cd03f3
+timeCreated: 1507990362
licenseType: Free
MonoImporter:
serializedVersion: 2
diff --git a/Scripts/Editor/NodeEditor.cs b/Scripts/Editor/NodeEditor.cs
index 8da947b..e1777eb 100644
--- a/Scripts/Editor/NodeEditor.cs
+++ b/Scripts/Editor/NodeEditor.cs
@@ -14,8 +14,7 @@ public class NodeEditor {
/// Port handle positions need to be returned to the NodeEditorWindow
public virtual void OnNodeGUI(out Dictionary portPositions) {
DrawDefaultHeaderGUI();
- DrawDefaultNodePortsGUI(out portPositions);
- DrawDefaultNodeBodyGUI();
+ DrawDefaultNodeBodyGUI(out portPositions);
}
protected void DrawDefaultHeaderGUI() {
@@ -23,40 +22,15 @@ public class NodeEditor {
GUILayout.Label(target.name, NodeEditorResources.styles.nodeHeader, GUILayout.Height(30));
}
- /// Draws standard editors for all fields marked with or
- protected void DrawDefaultNodePortsGUI(out Dictionary portPositions) {
+ /// Draws standard field editors for all public fields
+ protected void DrawDefaultNodeBodyGUI(out Dictionary portPositions) {
portPositions = new Dictionary();
- Event e = Event.current;
-
- GUILayout.BeginHorizontal();
-
- //Inputs
- GUILayout.BeginVertical();
- for (int i = 0; i < target.InputCount; i++) {
- Vector2 handlePoint = DrawNodePortGUI(target.inputs[i]);
- portPositions.Add(target.inputs[i], handlePoint);
- }
- GUILayout.EndVertical();
-
- //Outputs
- GUILayout.BeginVertical();
- for (int i = 0; i < target.OutputCount; i++) {
- Vector2 handlePoint = DrawNodePortGUI(target.outputs[i]);
- portPositions.Add(target.outputs[i], handlePoint);
- }
- GUILayout.EndVertical();
-
- GUILayout.EndHorizontal();
- }
-
- /// Draws standard field editors for all public fields
- protected void DrawDefaultNodeBodyGUI() {
FieldInfo[] fields = GetInspectorFields(target);
for (int i = 0; i < fields.Length; i++) {
object[] fieldAttribs = fields[i].GetCustomAttributes(false);
- if (NodeEditorUtilities.HasAttrib(fieldAttribs) || NodeEditorUtilities.HasAttrib(fieldAttribs)) continue;
- DrawFieldInfoDrawerGUI(fields[i]);
+ if (fields[i].Name == "graph" || fields[i].Name == "rect") continue;
+ NodeEditorGUILayout.PropertyField(target, fields[i], portPositions);
}
}
@@ -97,73 +71,6 @@ public class NodeEditor {
return node.GetType().GetFields().Where(f => f.IsPublic || f.GetCustomAttributes(typeof(SerializeField), false) != null).ToArray();
}
- private void DrawFieldInfoDrawerGUI(FieldInfo fieldInfo) {
- Type fieldType = fieldInfo.FieldType;
- string fieldName = fieldInfo.Name;
- string fieldPrettyName = fieldName.PrettifyCamelCase();
- object fieldValue = fieldInfo.GetValue(target);
- object[] fieldAttribs = fieldInfo.GetCustomAttributes(false);
-
- HeaderAttribute headerAttrib;
- if (NodeEditorUtilities.GetAttrib(fieldAttribs, out headerAttrib)) {
- EditorGUILayout.LabelField(headerAttrib.header);
- }
-
- EditorGUI.BeginChangeCheck();
- if (fieldType == typeof(int)) {
- fieldValue = NodeEditorGUILayout.IntField(fieldPrettyName, (int) fieldValue);
- } else if (fieldType == typeof(bool)) {
- fieldValue = EditorGUILayout.Toggle(fieldPrettyName, (bool) fieldValue);
- } else if (fieldType.IsEnum) {
- fieldValue = NodeEditorGUILayout.EnumField(fieldPrettyName, (Enum)fieldValue);
- } else if (fieldType == typeof(string)) {
-
- if (fieldName == "name") return; //Ignore 'name'
- TextAreaAttribute textAreaAttrib;
- if (NodeEditorUtilities.GetAttrib(fieldAttribs, out textAreaAttrib)) {
- fieldValue = EditorGUILayout.TextArea(fieldValue != null ? (string) fieldValue : "");
- } else
- fieldValue = EditorGUILayout.TextField(fieldPrettyName, fieldValue != null ? (string) fieldValue : "");
- } else if (fieldType == typeof(Rect)) {
- if (fieldName == "rect") return; //Ignore 'rect'
- fieldValue = EditorGUILayout.RectField(fieldPrettyName, (Rect) fieldValue);
- } else if (fieldType == typeof(float)) {
- fieldValue = NodeEditorGUILayout.FloatField(fieldPrettyName, (float) fieldValue);
- } else if (fieldType == typeof(double)) {
- fieldValue = NodeEditorGUILayout.DoubleField(fieldPrettyName, (double) fieldValue);
- } else if (fieldType == typeof(long)) {
- fieldValue = NodeEditorGUILayout.LongField(fieldPrettyName, (long) fieldValue);
- } else if (fieldType == typeof(Vector2)) {
- fieldValue = EditorGUILayout.Vector2Field(fieldPrettyName, (Vector2) fieldValue);
- } else if (fieldType == typeof(Vector3)) {
- fieldValue = EditorGUILayout.Vector3Field(new GUIContent(fieldPrettyName), (Vector3) fieldValue);
- } else if (fieldType == typeof(Vector4)) {
- fieldValue = EditorGUILayout.Vector4Field(fieldPrettyName, (Vector4) fieldValue);
- } else if (fieldType == typeof(Color)) {
- Rect rect = EditorGUILayout.GetControlRect();
- rect.width *= 0.5f;
- EditorGUI.LabelField(rect, fieldPrettyName);
- rect.x += rect.width;
- fieldValue = EditorGUI.ColorField(rect, (Color) fieldValue);
- } else if (fieldType == typeof(AnimationCurve)) {
- Rect rect = EditorGUILayout.GetControlRect();
- rect.width *= 0.5f;
- EditorGUI.LabelField(rect, fieldPrettyName);
- rect.x += rect.width;
-
- AnimationCurve curve = fieldValue != null ? (AnimationCurve) fieldValue : new AnimationCurve();
- curve = EditorGUI.CurveField(rect, curve);
- if (fieldValue != curve) fieldInfo.SetValue(target, curve);
- } else if (fieldType.IsSubclassOf(typeof(UnityEngine.Object)) || fieldType == typeof(UnityEngine.Object)) {
- if (fieldName == "graph") return; //Ignore 'graph'
- fieldValue = EditorGUILayout.ObjectField(fieldPrettyName, (UnityEngine.Object) fieldValue, fieldType, true);
- }
- if (EditorGUI.EndChangeCheck()) {
- fieldInfo.SetValue(target, fieldValue);
- EditorUtility.SetDirty(target);
- }
- }
-
public virtual int GetWidth() {
return 200;
}
diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs
index aeed708..95b4600 100644
--- a/Scripts/Editor/NodeEditorAction.cs
+++ b/Scripts/Editor/NodeEditorAction.cs
@@ -137,6 +137,7 @@ public partial class NodeEditorWindow {
Vector2 mousePos = Event.current.mousePosition;
Node newHoverNode = null;
foreach (Node node in graph.nodes) {
+ if (node == null) return;
//Get node position
Vector2 nodePos = GridToWindowPosition(node.rect.position);
Rect windowRect = new Rect(nodePos, new Vector2(node.rect.size.x / zoom, node.rect.size.y / zoom));
diff --git a/Scripts/Editor/NodeEditorAssetModProcessor.cs b/Scripts/Editor/NodeEditorAssetModProcessor.cs
new file mode 100644
index 0000000..a1756f5
--- /dev/null
+++ b/Scripts/Editor/NodeEditorAssetModProcessor.cs
@@ -0,0 +1,35 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEditor;
+using UnityEngine;
+
+public class NodeEditorAssetModProcessor : UnityEditor.AssetModificationProcessor {
+ public static AssetDeleteResult OnWillDeleteAsset(string path, RemoveAssetOptions options) {
+ UnityEngine.Object obj = AssetDatabase.LoadAssetAtPath(path);
+
+ if (!(obj is UnityEditor.MonoScript)) return AssetDeleteResult.DidNotDelete;
+
+ UnityEditor.MonoScript script = obj as UnityEditor.MonoScript;
+ System.Type scriptType = script.GetClass();
+
+ if (scriptType != typeof(Node) && !scriptType.IsSubclassOf(typeof(Node))) return AssetDeleteResult.DidNotDelete;
+
+ //Find ScriptableObjects using this script
+ string[] guids = AssetDatabase.FindAssets("t:" + scriptType);
+ for (int i = 0; i < guids.Length; i++) {
+ string assetpath = AssetDatabase.GUIDToAssetPath(guids[i]);
+ Object[] objs = AssetDatabase.LoadAllAssetRepresentationsAtPath(assetpath);
+ for (int k = 0; k < objs.Length; k++) {
+ Node node = objs[k] as Node;
+ if (node.GetType() == scriptType) {
+ if (node != null && node.graph != null) {
+ Debug.LogWarning(node.name + " of " + node.graph + " depended on deleted script and has been removed automatically.", node.graph);
+ node.graph.RemoveNode(node);
+ }
+ }
+ }
+
+ }
+ return AssetDeleteResult.DidNotDelete;
+ }
+}
\ No newline at end of file
diff --git a/Scripts/Editor/NodeEditorAssetModProcessor.cs.meta b/Scripts/Editor/NodeEditorAssetModProcessor.cs.meta
new file mode 100644
index 0000000..057198b
--- /dev/null
+++ b/Scripts/Editor/NodeEditorAssetModProcessor.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: e515e86efe8160243a68b7c06d730c9c
+timeCreated: 1507982232
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs
index 74b0a86..cc7fca0 100644
--- a/Scripts/Editor/NodeEditorGUI.cs
+++ b/Scripts/Editor/NodeEditorGUI.cs
@@ -116,6 +116,8 @@ public partial class NodeEditorWindow {
/// Draws all connections
public void DrawConnections() {
foreach (Node node in graph.nodes) {
+ //If a null node is found, return. This can happen if the nodes associated script is deleted. It is currently not possible in Unity to delete a null asset.
+ if (node == null) return;
for (int i = 0; i < node.OutputCount; i++) {
NodePort output = node.outputs[i];
@@ -126,6 +128,7 @@ public partial class NodeEditorWindow {
NodePort input = output.GetConnection(k);
if (input == null) return; //If a script has been updated and the port doesn't exist, it is removed and null is returned. If this happens, return.
+ if (!_portConnectionPoints.ContainsKey(input)) return;
Vector2 to = _portConnectionPoints[input].center;
DrawConnection(from, to, NodeEditorPreferences.GetTypeColor(output.type));
}
@@ -148,6 +151,7 @@ public partial class NodeEditorWindow {
BeginZoomed(position, zoom);
foreach (Node node in graph.nodes) {
+ if (node == null) return;
NodeEditor nodeEditor = GetNodeEditor(node.GetType());
nodeEditor.target = node;
diff --git a/Scripts/Editor/NodeEditorGUILayout.cs b/Scripts/Editor/NodeEditorGUILayout.cs
index e4d3b39..9683c70 100644
--- a/Scripts/Editor/NodeEditorGUILayout.cs
+++ b/Scripts/Editor/NodeEditorGUILayout.cs
@@ -1,6 +1,8 @@
using System;
using System.Collections;
using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
using UnityEditor;
using UnityEngine;
@@ -9,6 +11,115 @@ public class NodeEditorGUILayout {
private static double tempValue;
+ public static object PortField(string label, object value, NodePort port, bool fallback, out Vector2 portPosition) {
+ if (fallback) value = PropertyField(label, value);
+ else EditorGUILayout.LabelField(label);
+
+ Rect rect = GUILayoutUtility.GetLastRect();
+ if (port.direction == NodePort.IO.Input) rect.position = rect.position - new Vector2(16, 0);
+ if (port.direction == NodePort.IO.Output) rect.position = rect.position + new Vector2(rect.width, 0);
+ rect.size = new Vector2(16, 16);
+
+ Color col = GUI.color;
+ GUI.color = new Color32(90, 97, 105, 255);
+ GUI.DrawTexture(rect, NodeEditorResources.dotOuter);
+ GUI.color = NodeEditorPreferences.GetTypeColor(port.type);
+ GUI.DrawTexture(rect, NodeEditorResources.dot);
+ GUI.color = col;
+ portPosition = rect.center;
+
+ return value;
+ }
+
+ public static object PropertyField(Node target, FieldInfo fieldInfo, Dictionary portPositions) {
+ Type fieldType = fieldInfo.FieldType;
+ string fieldName = fieldInfo.Name;
+ string fieldPrettyName = fieldName.PrettifyCamelCase();
+ object fieldValue = fieldInfo.GetValue(target);
+ object[] fieldAttribs = fieldInfo.GetCustomAttributes(false);
+
+ HeaderAttribute headerAttrib;
+ if (NodeEditorUtilities.GetAttrib(fieldAttribs, out headerAttrib)) {
+ EditorGUILayout.LabelField(headerAttrib.header);
+ }
+
+ Node.OutputAttribute outputAttrib;
+ Node.InputAttribute inputAttrib;
+
+ EditorGUI.BeginChangeCheck();
+ if (NodeEditorUtilities.GetAttrib(fieldAttribs, out inputAttrib)) {
+ NodePort port = target.GetPortByFieldName(fieldName);
+ Vector2 portPos;
+ fieldValue = PortField(fieldPrettyName, fieldValue, port, inputAttrib.fallback, out portPos);
+ portPositions.Add(port, portPos);
+ } else if (NodeEditorUtilities.GetAttrib(fieldAttribs, out outputAttrib)) {
+ NodePort port = target.GetPortByFieldName(fieldName);
+ Vector2 portPos;
+ fieldValue = PortField(fieldPrettyName, fieldValue, port, outputAttrib.fallback, out portPos);
+ portPositions.Add(port, portPos);
+ } else {
+ fieldValue = PropertyField(fieldPrettyName, fieldValue);
+ }
+
+ if (EditorGUI.EndChangeCheck()) {
+ fieldInfo.SetValue(target, fieldValue);
+ EditorUtility.SetDirty(target);
+ }
+ return fieldValue;
+ }
+
+ public static object PropertyField(string label, object value) {
+ if (value is int) return IntField(label, (int) value);
+ else if (value is float) return FloatField(label, (float) value);
+ else if (value is double) return DoubleField(label, (double) value);
+ else if (value is long) return LongField(label, (long) value);
+ else if (value is bool) return Toggle(label, (bool) value);
+ else if (value is Enum) return EnumField(label, (Enum) value);
+ else if (value is string) return TextField(label, (string) value);
+ else if (value is Rect) return RectField(label, (Rect) value);
+ else if (value is Vector2) return Vector2Field(label, (Vector2) value);
+ else if (value is Vector3) return Vector3Field(label, (Vector3) value);
+ else if (value is Vector4) return Vector4Field(label, (Vector4) value);
+ else if (value is Color) return ColorField(label, (Color) value);
+ else if (value is AnimationCurve) return CurveField(label, (AnimationCurve) value);
+ else if (value == null || value.GetType().IsSubclassOf(typeof(UnityEngine.Object)) || value.GetType() == typeof(UnityEngine.Object)) return ObjectField(label, (UnityEngine.Object) value);
+ else return value;
+ }
+ public static Rect GetRect(string label) {
+ Rect rect = EditorGUILayout.GetControlRect();
+ rect.width *= 0.5f;
+ EditorGUI.LabelField(rect, label);
+ rect.x += rect.width;
+ return rect;
+ }
+ public static UnityEngine.Object ObjectField(string label, UnityEngine.Object value) {
+ return EditorGUI.ObjectField(GetRect(label), value, value.GetType(), true);
+ }
+ public static AnimationCurve CurveField(string label, AnimationCurve value) {
+ if (value == null) value = new AnimationCurve();
+ return EditorGUI.CurveField(GetRect(label), value);
+ }
+ public static Color ColorField(string label, Color value) {
+ return EditorGUI.ColorField(GetRect(label), value);
+ }
+ public static Vector4 Vector4Field(string label, Vector4 value) {
+ return EditorGUILayout.Vector4Field(label, value);
+ }
+ public static Vector3 Vector3Field(string label, Vector3 value) {
+ return EditorGUILayout.Vector3Field(label, value);
+ }
+ public static Vector2 Vector2Field(string label, Vector2 value) {
+ return EditorGUILayout.Vector2Field(label, value);
+ }
+ public static Rect RectField(string label, Rect value) {
+ return EditorGUILayout.RectField(label, value);
+ }
+ public static string TextField(string label, string value) {
+ return EditorGUI.TextField(GetRect(label), value);
+ }
+ public static bool Toggle(string label, bool value) {
+ return EditorGUI.Toggle(GetRect(label), value);
+ }
public static int IntField(string label, int value) {
GUIUtility.GetControlID(FocusType.Passive);
Rect rect = EditorGUILayout.GetControlRect();
@@ -119,10 +230,6 @@ public class NodeEditorGUILayout {
}
}
public static Enum EnumField(string label, Enum value) {
- Rect rect = EditorGUILayout.GetControlRect();
- rect.width *= 0.5f;
- EditorGUI.LabelField(rect, label);
- rect.x += rect.width;
- return EditorGUI.EnumPopup(rect, value);
+ return EditorGUI.EnumPopup(GetRect(label), value);
}
}
\ No newline at end of file
diff --git a/Scripts/Editor/NodeEditorResources.cs b/Scripts/Editor/NodeEditorResources.cs
index 0b3bf3b..7ec60ad 100644
--- a/Scripts/Editor/NodeEditorResources.cs
+++ b/Scripts/Editor/NodeEditorResources.cs
@@ -48,7 +48,7 @@ public static class NodeEditorResources {
nodeBody = new GUIStyle();
nodeBody.normal.background = NodeEditorResources.nodeBody;
nodeBody.border = new RectOffset(32, 32, 32, 32);
- nodeBody.padding = new RectOffset(10, 10, 2, 2);
+ nodeBody.padding = new RectOffset(16, 16, 4, 6);
}
}
diff --git a/Scripts/Node.cs b/Scripts/Node.cs
index 69f39e3..fd6e23a 100644
--- a/Scripts/Node.cs
+++ b/Scripts/Node.cs
@@ -9,7 +9,7 @@ using UnityEngine;
public abstract class Node : ScriptableObject {
/// Name of the node
- [NonSerialized] public NodeGraph graph;
+ [SerializeField] public NodeGraph graph;
[SerializeField] public Rect rect = new Rect(0, 0, 200, 200);
/// Input s. It is recommended not to modify these at hand. Instead, see
[SerializeField] public List inputs = new List();
@@ -99,12 +99,14 @@ public abstract class Node : ScriptableObject {
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
public class InputAttribute : Attribute {
- public InputAttribute() { }
+ public bool fallback;
+ public InputAttribute(bool fallback) { this.fallback = fallback; }
}
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
public class OutputAttribute : Attribute {
- public OutputAttribute() { }
+ public bool fallback;
+ public OutputAttribute(bool fallback) { this.fallback = fallback; }
}
private void GetPorts() {