- Home /
Draw inspector of array elements?
Hello community.
The short version is I wish to draw custom inspectors for array elements within the Unity editor. The long version is this:
I am playing around with PropertyDrawer and CustomEditor classes for improving my inspectors in the editor.
I have created custom .asset files from ScriptableObjects as I want to use the .asset files as configurations files as in this post: http://www.jacobpennock.com/Blog/?p=670
First I create the code for the ScriptableObject:
/** Base class */
using UnityEngine;
using System.Collections;
public abstract class Decorator : ScriptableObject
{
}
/** Derived class (value object) */
using UnityEngine;
using System.Collections;
[System.Serializable]
public class DcRigidBody : Decorator
{
public bool freezeVertical = false;
public bool freezeHorizontal = false;
public bool freezeRotation = false;
public float mass = 1.0f;
public float drag = 0.0f;
public float angularDrag = 0.0f;
[Compact]
public Vector3 constantForce = Vector3.zero;
public PhysicMaterial material = null;
}
I turn the script into an .asset file and name the .asset file "RigidBody (falling)". It shows up in the inspector like this:
Inspector A
I then create a second ScriptableObject .asset that contains an array of of the first type of assets:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Decorated : ScriptableObject
{
public List<Decorator> decorators;
}
I name the .asset file "Matter (falling) and it shows up in the inspector like this:
Inspector B
What I wish to do is to create an custom inspector for the array elements, so the inspector for asset A shows up when I expand the array in asset B. This is a mock-up of what I wish to achieve:
Inspector C (combines inspector A and inspector B - mock-up)
So far I have tried to create a custom editor for the ScriptableObject that contains the array. It look like this:
using UnityEngine;
using System.Collections;
using UnityEditor;
[CustomEditor(typeof(Decorated))]
public class DecoratedEditor : Editor
{
override public void OnInspectorGUI ()
{
Decorated decorated = target as Decorated;
EditorGUIUtility.LookLikeControls();
DrawDefaultInspector();
foreach( Decorator d in decorated.decorators )
{
Debug.Log( d.ToString() );
}
}
}
This draws Inspector A and then output the array elements to the console. What I want to do is call something like DrawDefaultInspector() for each array element and let it all render in the same inspector so i end up with the mocked-up Inspector C, but I can't figure out how to do it?
Any advices?
I seem to remember that there's some doc/thread somewhere that says that PropertyDrawers won't work for array elements - in any case, I found I had to do $$anonymous$$e myself.
Answer by Malogic · Apr 24, 2013 at 10:40 AM
Hi.
I came up with a solution using reflection to inspect the array / List<> elements and draw an appropriate GUI. This is my initial (untested version) - screenshot below:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using System.Reflection;
[CustomEditor(typeof(Decorated))]
public class DecoratedEditor : Editor
{
// properties
Object of = null;
override public void OnInspectorGUI ()
{
// DrawDefaultInspector();
// helper vars
Decorated decorated = target as Decorated;
List<Decorator> decorators = decorated.decorators;
// create add button
EditorGUILayout.BeginHorizontal();
bool addButton = GUILayout.Button("Add", GUILayout.Width(70.0f));
if(addButton)
{
if(of != null)
{
decorators.Add(of as Decorator);
of = null;
}
}
of = EditorGUILayout.ObjectField(of, typeof(Decorator), false);
EditorGUILayout.EndHorizontal();
EditorGUILayout.Separator();
// draw inspectors for each element
foreach( Decorator decorator in decorators )
{
// title bar for decorator
EditorGUIUtility.LookLikeControls();
EditorGUILayout.Separator();
EditorGUILayout.BeginHorizontal();
// create remove button
bool removeButton = GUILayout.Button("Remove", GUILayout.Width(70.0f));
if(removeButton)
{
decorators.Remove(decorator);
return;
}
// object selector
object df = EditorGUILayout.ObjectField(decorator, typeof(Decorator), false);
if(df != decorator)
{
int index = decorators.IndexOf(decorator);
decorators[index] = df as Decorator;
}
EditorGUILayout.EndHorizontal();
EditorGUIUtility.LookLikeInspector();
// parse fields using reflection
FieldInfo[] info = decorator.GetType().GetFields();
foreach(FieldInfo field in info )
{
// handle bool
if(field.FieldType == typeof(System.Boolean))
{
bool b = (bool)field.GetValue(decorator);
bool bf = EditorGUILayout.Toggle(field.Name, b);
field.SetValue(decorator, bf);
}
// handle float
if(field.FieldType == typeof(System.Single))
{
float f = (float)field.GetValue(decorator);
float ff = EditorGUILayout.FloatField(field.Name, f);
field.SetValue(decorator, ff);
}
// handle Vector3
if(field.FieldType == typeof(UnityEngine.Vector3))
{
Vector3 v = (Vector3)field.GetValue(decorator);
Vector3 vf = EditorGUILayout.Vector3Field(field.Name, v);
field.SetValue(decorator, vf);
}
}
// store changes
if(GUI.changed)
{
EditorUtility.SetDirty(decorator);
}
}
}
}