通过其他类显示时,UIElements自定义Inspector不显示PropertyField
我有一个MonoBehaviour类,其中包含ScriptableObject类型的属性planeSettings。我为该ScriptableObject和MonoBehaviour类创建了基于UIElements的自定义Inspector。
直接查看ScriptableObject时,Inspector渲染正常,但通过MonoBehaviour类查看时缺失两个字段(截图黄线标注处为预期显示位置,空白仅因包裹VisualElement设置了minHeight)。
查看UIToolkitDebugger时,对应行的VisualElement中存在两个PropertyField,但无法展开查看详情。
请问如何进一步调试?是否有可行的workaround?我希望通过MonoBehaviour类整合多个不同ScriptableObject的自定义Inspector。
相关代码
NoiseLayerPointsConfig.cs(MonoBehaviour类)
using System.Collections; using System.Collections.Generic; using UnityEngine; public class NoiseLayerPointsConfig : MonoBehaviour { public PlaneSettings planeSettings; void OnEnable() { planeSettings = Resources.Load<PlaneSettings>("Set1"); } void Start() { } void Update() { } }
CustomEditor_NoiseLayerPoints.cs(自定义编辑器)
using UnityEditor; using UnityEditor.UIElements; using UnityEngine; using UnityEngine.UIElements; [CustomEditor(typeof(NoiseLayerPointsConfig))] public class CustomEditor_NoiseLayerPointsInspector : Editor { private NoiseLayerPointsConfig comp; public override VisualElement CreateInspectorGUI() { comp = (NoiseLayerPointsConfig)target; var root = new VisualElement(); root.style.backgroundColor = new Color(0.0f, 0.0f, 0.0f); Editor planesettingsEditor = Editor.CreateEditor(comp.planeSettings); return new InspectorElement(planesettingsEditor); } }
PlaneSettings.cs(ScriptableObject)
using System.Collections; using System.Collections.Generic; using UnityEngine; [CreateAssetMenu(fileName = "PlaneSettings", menuName = "ScriptableObjects/PlaneSettings", order = 1)] public class PlaneSettings : ScriptableObject { public float mapWidth = 10f; public float mapHeight = 10f; public enum CoordinateMode { Center, LowerLeftCorner } public CoordinateMode coordinateMode = CoordinateMode.Center; }
CustomEditor_PlaneSettings.cs(ScriptableObject自定义编辑器)
using UnityEditor; using UnityEditor.UIElements; using UnityEngine; using UnityEngine.UIElements; [CustomEditor(typeof(PlaneSettings))] public class CustomEditor_PlaneSettingsInspector : Editor { private PlaneSettings comp; public override VisualElement CreateInspectorGUI() { comp = (PlaneSettings)target; var root = new VisualElement(); root.style.backgroundColor = new Color(0.0f, 0.0f, 0.0f); root.style.paddingTop = 3f; root.style.paddingBottom = 3f; root.style.paddingLeft = 3f; root.style.paddingRight = 3f; var wrapperContainer = new VisualElement(); wrapperContainer.style.borderTopWidth = wrapperContainer.style.borderRightWidth = 1; wrapperContainer.style.borderBottomWidth = wrapperContainer.style.borderLeftWidth = 1; wrapperContainer.style.backgroundColor = new Color(101f/255f, 67f/255f, 33f/255f); var label = new Label("Plane Settings"); label.style.unityTextAlign = TextAnchor.MiddleLeft; label.style.unityFontStyleAndWeight = FontStyle.Bold; wrapperContainer.Add(label); var flexContainerTileDimensions = new VisualElement(); flexContainerTileDimensions.style.flexDirection = FlexDirection.Row; flexContainerTileDimensions.style.display = DisplayStyle.Flex; flexContainerTileDimensions.style.flexWrap = Wrap.Wrap; var mapWidthField = new PropertyField(serializedComp.FindProperty("mapWidth")); mapWidthField.style.flexBasis = 1f; mapWidthField.style.flexGrow = 1f; mapWidthField.style.flexShrink = 0f; mapWidthField.style.minHeight = new StyleLength(17); var mapHeightField = new PropertyField(serializedComp.FindProperty("mapHeight")); mapHeightField.style.flexBasis = 1f; mapHeightField.style.flexGrow = 1f; mapHeightField.style.flexShrink = 0f; mapHeightField.style.minHeight = new StyleLength(17); flexContainerTileDimensions.Add(mapWidthField); flexContainerTileDimensions.Add(mapHeightField); wrapperContainer.Add(flexContainerTileDimensions); var coordinateModeField = new EnumField((PlaneSettings.CoordinateMode)serializedComp.FindProperty("coordinateMode").enumValueIndex); coordinateModeField.label = "Coordinate Mode"; coordinateModeField.labelElement.style.unityTextAlign = TextAnchor.MiddleLeft; coordinateModeField.RegisterValueChangedCallback(evt => { serializedComp.FindProperty("coordinateMode").enumValueIndex = (int)(PlaneSettings.CoordinateMode)evt.newValue; serializedComp.ApplyModifiedProperties(); }); wrapperContainer.Add(coordinateModeField); root.Add(wrapperContainer); return root; } }
核心问题
你的CustomEditor_PlaneSettingsInspector中错误地使用了未定义的serializedComp变量,正确的序列化对象引用应为Editor基类的serializedObject。这导致FindProperty返回null,PropertyField因未绑定有效SerializedProperty而无法渲染内容。此外,子编辑器的生命周期未正确管理,可能引发UI更新异常。
调试步骤
- 验证SerializedProperty有效性:创建PropertyField前,打印
serializedObject.FindProperty("mapWidth"),确认属性路径正确且返回非null值。 - UIToolkitDebugger检查绑定:选中空白PropertyField,在Debugger的"Bindings"面板查看是否有绑定的SerializedProperty,若为null则说明属性查找失败。
- 添加日志输出:在自定义编辑器的关键步骤(如创建字段、绑定属性)添加
Debug.Log,确认逻辑执行流程。
可行修复方案
方案1:修复ScriptableObject编辑器的序列化问题
将CustomEditor_PlaneSettingsInspector中所有serializedComp替换为serializedObject,确保属性绑定正确:
using UnityEditor; using UnityEditor.UIElements; using UnityEngine; using UnityEngine.UIElements; [CustomEditor(typeof(PlaneSettings))] public class CustomEditor_PlaneSettingsInspector : Editor { public override VisualElement CreateInspectorGUI() { var root = new VisualElement(); root.style.backgroundColor = new Color(0.0f, 0.0f, 0.0f); root.style.paddingTop = 3f; root.style.paddingBottom = 3f; root.style.paddingLeft = 3f; root.style.paddingRight = 3f; var wrapperContainer = new VisualElement(); wrapperContainer.style.borderTopWidth = wrapperContainer.style.borderRightWidth = 1; wrapperContainer.style.borderBottomWidth = wrapperContainer.style.borderLeftWidth = 1; wrapperContainer.style.backgroundColor = new Color(101f/255f, 67f/255f, 33f/255f); var label = new Label("Plane Settings"); label.style.unityTextAlign = TextAnchor.MiddleLeft; label.style.unityFontStyleAndWeight = FontStyle.Bold; wrapperContainer.Add(label); var flexContainerTileDimensions = new VisualElement(); flexContainerTileDimensions.style.flexDirection = FlexDirection.Row; flexContainerTileDimensions.style.display = DisplayStyle.Flex; flexContainerTileDimensions.style.flexWrap = Wrap.Wrap; // 使用正确的serializedObject获取属性 var mapWidthProp = serializedObject.FindProperty("mapWidth"); var mapWidthField = new PropertyField(mapWidthProp); mapWidthField.style.flexBasis = 1f; mapWidthField.style.flexGrow = 1f; mapWidthField.style.flexShrink = 0f; mapWidthField.style.minHeight = new StyleLength(17); var mapHeightProp = serializedObject.FindProperty("mapHeight"); var mapHeightField = new PropertyField(mapHeightProp); mapHeightField.style.flexBasis = 1f; mapHeightField.style.flexGrow = 1f; mapHeightField.style.flexShrink = 0f; mapHeightField.style.minHeight = new StyleLength(17); flexContainerTileDimensions.Add(mapWidthField); flexContainerTileDimensions.Add(mapHeightField); wrapperContainer.Add(flexContainerTileDimensions); var coordinateModeProp = serializedObject.FindProperty("coordinateMode"); var coordinateModeField = new EnumField((PlaneSettings.CoordinateMode)coordinateModeProp.enumValueIndex); coordinateModeField.label = "Coordinate Mode"; coordinateModeField.labelElement.style.unityTextAlign = TextAnchor.MiddleLeft; coordinateModeField.RegisterValueChangedCallback(evt => { coordinateModeProp.enumValueIndex = (int)(PlaneSettings.CoordinateMode)evt.newValue; serializedObject.ApplyModifiedProperties(); serializedObject.Update(); // 更新序列化对象,确保UI同步 }); wrapperContainer.Add(coordinateModeField); root.Add(wrapperContainer); return root; } }
方案2:正确管理子编辑器生命周期
在MonoBehaviour的自定义编辑器中缓存子编辑器,并在OnDisable时销毁,避免内存泄漏和UI异常:
using UnityEditor; using UnityEditor.UIElements; using UnityEngine; using UnityEngine.UIElements; [CustomEditor(typeof(NoiseLayerPointsConfig))] public class CustomEditor_NoiseLayerPointsInspector : Editor { private NoiseLayerPointsConfig comp; private Editor planeSettingsEditor; // 缓存子编辑器 public override VisualElement CreateInspectorGUI() { comp = (NoiseLayerPointsConfig)target; var root = new VisualElement(); root.style.backgroundColor = new Color(0.0f, 0.0f, 0.0f); // 销毁旧的子编辑器,避免重复创建 if(planeSettingsEditor != null) { DestroyImmediate(planeSettingsEditor); } if(comp.planeSettings != null) { planeSettingsEditor = Editor.CreateEditor(comp.planeSettings); root.Add(new InspectorElement(planeSettingsEditor)); } return root; } private void OnDisable() { // 清理子编辑器 if(planeSettingsEditor != null) { DestroyImmediate(planeSettingsEditor); planeSettingsEditor = null; } } }
方案3:直接复用ScriptableObject的UI构建逻辑
若需整合多个ScriptableObject的Inspector,可将每个ScriptableObject的UI构建逻辑提取为静态方法,直接在MonoBehaviour编辑器中调用,避免创建子编辑器:
// 在CustomEditor_PlaneSettingsInspector中添加静态方法 public static VisualElement CreatePlaneSettingsUI(SerializedObject serializedObj) { var root = new VisualElement(); root.style.backgroundColor = new Color(0.0f, 0.0f, 0.0f); root.style.paddingTop = 3f; root.style.paddingBottom = 3f; root.style.paddingLeft = 3f; root.style.paddingRight = 3f; var wrapperContainer = new VisualElement(); wrapperContainer.style.borderTopWidth = wrapperContainer.style.borderRightWidth = 1; wrapperContainer.style.borderBottomWidth = wrapperContainer.style.borderLeftWidth = 1; wrapperContainer.style.backgroundColor = new Color(101f/255f, 67f/255f, 33f/255f); var label = new Label("Plane Settings"); label.style.unityTextAlign = TextAnchor.MiddleLeft; label.style.unityFontStyleAndWeight = FontStyle.Bold; wrapperContainer.Add(label); var flexContainerTileDimensions = new VisualElement(); flexContainerTileDimensions.style.flexDirection = FlexDirection.Row; flexContainerTileDimensions.style.display = DisplayStyle.Flex; flexContainerTileDimensions.style.flexWrap = Wrap.Wrap; var mapWidthProp = serializedObj.FindProperty("mapWidth"); var mapWidthField = new PropertyField(mapWidthProp); mapWidthField.style.flexBasis = 1f; mapWidthField.style.flexGrow = 1f; mapWidthField.style.flexShrink = 0f; mapWidthField.style.minHeight = new StyleLength(17); var mapHeightProp = serializedObj.FindProperty("mapHeight"); var mapHeightField = new PropertyField(mapHeightProp); mapHeightField.style.flexBasis = 1f; mapHeightField.style.flexGrow = 1f; mapHeightField.style.flexShrink = 0f; mapHeightField.style.minHeight = new StyleLength(17); flexContainerTileDimensions.Add(mapWidthField); flexContainerTileDimensions.Add(mapHeightField); wrapperContainer.Add(flexContainerTileDimensions); var coordinateModeProp = serializedObj.FindProperty("coordinateMode"); var coordinateModeField = new EnumField((PlaneSettings.CoordinateMode)coordinateModeProp.enumValueIndex); coordinateModeField.label = "Coordinate Mode"; coordinateModeField.labelElement.style.unityTextAlign = TextAnchor.MiddleLeft; coordinateModeField.RegisterValueChangedCallback(evt => { coordinateModeProp.enumValueIndex = (int)(PlaneSettings.CoordinateMode)evt.newValue; serializedObj.ApplyModifiedProperties(); serializedObj.Update(); }); wrapperContainer.Add(coordinateModeField); root.Add(wrapperContainer); return root; } // 在MonoBehaviour编辑器中调用 [CustomEditor(typeof(NoiseLayerPointsConfig))] public class CustomEditor_NoiseLayerPointsInspector : Editor { private NoiseLayerPointsConfig comp; public override VisualElement CreateInspectorGUI() { comp = (NoiseLayerPointsConfig)target; var root = new VisualElement(); root.style.backgroundColor = new Color(0.0f, 0.0f, 0.0f); if(comp.planeSettings != null) { var serializedPlaneSettings = new SerializedObject(comp.planeSettings); var planeUI = CustomEditor_PlaneSettingsInspector.CreatePlaneSettingsUI(serializedPlaneSettings); root.Add(planeUI); root.Bind(serializedPlaneSettings); // 绑定序列化对象,确保属性修改同步 } return root; } }
内容的提问来源于stack exchange,提问作者C4pt4inC4nn4bis

