Как в Unity создать поле в инспекторе с пользовательским окном выбора

Я разрабатываю пакет для Unity для адаптирования интерфейсов (в основном) под разные темы и языки. Я уже написал классы для пользовательских UI-элементов, и мне надо сделать чтобы я мог в поле со строкой выбирать значение из путей которые есть в теме/языке как например в полях public Color m_Color;, public GameObject m_GameObject; или даже public AnimationCurve m_AnimationCurve. Вот класс для UI-изображения:

using Adapter.Styles;
using UnityEngine;

namespace Adapter.Elements
{
    [AddComponentMenu("UI/Adaptive/Image", order: 2)]
    public class Image : UnityEngine.UI.Image, IElement
    {
        [SerializeField] protected Setting m_Setting;
        [SerializeField] protected string m_Style;

        public Setting setting { get => m_Setting; set => m_Setting = value; }
        public string style { get => m_Style; set => m_Style = value; }



        public void Modify()
        {
            if (m_Setting != null && m_Setting.currentTheme != null && m_Setting.currentTheme.SearchStyle(m_Style, out ImageStyle imageStyle))
                imageStyle.Apply(this);
        }
        
        private void OnThemeChanged(string themeName, bool nameIsEmpty) => Modify();

        protected override void OnEnable()
        {
            base.OnEnable();

            if (m_Setting != null)
                m_Setting.ThemeChanged += OnThemeChanged;
        }
        protected override void OnDisable()
        {
            base.OnDisable();

            if (m_Setting != null)
                m_Setting.ThemeChanged -= OnThemeChanged;
        }

        protected override void Start()
        {
            base.Start();

            Modify();
        }
    }
}

Вот картинка что я имею ввиду, туда куда указывает красная стрелка надо что-то наподобие куда указывают зеленые:

Пример

Пример того что я хочу добиться:

[SerializedField, AddMiniButton(typeof(SomeCustomWindow))] protected string m_Style;

Желательно чтобы в этом поле можно было писать вручную но можно и как public Color m_Color; а я тогда в окне инспектора все реализую, сейчас я исследую метод EditorGUI.DoObjectField может что-то и найду.

PS: И еще вопрос, если у меня вдруг не получится реализовать такое поле самому по техническим причинам то как мне хотя бы получить иконки как на этих полях: кружочек или например пипетка как на цветовом поле. Я уже очень много спрашивал у ChatGPT но он мне пишет класс с полем с текстурой а не получение этих иконок. Получилось только что-то с иконками ошибки, предупреждения и информации но они для другого нужны были.

PSS: Как можно создать окно только с кнопкой закрыть. Я когда то в Visual Studio создавал проект и там я как-то создавал окно у которого был только крестик на панели сверху. В Unity такие окна есть на как раз этих полях дл выбора объектов или цвета.

Кстати я не против кода на c++ если это единственный вариант.


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

Автор решения: Yaroslav

Если это ключи, коих не сотни, то проще отображать поле в виде выпадающего списка.

public class StyleFieldDrawer : PropertyDrawer
{
    public override VisualElement CreatePropertyGUI (SerializedProperty property)
    {
        var options = GetOptions();
        var field = new PopupField<string>(property.displayName, options, property.stringValue);
        field.BindProperty(property);
        return field;
    }

    // Поскольку все стили это ScriptableObject, 
    // то эти ассеты можно найти и собрать список всех ключий.
    private List<string> GetOptions ()
    {
        List<string> options = new List<string>();
        var guids = AssetDatabase.FindAssets("t:MyStyleObjectType", null);
        foreach (string g in guids)
        {
            string path = AssetDatabase.GUIDToAssetPath(g);
            var style = AssetDatabase.LoadAssetAtPath(path) as MyStyleObjectType;
            if (style != null)
                options.Add(style.Key);
        }
        return options;
    }
}

С тем-же успехом можно отображать поле в виде объекта, хотя хранит оно только string.

public class StyleFieldDrawer : PropertyDrawer
{
    private SerializedProperty _property;

    public override VisualElement CreatePropertyGUI (SerializedProperty property)
    {
        // Не совсем уверен что можно хранить ссылку на сериализуемое свойство.
        _property = property;
        
        var field = new ObjectField(property.displayName);
        field.objectType = typeof(MyStyleObjectType);
        field.value = GetStyle(property.stringValue);
        // В идеале нужно подписываться при AttachToPanelEvent 
        // и отписываться при DetachFromPanelEvent от всех событий.
        field.RegisterValueChangedCallback(OnChange);

        return field;
    }

    private void OnChange (ChangeEvent<Object> e)
    {
        if (e.target == null)
            _property.stringValue = "";
        else
            _property.stringValue = (e.target as MyStyleObjectType).Key;
    }

    private MyStyleObjectType GetStyle (string key)
    {
        var guids = AssetDatabase.FindAssets("t:MyStyleObjectType", null);
        foreach (string g in guids)
        {
            string path = AssetDatabase.GUIDToAssetPath(g);
            var style = AssetDatabase.LoadAssetAtPath(path) as MyStyleObjectType;
            if (style != null && style.Key == key)
                return style;
        }
        return null;
    }
}

п.с. код написа на коленках и не факт, что работает.


Как написать атрибут для поля, показано в PropertyDrawer.

→ Ссылка