【问题标题】:Unity3D editor: Rendering nested elements in sidebarUnity3D 编辑器:在侧边栏中渲染嵌套元素
【发布时间】:2021-06-07 04:09:18
【问题描述】:

在 Unity 中,我的一个 MonoBehaviours 有一个指向另一个对象(ScriptableObject)的字段。如果我双击该字段,我可以看到该对象的字段。如何将这些字段渲染到顶级 MonoBehaviour 的属性抽屉中?

图片形式

我有什么

(双击元素)

我想要什么

我有自己的[CustomEditor] 组件,但我不能让它正常工作;像这样的东西:

SerializedProperty activityStack = serializedObject.FindProperty("activityStack");
EditorGUILayout.PropertyField(activityStack.GetArrayElementAtIndex(0));

只呈现“元素 0(空闲活动)”位,而不是引用的实际内容。

【问题讨论】:

    标签: unity3d unity-editor unity3d-editor


    【解决方案1】:

    因为ScriptableObject 的默认PropertyField 只是您得到的:UnityEngine.Object 引用字段,例如用于游戏对象和组件以及其他资产;)


    当然,您可以实现您想要实现的目标,但这有点复杂,不太适合维护,我不建议这样做


    我不知道你的ScriptableObject 所以这里举个例子

    public class ExampleSO : ScriptableObject
    {
        public int SomeInt;
        [SerializeField] private string _someString;
    }
    

    还有你的MonoBehaviour,例如

    public class Example : MonoBehaviour
    {
        public List<ExampleSO> _SOList;
    }
    

    那么编辑器可能看起来像例如

    using UnityEditor;
    using UnityEngine;
    
    // This is the namespace for the ReorderableList
    using UnityEditorInternal;
    
    [CustomEditor(typeof(Example))]
    public class ExampleEditor : Editor
    {
        SerializedProperty _SOList;
    
        Example _example;
        MonoScript _script;
    
        ReorderableList _list;
    
        private void OnEnable()
        {
            // Link up the serializedProperty
            _SOList = serializedObject.FindProperty("_SOList");
    
            // get the casted target instance (only needed for drawing the script field)
            _example = (Example) target;
    
            // get the according script instance (only needed for drawing the script field)
            _script = MonoScript.FromMonoBehaviour(_example);
    
            // Set up the ReorderableList
            _list = new ReorderableList(serializedObject, _SOList, true, true, true, true)
            {
                // What shall be displayed as header for the list?
                drawHeaderCallback = (Rect rect) => EditorGUI.LabelField(rect, _SOList.displayName),
    
                // How is each element displayed?
                drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
                {
                    // Get the element in the list (SerializedProperty)
                    var element = _SOList.GetArrayElementAtIndex(index);
    
                    // and draw the default object reference field
                    EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), element, new GUIContent("Reference"));
    
                    // Check if an asset is referenced - if not we are done here
                    if (!element.objectReferenceValue) return;
    
                    // Otherwise get the SerializedObject for this asset
                    var elementSerializedObject = new SerializedObject(element.objectReferenceValue);
    
                    // and all the properties (SerializedProperty) of it you want to display
                    var someInt = elementSerializedObject.FindProperty("SomeInt");
                    var someString = elementSerializedObject.FindProperty("_someString");
    
                    // Similar to the OnInspectorGUI first load the current values into this serializedobject
                    elementSerializedObject.Update();
                    {
                        // Adding some indentation just to show that the following fields are actually belonging to the referenced asset
                        EditorGUI.indentLevel++;
                        {
                            rect = EditorGUI.IndentedRect(rect);
    
                            // shift down the rect by one line
                            rect.y += EditorGUIUtility.singleLineHeight;
    
                            // Draw the field for the Int 
                            EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), someInt);
    
                            // Shift down the rect another line
                            rect.y += EditorGUIUtility.singleLineHeight;
      
                            // Draw the string field
                            EditorGUI.PropertyField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight), someString);
                        }
                        EditorGUI.indentLevel--;
                    }
                    // Write back the changed values and trigger the checks for logging dirty states and Undo/Redo
                    elementSerializedObject.ApplyModifiedProperties();
                },
    
                // How much vertical space should be reserved for each element?
                elementHeightCallback = (int index) =>
                {
                    // Get the elements serialized property
                    var element = _SOList.GetArrayElementAtIndex(index);
    
                    // by default we have only the asset reference -> single line
                    var lines = 1;
    
                    // if the asset is referenced adds space for the additional fields
                    if (element.objectReferenceValue) lines += 2; // or how many lines you'll need
    
                    return lines * EditorGUIUtility.singleLineHeight;
                }
            };
        }
    
        public override void OnInspectorGUI()
        {
            // draw th script field
            DrawScriptField();
    
            // Load the current values into the serializedObject
            serializedObject.Update();
            {
                // let the ReorderableList do its magic
                _list.DoLayoutList();
            }
            // Write back the changed values into the actual instance
            serializedObject.ApplyModifiedProperties();
        }
    
        // Just draws the usual script field at the top of the Inspector
        private void DrawScriptField()
        {
            EditorGUI.BeginDisabledGroup(true);
            {
                EditorGUILayout.ObjectField("Script", _script, typeof(Example), false);
            }
            EditorGUI.EndDisabledGroup();
    
            EditorGUILayout.Space();
        }
    }
    

    这会产生以下 Inspector。如您所见,我打开了MonoBehaviour 的Ispectors 和ExampleSO 的两个实例,以显示如何将这些值转移到实际实例中

    【讨论】:

    • 啊,这个答案非常有帮助,谢谢!你比严格必要的更深入,但你的解决方案比我想的要好。我最大的遗憾是不知道new SerializedObject(element.objectReferenceValue);
    猜你喜欢
    • 1970-01-01
    • 2017-07-02
    • 1970-01-01
    • 2019-05-12
    • 1970-01-01
    • 1970-01-01
    • 2020-10-06
    • 2019-06-16
    • 1970-01-01
    相关资源
    最近更新 更多