【问题标题】:Can I create a custom MonoBehaviour editor to edit a list from a ScriptableObject field?我可以创建自定义 MonoBehaviour 编辑器来编辑 ScriptableObject 字段中的列表吗?
【发布时间】:2021-12-27 23:11:31
【问题描述】:

我有一个 MonoBehaviour 的自定义编辑器来显示可重新排序的项目列表。

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

这可以正常工作。但是,我想让这个 MonoBehaviour 的每个实例的 Inspector 编辑相同的元素集,所以我创建了一个 ScriptableObject

public MyScriptableObject : ScriptableObject
{
   public MyArrayElement[] myList;  
}

然后将MyComponent.myList替换为MyScriptableObject的实例

public class MyComponent : MonoBehaviour
{       
   // Remove this
   // public MyArrayElement[] myList;

   // Add this
   public MyScriptableObject myScriptableObject;
}

然后我想更新 MonoBehaviour 的自定义编辑器以显示 myScriptableObject.myList

我试过了,但是 Inspector 中的列表是空的,即使 ScriptableObject 的列表不为空

SerializedProperty property = this.serializedObject.FindProperty("myScriptableObject").FindPropertyRelative("myList");
this.list = new ReorderableList(this.serializedObject, property, true, true, true, true);

有没有办法让我的MonoBehaviours 编辑器让我编辑ScriptableObjects 数组?

【问题讨论】:

    标签: c# unity3d


    【解决方案1】:

    是的,当然有! ;)

    但首先:从 Unity 2020 开始,不再需要使用 ReorderableList,除非您希望 List<T>T[] 有一些非常特殊的行为,现在无论如何这都是默认抽屉!

    您可能需要考虑为您的列表元素 (MyArrayElement) 实现一个特殊的 CustomPropertyDrawer,以便它实际上以这种方式显示在您在检查器中公开的任何地方。

    例如

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

    然后使用 FindPropertyRelative 仅适用于 [Serializable] 类/结构及其子字段。

    每当您处理单独的 UnityEngine.Object 引用(另一个 MonoBehaviourScriptableObject 等)时,您需要做的是遍历该引用实例的 SerializedObject

    例如

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

    注意:在智能手机上打字,但我希望思路清晰

    【讨论】:

    • 当我在“项目”视图中选择了ScriptableObject 时,该列表显示正确,并且可以在检查器中查看/更新它时正常工作。但是,当我在引用了ScriptableObject 列表的场景中选择MonoBehaviour 时,我似乎无法在检查器中编辑我的任何列表元素。每次我更改一个值时,该值都会在我跳出该字段后恢复。我可以添加/删除/重新排序元素,但不能编辑单个元素。你知道发生了什么吗?
    • @BenRubin 你还在使用ReorderableList(在这种情况下它可能与我的this old question 有关)还是提到的属性抽屉? (老实说,在 PC 上编写编辑器脚本已经够难了,但在手机上……^^)
    • 不,我停止使用ReorderableList。我在您的代码中使用PropertyDrawer。当我直接编辑ScriptableObject 时,它工作得很好。但是当我编辑MonoBehaviour 时,就像Unity 无法识别列表的每个元素中的字段何时更新,但它确实识别列表本身何时更新。我添加了一个Console.Log 以查看ApplyModifiedProperties 何时返回true,并且在更新列表元素的字段时它不返回true
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-17
    • 1970-01-01
    • 1970-01-01
    • 2014-03-07
    • 2022-01-23
    相关资源
    最近更新 更多