代码之家  ›  专栏  ›  技术社区  ›  Max

Unity3D编辑器:渲染侧边栏中的嵌套元素

  •  0
  • Max  · 技术社区  · 4 年前

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

    图片形式

    我所拥有的

    enter image description here

    (双击元素)

    enter image description here

    我想要什么

    enter image description here

    我有自己的 [CustomEditor] 组件,但我无法使其正常工作;像这样的东西:

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

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

    0 回复  |  直到 4 年前
        1
  •  2
  •   derHugo    4 年前

    因为默认 PropertyField 对于 ScriptableObject 就是你得到的那个:A UnityEngine.Object 参考字段,如GameObject和Components以及其他资产;)


    当然你 可以 实现你想要实现的目标,但这有点复杂 保养不是很好,我不推荐 .


    我不知道你 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();
        }
    }
    

    这就产生了以下检查员。正如你所看到的,我打开了 MonoBehavior 和的两个实例 ExampleSO 显示如何将值转换为实际实例

    enter image description here