You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

通过其他类显示时,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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 09:15:58