- Home /
Re-orderable object lists in inspector
Hello. I have a list of serializable objects to represent entries in a library. I want to be able to order them in the inspector, just for ease of work. I know this is a common question, so I've looked up some answers and found a plugin by rotorz. This works like a charm for simple type lists, but not for lists of a serialized object class.
Any ideas? What is your way around it?
Answer by troien · Nov 07, 2014 at 03:35 PM
The way I should do that is by making a custom inspector.
Since Unity 4.5 There is this class called UnityEditorInternal.ReorderableList. Sadly it is not documented, but it is a very nice class. (The result should be compareable to your list)
This is a class that can be used to draw a list in the inspector the way Unity draws some of it's own lists (Like the list used to add sorting layers for instance)
Since you are talking about serializable objects, I assume you are talking about classes that have the Serializable attribute, and not MonoBehaviours that are serializable by default.
To implement this you need a Class that is Serializable (Or MonoBehaviour)
using UnityEngine;
using System;
[Serializable]
public class ListItemExample
{
public bool boolValue;
public string stringvalue;
}
Then you need the MonoBehaviour that has a list you want to show in the inspector
using UnityEngine;
using System.Collections.Generic;
public class ListExample : MonoBehaviour
{
[HideInInspector] // we do this beacause we call base.OnInspectorGUI(); And we don't want that to draw the list aswell.
public List<ListItemExample> list;
}
And then the place where the magic happens. The custom inspector.
using UnityEngine;
using UnityEditor;
using UnityEditorInternal;
[CustomEditor(typeof(ListExample))]
public class listExampleInspector : Editor
{
private ReorderableList reorderableList;
private ListExample listExample
{
get
{
return target as ListExample;
}
}
private void OnEnable()
{
reorderableList = new ReorderableList(listExample.list,typeof(ListItemExample), true, true, true, true);
// This could be used aswell, but I only advise this your class inherrits from UnityEngine.Object or has a CustomPropertyDrawer
// Since you'll find your item using: serializedObject.FindProperty("list").GetArrayElementAtIndex(index).objectReferenceValue
// which is a UnityEngine.Object
// reorderableList = new ReorderableList(serializedObject, serializedObject.FindProperty("list"), true, true, true, true);
// Add listeners to draw events
reorderableList.drawHeaderCallback += DrawHeader;
reorderableList.drawElementCallback += DrawElement;
reorderableList.onAddCallback += AddItem;
reorderableList.onRemoveCallback += RemoveItem;
}
private void OnDisable()
{
// Make sure we don't get memory leaks etc.
reorderableList.drawHeaderCallback -= DrawHeader;
reorderableList.drawElementCallback -= DrawElement;
reorderableList.onAddCallback -= AddItem;
reorderableList.onRemoveCallback -= RemoveItem;
}
/// <summary>
/// Draws the header of the list
/// </summary>
/// <param name="rect"></param>
private void DrawHeader(Rect rect)
{
GUI.Label(rect, "Our fancy reorderable list");
}
/// <summary>
/// Draws one element of the list (ListItemExample)
/// </summary>
/// <param name="rect"></param>
/// <param name="index"></param>
/// <param name="active"></param>
/// <param name="focused"></param>
private void DrawElement(Rect rect, int index, bool active, bool focused)
{
ListItemExample item = listExample.list[index];
EditorGUI.BeginChangeCheck();
item.boolValue = EditorGUI.Toggle(new Rect(rect.x, rect.y, 18, rect.height), item.boolValue);
item.stringvalue = EditorGUI.TextField(new Rect(rect.x + 18, rect.y, rect.width - 18, rect.height), item.stringvalue);
if (EditorGUI.EndChangeCheck())
{
EditorUtility.SetDirty(target);
}
// If you are using a custom PropertyDrawer, this is probably better
// EditorGUI.PropertyField(rect, serializedObject.FindProperty("list").GetArrayElementAtIndex(index));
// Although it is probably smart to cach the list as a private variable ;)
}
private void AddItem(ReorderableList list)
{
listExample.list.Add(new ListItemExample());
EditorUtility.SetDirty(target);
}
private void RemoveItem(ReorderableList list)
{
listExample.list.RemoveAt(list.index);
EditorUtility.SetDirty(target);
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
// Actually draw the list in the inspector
reorderableList.DoLayoutList();
}
}
This is then how it looks in the editor:
This works even better if you use a CustomPropertyDrawer (for the link: go to the "WRITING A PROPERTYDRAWER FOR A CUSTOM SERIALIZABLE CLASS" part) for you custom Serializable class. Maybe the solution you are using now also works when you use a custompropertydrawer, but I can't tell for sure because I never used this plugin ;)
THAN$$anonymous$$ YOU FOR GUIDING US THROUGH THIS YOU'RE A LEGEND!!!
Hi @troien, thank you for this explanation on this complex subject. I'm currently trying to implement this, but with different properties type.
I want the RouteTarget class's Events list to be reorderable. This is the RouteTarget's class :
public class RouteTarget : $$anonymous$$onoBehaviour {
[HideInInspector]
public List<UnityEvent> Events;
internal int CurrentEventId = 0;
public void ExecuteNextEvent()
{
if (CurrentEventId < Events.Count)
{
Events[CurrentEventId].Invoke();
CurrentEventId += 1;
}
}
}
and it's custom editor :
using UnityEngine;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine.Events;
[CustomEditor(typeof(RouteTarget))]
public class RouteTargetCustomEditor : Editor
{
private ReorderableList list;
private RouteTarget routetarget
{
get
{
return target as RouteTarget;
}
}
private void OnEnable()
{
list = new ReorderableList(routetarget.Events, typeof(UnityEvent), true, true, true, true);
list.drawElementCallback += DrawElement;
}
private void OnDisable()
{
list.drawElementCallback -= DrawElement;
}
private void DrawElement(Rect rect, int index, bool active, bool focused)
{
UnityEvent item = routetarget.Events[index];
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
// Actually draw the list in the inspector
list.DoLayoutList();
}
}
I understand how all this works, but my problem is that I don't find how to display my list's elements. You use item.stringvalue for string type and item.boolValue for bool types, but how to do that with a UnityEvent type ?
Thank you very much for your help.
What I think would work is to use just the SerializedProperty of the UnityEvent. Sinse you want the default display of a UnityEvent, using EditorGUI.PropertyField can be used for that.
See the line that is commented out in the DrawElement.
EditorGUI.PropertyField(rect, serializedObject.FindProperty("Events").GetArrayElementAtIndex(index));
In this case, You can use the SerializedProperty of your list for the initializing of your list aswell (also the commented out part). It would be hard to access the actual event values from the SerializedProperty (As I have no idea how Unity actually serializes it under the hood). But since you simply want to display the SerializedProperty the way Unity does, there isn't really a need for it :p
Fantastically helpful answer.... Here's my fancy example implementation that works nicely with customPropertyDrawer
and self hides/expands the list elements to a designated line-height https://gist.github.com/JesseHamburger/3d17f3892e671a4ae17873ec4e8a0926
Hi, i don't know what's wrong with my code, but it keep sending me "Object reference not set to an instance of an object" Error. Here's my custom inspector script(i've cut off unnecessary lines) https://gist.github.com/kubawich/1c2b4f1c8e7917c042bdf0589efe7597
If someone need i can send whole script. What is wrong with this code? It says that reorderable list isn't created at 54 line but its actually is.
Answer by RoyalCoder · Feb 25, 2018 at 03:24 PM
Hi guys,
How to make a object references for inspector list, to be used like drag and drop in Unity? For strings, ints floats I managed but for GameObject or Transforms?
Thanks in advance!
Answer by quizcanners · May 03, 2019 at 03:26 PM
I made a series of methods for my custom inspectors since I had to write waaay too many of those in my time. It lets me reorder, copy, select/delete etc.
There is a GIST that shows what needs to be done to make custom Inspector.
And the scripts themselves are here.
In this video, I show off one of the custom inspectors to control some global lighting settings.
Not many people use those scripts as I know, and there is probably still room for improvements, but I think it could save some time overall.
Your answer
Follow this Question
Related Questions
A node in a childnode? 1 Answer
Default values are not working in Editor 1 Answer
Update List<> in editor 0 Answers
How can I display 4 items in random order but never double? 2 Answers
MonoBehaviour execution order from Edit to Play mode and back. 2 Answers