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

Unity中自定义Editor对普通Serializable类列表不生效问题求助

问题解决:Serializable类列表中自定义Inspector不生效的问题

我需要存储大量DataFrame类实例,这个类包含众多字段,导致列表显示过长。于是为DataFrame制作了自定义Inspector,希望将其数据压缩为3行显示,但重写OnInspectorGUI后,该类在列表中仍显示默认样式;而将其改为ScriptableObject或MonoBehaviour时,自定义Inspector可正常生效,问题仅出现在列表显示场景中。无[System.Serializable]标签时列表无法显示,相关代码如下:

DataFrame类代码

[System.Serializable]
public class DataFrame 
{
    public const int LINES_NUMBER = 10;

    [Header("Units")]
    public float UnitZombieHealthCoefficient;
    public int UnitMonsterNumber;
    [Space(5)]
    public float UnitEnemyCountCoefficient;

    [Header("Objects")]
    public int ObjTrapNumber;
    public int ObjCoinNumber;

    [Header("Control point Chance")]
    public float CpShopCoefficient;
    public float CpEventCoefficient;
    public float CpAbibityCoefficient;
    public float CpCoinsCoefficient;
}

原自定义Editor代码

[CustomEditor(typeof(DataFrame))]
public class DataStageEditor : Editor
{
    #region  SerializedProperties
    SerializedObject _serializedObject;

    SerializedProperty _zombieHealthCoefficient;
    SerializedProperty _monsterNumber;
    SerializedProperty _enemyCountCoefficient;

    SerializedProperty _trapNumber;
    SerializedProperty _coinNumber;

    SerializedProperty _shopCoefficient;
    SerializedProperty _eventCoefficient;
    SerializedProperty _abibityCoefficient;
    SerializedProperty _coinsCoefficient;
    #endregion


    private void OnEnable() {
        SetProperties();
    }

    private void SetProperties() {
        _serializedObject = new SerializedObject(target);

        _zombieHealthCoefficient = _serializedObject.FindProperty(nameof(DataFrame.UnitZombieHealthCoefficient));
        _monsterNumber = _serializedObject.FindProperty(nameof(DataFrame.UnitMonsterNumber));
        _enemyCountCoefficient = _serializedObject.FindProperty(nameof(DataFrame.UnitEnemyCountCoefficient));

        _trapNumber = _serializedObject.FindProperty(nameof(DataFrame.ObjTrapNumber));
        _coinNumber = _serializedObject.FindProperty(nameof(DataFrame.ObjCoinNumber));

        _shopCoefficient = _serializedObject.FindProperty(nameof(DataFrame.CpShopCoefficient));
        _eventCoefficient = _serializedObject.FindProperty(nameof(DataFrame.CpEventCoefficient));
        _abibityCoefficient = _serializedObject.FindProperty(nameof(DataFrame.CpAbibityCoefficient));
        _coinsCoefficient = _serializedObject.FindProperty(nameof(DataFrame.CpCoinsCoefficient));
    }

    public override void OnInspectorGUI() {

        EditorGUILayout.LabelField("TEST");
        EditorGUILayout.PropertyField(_zombieHealthCoefficient, true); 
        EditorGUILayout.PropertyField(_monsterNumber, true);
        EditorGUILayout.PropertyField(_enemyCountCoefficient, true);

        EditorGUILayout.Space(50);
        EditorGUILayout.PropertyField(_trapNumber, true);
        EditorGUILayout.PropertyField(_coinNumber, true);

        EditorGUILayout.PropertyField(_shopCoefficient, true);
        EditorGUILayout.PropertyField(_eventCoefficient, true);
        EditorGUILayout.PropertyField(_abibityCoefficient, true);
        EditorGUILayout.PropertyField(_coinsCoefficient, true);
    }
}

解决方案

问题核心:普通[System.Serializable]标记的类在列表中显示时,Unity不会调用你编写的CustomEditor,CustomEditor仅对继承自UnityEngine.Object的类型(如MonoBehaviour、ScriptableObject)生效。要实现列表中的自定义显示,需要使用PropertyDrawer替代CustomEditor。

实现自定义PropertyDrawer

using UnityEditor;
using UnityEngine;

