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 EditorGUILayoutdoesn’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 Fooextend 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)?

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 _= (Bar)EditorGUILayout.ObjectField(list*, typeof(Bar));*_

}

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

[140234-χωρις-τιτλο.png|140234]

It works! Thanks :slight_smile: