В List в инспекторе мой класс отображается как стандартный, хотя у класса есть кастомный инспектор

Мне нужно хранить длинный список классов "DataFrame". В "DataFrame" много полей, поэтому лист сильно растягивается. Я хочу создать пользовательский инспектор для "DataFrame", чтобы все его данные отображались в 3 строках.

Когда я переопределяю "OnInspectorGUI" для класса, он отображается в списке так, как будто "OnInspectorGUI" не был переопределен. В то же время, если я наследую класс от "ScriptableObject" или "MonoBehaviour", он отображается в инспекторе с переопределенным "OnInspectorGUI".

Проблема в отображении только в списке.

Вот как выглядит лист:

ScriptableObject

Вот как выглядит ScriptableObject: ScriptableObject

Классы:

[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;
}




[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);
    }

Ответы (2 шт):

Автор решения: Aiden VMR

Мне порекомендовали использовать PropertyDrawer для этого.

Вот полезные видео которые быстро дадут понимание о PropertyDrawer: https://www.youtube.com/watch?v=ur-qy6SjVQw

https://www.youtube.com/watch?v=ZeTxVarrkXw

→ Ссылка
Автор решения: Yaroslav

Из документации Custom Editors, первое предложение, первого абзаца:

To speed up application development, create custom editors FOR COMPONENTS you commonly use.

На самом деле речь не только о упомянутых Component (MonoBehaviour от него наследуется), а о всех UnityEngine.Object, включая ScriptableObject.


Твой класс DataFrame является не UnityEngine.Object, а базовым классом C#, object. Инспектора он не имеет вовсе!

Для кастумной отрисовки отдельных типов, как например Vector3, используемых в множестве инспекторов, как поле, есть PropertyDrawer. Твой DataFrame кстати не имеет поведения, а лишь модель данных, набор полей объединённая в одну логическую сущность, как упомянутый Vector3, для чего обычно используют struct, а не class.


Писать кастомные редакторы или драверы только для хедеров и пробелов, бесполезная трата времени. Более того, это явно указывает на ошибки перегруженности ответственности и необходимость дробления, например:

[Serializable]
public struct LevelData
{
    public LevelOptions options;
    public LevelEnemies enemy;
    public LevelInteractingEnvironment environment;
}

[Serializable]
public struct LevelOptions
{
    public float shopFactor;
    public float eventFactor;
    public float abibityFactor;
    public float coinsFactor;
}

[Serializable]
public struct LevelEnemies
{
    public int count;
    public float countFactor;
    public float healthFactor;
}

[Serializable]
public struct LevelInteractingEnvironment
{
    public int trapCount;
    public int coinCount;
}

И контекст типа level.enemy.count не требует никаких комментариев и уж тем более хедеров, которые где-то там и выглядит в инспекторе хорошо, без написания дополнительного и бестолкового кода. Даже разбивать на отдельные файлы не стоит, можно всё в каком-нибудь LevelTypes.cs или тот-же LevelData.cs оставить.

И нужно следить за неймингом, что бы и смысл был и однозначность, а не как у тебя unit/enemy/monster/zombie и всё обозначает одно и то-же.


В struct нельзя указывать дефолтные значения, но можно поступать так-же как Vector3.right, через static:

public struct LevelEnemies
{
    ...

    public static LevelEnemies defaultValues 
        => new LevelEnemies() { count = 10, countFactor = 1f, healthFactor = 1f };
}
→ Ссылка