I have a custom editor for a MonoBehaviour
to display a reorderable list of items.
public class MyComponent : MonoBehaviour
{
public MyArrayElement[] myList;
}
public struct MyArrayElement
{
public string firstField;
public string secondField;
}
[CustomEditor(typeof(MyComponent))]
public class MyComponentEditor : Editor
{
private ReorderableList list;
private void OnEnable()
{
SerializedProperty property = this.serializedObject.FindProperty("myList");
this.list = new ReorderableList(this.serializedObject, property, true, true, true, true);
list.drawElementCallback = DrawListItems;
}
public override void OnInspectorGUI()
{
serializedObject.Update();
list.DoLayoutList();
serializedObject.ApplyModifiedProperties();
}
void DrawListItems(Rect rect, int index, bool isActive, bool isFocused)
{
EditorGUI.PropertyField(
new Rect(rect.x, rect.y, 100, EditorGUIUtility.singleLineHeight),
element.FindPropertyRelative("firstField"),
GUIContent.none);
EditorGUI.PropertyField(
new Rect(rect.x 150, rect.y, 100, EditorGUIUtility.singleLineHeight),
element.FindPropertyRelative("secondField"),
GUIContent.none);
}
}
This works correctly. However, I would like to have the Inspector for every instance of this MonoBehaviour
edit the same set of elements, so I created a ScriptableObject
public MyScriptableObject : ScriptableObject
{
public MyArrayElement[] myList;
}
And then replace MyComponent.myList
with an instance of MyScriptableObject
public class MyComponent : MonoBehaviour
{
// Remove this
// public MyArrayElement[] myList;
// Add this
public MyScriptableObject myScriptableObject;
}
Then I want to update my custom editor for the MonoBehaviour
to show myScriptableObject.myList
I tried this, but the list is empty in the Inspector, even if the ScriptableObject
's list is not empty
SerializedProperty property = this.serializedObject.FindProperty("myScriptableObject").FindPropertyRelative("myList");
this.list = new ReorderableList(this.serializedObject, property, true, true, true, true);
Is there a way to get my MonoBehaviour
s editor to let me edit the ScriptableObject
s array?
CodePudding user response:
Yes of course there is! ;)
But first of all: Starting with Unity 2020 there is no need to use ReorderableList
anymore except you want some very special behavior simce for List<T>
and T[]
this is now the default drawer anyway!
You might want to consider to rather implement a special CustomPropertyDrawer
for your list elements (MyArrayElement
) in general so it is actually displayed this way everywhere you expose it in the Inspector.
Such as e.g.
[CustomPropertyDrawer(typeof(MyArrayElement))]
public class MyArrayElementDrawer : PropertyDrawer
{
public override int GetPropertyHeight ()
{
return EditorGUIUtility.singleLineHeight;
}
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginProperty(position, label, property);
// Draw label
position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
// Don't make child fields be indented
var indent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
// Calculate rects
var Rect1 = new Rect(position.x, position.y, 100, EditorGUIUtility.singleLineHeight);
var Rect2 = new Rect(position.x 150, position.y, 100, EditorGUIUtility.singleLineHeight);
EditorGUI.PropertyField(Rect1, property.FindPropertyRelative(nameof (MyArrayElement.firstField)), GUIContent.none);
EditorGUI.PropertyField(Rect2, property.FindPropertyRelative(nameof (MyArrayElement.secondField)), GUIContent.none);
EditorGUI.indentLevel = indent;
EditorGUI.EndProperty();
}
}
And then using FindPropertyRelative
only works for [Serializable]
classes/structs and their sub-fields.
Whenever you deal with a separate UnityEngine.Object
reference (another MonoBehaviour
, ScriptableObject
, etc) what you need to do is go through the SerializedObject
of the instance of that reference.
Like e.g.
var property = serializedObject.FindProperty(nameof(MyComponent.myScriptableObject));
EditorGUILayout.PropertyField(property, true);
if(property.objectReferenceValue)
{
var so = new SerializedObject(property.objectReferenceValue);
so.Update();
var listProperty = so.FindProperty(nameof(MyScriptableObjevt.myList));
EditorGUILayout.PropertyField(listProperty, true);
so.ApplyModifiedProperties();
}
Note: Typing on smartphone but I hope the idea gets clear