[CustomPropertyDrawer(typeof(DataFrame))]
public class DataFrameDrawer : PropertyDrawer
{
    // 定义该属性在Inspector中占用的高度(3行+间距)
    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        return EditorGUIUtility.singleLineHeight * 3 + EditorGUIUtility.standardVerticalSpacing * 2;
    }

    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        EditorGUI.BeginProperty(position, label, property);

        // 绘制属性前缀标签(保留Unity默认的折叠交互)
        position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
        
        // 分割出3行的显示区域
        var line1Rect = new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight);
        var line2Rect = new Rect(position.x, position.y + EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing, position.width, EditorGUIUtility.singleLineHeight);
        var line3Rect = new Rect(position.x, position.y + (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing)*2, position.width, EditorGUIUtility.singleLineHeight);

        // 第一行:Units相关字段
        var zombieHealthProp = property.FindPropertyRelative(nameof(DataFrame.UnitZombieHealthCoefficient));
        var monsterNumProp = property.FindPropertyRelative(nameof(DataFrame.UnitMonsterNumber));
        var enemyCountProp = property.FindPropertyRelative(nameof(DataFrame.UnitEnemyCountCoefficient));
        
        EditorGUI.PropertyField(new Rect(line1Rect.x, line1Rect.y, line1Rect.width/3, line1Rect.height), zombieHealthProp, new GUIContent("Zombie HP"));
        EditorGUI.PropertyField(new Rect(line1Rect.x + line1Rect.width/3, line1Rect.y, line1Rect.width/3, line1Rect.height), monsterNumProp, new GUIContent("Monster Num"));
        EditorGUI.PropertyField(new Rect(line1Rect.x + line1Rect.width*2/3, line1Rect.y, line1Rect.width/3, line1Rect.height), enemyCountProp, new GUIContent("Enemy Coeff"));

        // 第二行:Objects相关字段
        var trapNumProp = property.FindPropertyRelative(nameof(DataFrame.ObjTrapNumber));
        var coinNumProp = property.FindPropertyRelative(nameof(DataFrame.ObjCoinNumber));
        
        EditorGUI.PropertyField(new Rect(line2Rect.x, line2Rect.y, line2Rect.width/2, line2Rect.height), trapNumProp, new GUIContent("Trap Num"));
        EditorGUI.PropertyField(new Rect(line2Rect.x + line2Rect.width/2, line2Rect.y, line2Rect.width/2, line2Rect.height), coinNumProp, new GUIContent("Coin Num"));

        // 第三行:Control Point相关字段
        var shopProp = property.FindPropertyRelative(nameof(DataFrame.CpShopCoefficient));
        var eventProp = property.FindPropertyRelative(nameof(DataFrame.CpEventCoefficient));
        var abilityProp = property.FindPropertyRelative(nameof(DataFrame.CpAbibityCoefficient));
        var coinsCpProp = property.FindPropertyRelative(nameof(DataFrame.CpCoinsCoefficient));
        
        EditorGUI.PropertyField(new Rect(line3Rect.x, line3Rect.y, line3Rect.width/4, line3Rect.height), shopProp, new GUIContent("Shop"));
        EditorGUI.PropertyField(new Rect(line3Rect.x + line3Rect.width/4, line3Rect.y, line3Rect.width/4, line3Rect.height), eventProp, new GUIContent("Event"));
        EditorGUI.PropertyField(new Rect(line3Rect.x + line3Rect.width*2/4, line3Rect.y, line3Rect.width/4, line3Rect.height), abilityProp, new GUIContent("Ability"));
        EditorGUI.PropertyField(new Rect(line3Rect.x + line3Rect.width*3/4, line3Rect.y, line3Rect.width/4, line3Rect.height), coinsCpProp, new GUIContent("Coins"));

        EditorGUI.EndProperty();
    }
}

注意事项

  1. 原有的DataStageEditor可以保留,当DataFrame作为独立的ScriptableObject实例被选中时,依然可以用它显示详细信息
  2. PropertyDrawer专门负责处理Serializable类在Inspector(包括列表)中的渲染逻辑,完全适配你的压缩显示需求
  3. 可根据实际需求调整每行的字段布局、标签文字和高度

内容的提问来源于stack exchange,提问作者Aiden VMR

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 20:25:54