- Home /
EditorGUILayout List field
For an asset management tool I need to expose a given model to be able to create it:
[System.Serializable]
public class Foo{
public string name;
[SerializeField]
List<Bar> bars;
}
public class Bar: MonoBehaviour{
... Behaviour stuff
}
So I do it by exposing name
using an EditorGUILayout.TextField
, but exposing the List<Bar>
is a problem since EditorGUILayout
doesn't have an element for that.
I tried using an EditorGUILayout.PropertyField
but it seems that I can't convert Foo
to Object
(it seems impossible, but it's the error message I get from Unity. And when I make Foo
extend Object
, Unity can't create the asset (the error is weird: NullReferenceException(null)
and the editor crashes).
It seems that EditorGUILayout.PropertyField
works only for custom context menus, but in my example I'm in an EditorWindow
.
How can I do this like the default inspector does? Should I make the List<Bar>
display by myself (using a foreach loop etc)?
Answer by Bunny83 · Jul 12, 2016 at 01:29 PM
You basically have two options:
Use SerializedObject / SerializedProperty in combination with
EditorGUILayout.PropertyField
in order to draw the usual GUI for the array / list.iterate through the list manually and show whatever you want for each list element. Of course you have to handle the size change yourself which includes the creation of new elements.
For the first option you have to create a SerializedObject for your initial object, then use FindProperty to get that specific property and finally use the PropertyField method with includeChildren set to true. Note that Unity can't serialize your "Foo" on it's own. It's only serializable if it's used inside a MonoBehaviour or ScriptableObject. You have to create the SerializedObject for the MonoBehaviour / ScriptableObject and then use FindProperty with the right property path.
If you go the manual route the object doesn't need to be serializable at all. Of course the data wouldn't be saved in that case. By default a List of a MonoBehaviour type only shows an ObjectField for each element. If you also want to have a "size" field to change the number of elements you have to implement that yourself. Since you don't use Unitys serialization system you would have to make your "bars" field public, otherwise you can't access it from your editorwindow. Something like that:
Foo foo;
// [ ... ]
var list = foo.bars;
int newCount = Mathf.Max(0, EditorGUILayout.IntField("size", list.Count));
while (newCount < list.Count)
list.RemoveAt( list.Count - 1 );
while (newCount > list.Count)
list.Add(null);
for(int i = 0; i < list.Count; i++)
{
list[i] = (Bar)EditorGUILayout.ObjectField(list[i], typeof(Bar));
}
For anyone stumbling upon this solution, you might consider using a DelayedIntField
ins$$anonymous$$d of an IntField
. The IntField will instantly remove/add items to the list. While that sounds nice, it may be troublesome if your list items are complex and you accidently try to remove one by editing the number.
eg:
You want to remove one from 11 items.
You click into the number and remove a 1 to replace it with a 0.
You now have 1 item left, the other 10 are gone.
You enter a zero to have 10 items again.
All these items do now have the default values, the previous values are lost.
This will not happen when using the DelayedIntField as the number only applies as soon as you leave the input or press enter.
Of course you're absolutely right. The "Delayed" fields are relatively new but i think they where already available when i wrote the answer. In the past there always has been a DelayedTextField but it was private / internal so not available to us.
$$anonymous$$eep in $$anonymous$$d that Unity's default inspector behaviour for lists and arrays is a lot more "advanced" than this hacked together solution. Unity allows to duplicate elements in between and remove elements in between.
It can be only unity object? not c# system.object like int
I'm not sure what you're asking here but Unity's serializer can only serialize objects that are derived from UnityEngine.Object as top-level object. The SerializedObject wrapper can only wrap around UnityEngine.Object. So it has to be either a built-in Unity type or a $$anonymous$$onoBehaviour or ScriptableObject derived type.
You may want to have a look at the script serialization page. Your question doesn't seem to be related to writing editor code for lists (as this is what this question was about). If you have a different question, please ask a seperate question and be more specific about what you want to know or what your problem is.
Answer by Lyrcaxis · Jun 18, 2019 at 09:13 AM
A working example can be seen here:
namespace TESTT {
using UnityEditor;
using UnityEngine;
public class ListTestEditor : EditorWindow {
[MenuItem(itemName: "TestEditorList", menuItem = "Window/TestList")]
public static void Init() { GetWindow<ListTestEditor>("Haha", true); }
Editor editor;
[SerializeField] List<MyClass> ListTest = new List<MyClass>();
void OnGUI() {
if (!editor) { editor = Editor.CreateEditor(this); }
if (editor) { editor.OnInspectorGUI(); }
}
void OnInspectorUpdate() { Repaint(); }
}
[System.Serializable]
public class MyClass {
public List<int> myList;
public string myString;
public int myInt;
}
[CustomEditor(typeof(ListTestEditor), true)]
public class ListTestEditorDrawer : Editor {
public override void OnInspectorGUI() {
var list = serializedObject.FindProperty("ListTest");
EditorGUILayout.PropertyField(list, new GUIContent("My List Test"), true);
}
}
}
Note: to apply any property transformations you may need to call serializedObject.ApplyModifiedProperties();
Your answer
Follow this Question
Related Questions
Window Editor - Particle Effect Window Layout 0 Answers
Keep equal width for panels in EditorGUILayout.HorizontalScope 0 Answers
How do I iterate through a List and add to my EditorWindow? 1 Answer
How do I add a scrolling input text box in an EditorWindow? (more info added) 1 Answer
Small GUILayout button 1 Answer