In Unity gibt es keine Möglichkeit Inspektoren hierarchisch anzuordnen, deshalb dachte ich mir, wäre es doch mal an der Zeit zwei kleine Skripte zu entwickeln, die genau dafür verantwortlich sind.
Das erste Skript, ist der sogenannte StackEditor
, eine einfache Klasse, die vom Editor
erbt und so von allem benutzerdefinierten Inspektoren verwendet werden kann.
using System;
using System.Collections.Generic;
using UnityEditor;
using UObject = UnityEngine.Object;
[Serializable]
public class StackEditor : Editor
{
private Stack<Editor> activeEditors = new Stack<Editor>();
public override void OnInspectorGUI()
{
if (activeEditors.Count > 0)
activeEditors.Peek().OnInspectorGUI();
}
public void Pop()
{
DestroyImmediate(activeEditors.Pop());
}
public void Push<T>(UObject target) where T : Editor, new()
{
var editor = CreateEditor(target, typeof(T));
activeEditors.Push(editor);
if (editor is StackEditorBase)
{
var stackEditor = (StackEditorBase)editor;
stackEditor.StackEditor = this;
stackEditor.Load();
}
Repaint();
}
public void Push(UObject target)
{
var editor = CreateEditor(target);
activeEditors.Push(editor);
if (editor is StackEditorBase)
{
var stackEditor = (StackEditorBase)editor;
stackEditor.StackEditor = this;
stackEditor.Load();
}
Repaint();
}
private void OnDisable()
{
while (activeEditors.Count > 0)
Pop();
}
}
Nun ist es natürlich langweilig, nicht zu wissen, wie der denn genutzt werden soll ... ganz einfach: Dazu gibt es eine StackEditorBase
-Klasse. Diese dient der einfachen Nutzung des StackEditor
s und seinem hierarchischen Aufbau.
using System;
using UnityEditor;
[Serializable]
public class StackEditorBase : Editor
{
public StackEditor StackEditor
{
get; set;
}
public virtual string Title => GetType().Name;
public void Load()
{
OnLoad();
}
protected virtual void OnLoad()
{
}
}
Diese zwei Skripte sollten idealerweise in einem Ordner wie Plugins\StackEditor\Editor
platziert werden.
Genutzt werden kann dies dann wie folgt:
[Serializable]
[CustomEditor(typeof(ContainerObject))]
public class ContainerEditor : StackEditor
{
public override void OnInspectorGUI()
{
EditorGUILayout.LabelField("Container Editor");
EditorGUILayout.Space();
base.OnInspectorGUI();
}
private void OnEnable()
{
Push<ContainerObjectEditor>(target);
}
}
Hier wird Gebrauch von Unitys Resolve-Verhalten von Typen gemacht, da der Container-Editor nicht alle Logik für das Container-Objekt braucht, was ein eigener Editor wird.
[Serializable]
[CustomEditor(typeof(ConainerObject))]
public class ContainerObjectEditor : StackEditorBase
{
[MenuItem("Assets/Create/Container")]
[ContextMenu("Create/Container")]
public static void CreateObject()
{
var path = "";
var obj = Selection.activeObject;
if (!obj)
path = "Assets";
else
path = AssetDatabase.GetAssetPath(obj.GetInstanceID());
if (path.Length > 0)
{
if (!AssetDatabase.IsValidFolder(path))
path = Path.GetDirectoryName(path);
}
var instance = CreateInstance<ContainerObject>();
ProjectWindowUtil.CreateAsset(instance, AssetDatabase.GenerateUniqueAssetPath(path + "/New Container.asset"));
Selection.activeObject = instance;
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.PropertyField(serializedObject.FindProperty("startTime"));
serializedObject.ApplyModifiedProperties();
}
}
Um dann in der Hierarchie zu wechseln, reicht ein StackEditor.Push<>()
bzw. StackEditor.Pop()
. Ersteres geht tiefer, letzteres geht eine Ebene höher.
Möchtest du ein Beispiel-Projekt haben? Lade es dir als .unitypackage herunter.