1
0
mirror of https://github.com/Siccity/xNode.git synced 2025-12-20 09:16:01 +08:00

Exposed fields and custom editors

This commit is contained in:
Thor Brigsted 2017-09-21 13:22:34 +02:00
parent b7543df012
commit 846f7f30b1
12 changed files with 187 additions and 22 deletions

View File

@ -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));

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 0d2300267781fed46a6d964565309cbf
folderAsset: yes
timeCreated: 1505987971
licenseType: Free
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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();
}
}

View File

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: c4cc83d08877562419d86874dd3587e2
timeCreated: 1505987983
licenseType: Free
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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];

View File

@ -2,11 +2,102 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Reflection;
using System.Linq;
using System;
/// <summary> Base class to derive custom Node editors from. Use this to create your own custom inspectors and editors for your nodes. </summary>
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<T>(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;
}
}

View File

@ -168,7 +168,9 @@ public partial class NodeEditorWindow {
GUILayout.EndHorizontal();
// GUI
NodeEditor nodeEditor = GetNodeEditor(node.GetType());
nodeEditor.target = node;
nodeEditor.OnNodeGUI();
GUILayout.EndArea();

View File

@ -1,20 +1,50 @@
using System.Reflection;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using System;
using UnityEngine;
/// <summary> Contains reflection-related info </summary>
public partial class NodeEditorWindow {
private static Dictionary<Type, NodeEditor> 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<Type, NodeEditor>();
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);
}
}

View File

@ -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));
}
}

View File

@ -43,13 +43,6 @@ public partial class NodeEditorWindow : EditorWindow {
GUI.DragWindow();
}
/*public byte[] ProtoSerialize<T>(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;
}

View File

@ -41,8 +41,10 @@ public class NodePort :ISerializationCallbackReceiver{
}
public void Connect(NodePort port) {
if (connections == null) connections = new List<NodePort>();
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);