Unity UI Toolkit: Быстрый старт

—

от автора

Всем привет!

В игре может быть множество элементов интерфейса, всплывающих окон и т. д., и когда появится необходимость изменить общий стиль, например цвет кнопки или текста, то придется это менять во всех созданных элементах, если используется старая система UI Canvas — uGUI (IMGUI забудем как страшный сон). Не так давно Unity предоставили новую систему UI Toolkit, вдохновленную веб-технологиями (HTML-CSS vs UXML-USS) и позволяющую изменить цвет, шрифт и другие свойства всех элементов в игре одним движением. Преимуществ много, например можно подключить веб-дизайнера, и он тут быстро освоится.

Как и ожидается при появлении новой технологии, внятная документация отсутствует, статей в интернете мало и информации в них кусочками. Официальную документацию стараются делать, но явно не для людей. А так как мы все ценим свое драгоценное время, когда еще и горят дедлайны, мы хотим быстро и без лишних затрат использовать все удобства в разработке. Целью данной статьи как раз и является помощь в быстром освоении нового подхода Unity в создании интерфейсов, чтобы не отвлекаться на рысканье в потоках бессвязной документации и продолжать реализовывать свою фантазию или фантазию заказчика. Давно не доходили руки до написания статьи, но пора бы уже уронить свою каплю в море.

Для демонстрации основных часто используемых функций займемся созданием окошка с вводом текста, выбором из подгружаемого кодом списка и обработкой нажатия кнопки. Пусть это будет меню кафешки, когда нам захотелось перекусить. Это будет для вас отправной точкой для создания более сложных интерфейсов.

Установка UI Toolkit

Меню Window — Package Manager — Кнопка + — Add package from git URL… — com.unity.ui — Add

Изображение

После этого в меню Window появится новый пункт UI Toolkit.

Создание интерфейса

Меню Window — UI Toolkit — UI Builder

Изображение

Кратко пройдемся по редактору:

  • (1) — Viewport — рабочее пространство, видим что создаем

  • (2) — StyleSheets — перечень классов USS

  • (3) — Hierarchy — иерархия всех элементов на странице

  • (4) — Library — набор стандартных элементов управления

  • (5) — Inspector — визуальное средство редактирования свойств

Начнем с корневого элемента, назовем его Panel:

Изображение

Добавим класс, тут же попросит сохранить:

Изображение

На видео получилась такая последовательность сохранений:

  1. Сохраняется файл стилей USS.

  2. Сохраняется страница с разметкой UXML.

  3. Опять потребовало сохранить UXML, с первого раза видимо ему непонятно, не знаю как у вас, но у меня так всегда.

  4. Здесь я нажал Ctrl-S для очередной попытки, т.к. в панели Viewport (1) продолжала висеть звездочка рядом с названием файла — это означает, что сохранение не произошло.

Он так себя ведет первый раз, потом в процессе работы просто нажимайте Ctrl-S, чтобы сохранить весь прогресс.

Теперь обязательно сделайте вот что:

  1. Переходим на сцену.

  2. Создаем пустой объект.

  3. Добавим в него компонент UI Document.

Изображение

  1. В панели Project — Create — UI Toolkit — Panel Settings Asset.

Изображение

  1. Переносим его в свойство Panel Settings компонента UI Document.

  2. Файл cafe.uxml переносим в свойство Source Asset компонента UI Document.

Изображение

  1. Закрываем панель UI Builder.

  2. Открываем панель UI Builder (уже весело).

Для чего вот это вот все? В панели Viewport (1) появился важный пункт Unity Default Runtime Theme. Он позволяет во время редактирования видеть, как будет выглядеть интерфейс в игре. Зачем они сделали Dark и Light Editor Theme, которые выглядят совсем иначе и не связаны с игрой, только им известно.

Изображение

Продолжаем веселье, заново создавая корневой элемент Panel, т.к. он потерялся из-за манипуляций с сохранениями. Не переживайте, потом ничто никуда не потеряется, только первый раз Unity решает покапризничать. Перетаскиваем класс .panel на элемент Panel, после чего окно инспектора должно выглядеть так:

Изображение

Видим, что класс добавлен в элемент.

Теперь щелкаем на название класса в окне StyleSheets (2), чтобы менять свойства. Не перепутайте, щелкая на элемент в иерархии (3), иначе вы будете менять свойства только одного элемента, а нам нужно изменять все окна в игре, редактируя класс.

Установим ширину окна, выравниваем его по центру экрана с помощью margin: auto (все как для веб), цвет настроения черный с легкой прозрачностью, и border-radius: 10px.

Изображение

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

Изображение

В классе .panel установим цвет текста, чтобы цвет наследовался всем вложенным элементам, тут тоже все как в веб.

Изображение

Добавим класс для заголовка, просто установив шрифт чуть больше, и перетянем его на метку.

Изображение

В инспекторе можете сами пройтись по атрибутам, там все интуитивно понятно. Скажу только сразу, что разметка изначально настроена на технологию flex. Если вы не знакомы с flex, то гугл вам все расскажет. Все правила из веб разработки касаемо flex применимы и здесь.

Также здесь работает и масштабирование как в Canvas. Вначале мы создавали файл настроек Panel Settings Asset, и если на нем щелкнуть в панели Project, то в стандартном инспекторе мы увидим свойства Scale Mode как в Canvas. По умолчанию установлено Constant Physical Size, что для меня лично удобно, я всегда в Canvas переключал на это значение.

Обработчик событий: оживляем кнопку

Зачем нам это все, если мы не сможем это запрограммировать? Добавим обработчик на кнопку.

Добавим кнопку в наше окошко, сохраним и перейдем на сцену. Создаем скрипт с любым названием, например CafeUI. Ранее мы добавили пустой объект с компонентом UI Document. На него и вешаем скрипт. Переходим в редактор кода, например Visual Studio.

Unity рекомендует обрабатывать элементы страницы в методе OnEnable(), так и сделаем. В листинге ниже комментарии все объясняют.

CafeUI.cs
using UnityEngine; using UnityEngine.UIElements; //ссылка на библиотеку для работы с UI Toolkit  public class CafeUI : MonoBehaviour {     Button okButton;      void OnEnable()     {         //Получаем ссылку на компонент UIDocument         var uiDocument = GetComponent<UIDocument>();         //Находим кнопку таким запросом, в параметр передаем имя кнопки         okButton = uiDocument.rootVisualElement.Q<Button>("okButton");         //Регистрируем событие нажатия кнопки         okButton.RegisterCallback<ClickEvent>(ClickMessage);     }      void ClickMessage(ClickEvent e)     {         //Реализуем тут любые действия при нажатии кнопки         Debug.Log("ok");     } }

Единственного удобства uGUI в перетаскивании методов на кнопку тут нет, но и такой способ особо не напрягает.

Список товаров, связывание данных (ListView и Binding)

В кафе должен быть список товаров, и мы, конечно же, хотим формировать его динамически во время выполнения. Источник данных может быть любой, но в данном случае для простоты мы будем генерировать список в коде, чтобы отобразить его в окне.

Кидаем на страницу в UI Builder элемент ListView, установим ему атрибуты fixed-item-height="50" style="height: 120px; margin: 10px;". В панели Project создаем новый UXML-документ (Create — UI Toolkit — UI Document), задаем ему имя item.uxml и открываем его двойным щелчком или клавишей Enter. Это будет шаблон элементов списка. Он будет состоять из картинки, названия товара и цены. Должно выглядеть это так:

Изображение

Внизу в коде uxml видно, какие атрибуты выставлены элементам. Здесь можно обойтись без uss-стилей, т.к. других окон у нас не будет, но если у вас должно быть несколько окон со списками, то удобнее будет установить единый стиль для списков в одном uss-файле. Цвет текста будет наследоваться от класса .panel. Корневому элементу установлено расположение вложенных элементов в строку и выравнивание посередине. Картинка 50px на 50px, название товара занимает всю оставшуюся область (flex-grow: 1), и для цены установлена фиксированная ширина, иначе весь список будет кривой, т.к. цена может быть разной.

Далее нужно создать класс, который будет представлять данные о товаре:

Item.cs
public class Item {     public string Name { get; set; }     public decimal Price { get; set; } }

Теперь самое сложное. В официальной документации предоставили довольно сложноватый пример, состоящий из нескольких классов и запутанного кода. Я же не люблю плодить много файлов и классов, и максимально упростил пример, чтобы сосредоточиться только лишь на связывании данных. Сделаем все по порядку. Нумерация пунктов соответствует нумерации в комментариях кода.

  1. Инициализируем список товаров. Здесь для простоты создаем список из двух товаров, в реальной игре же источник данных может быть любой — база данных, api-запросы, файлы и т.д.

  2. Находим отображаемый список на странице. Это ListView с именем list на главной странице cafe.uxml.

  3. Связываем шаблон элементов списка с самим списком.

  4. Связываем данные с отображаемым списком.

  5. Регистрируем событие выбора из списка.

  6. Переносим шаблон item.uxml на переменную itemsListTemplate в инспекторе

Изображение

  1. Стилизуем список. Тут пара хитростей. В UI Toolkit есть встроенные классы для каждого контрола, их очень много и можно посмотреть по ссылке https://docs.unity3d.com/2021.3/Documentation/Manual/UIE-ElementRef.html

  • изменим цвет наведения мыши на элемент списка: .unity-list-view__item:hover { background-color: rgba(0, 0, 0, 0.2); }

  • изменим цвет выбранного элемента; второй селектор тут добавлен, чтобы при наведении мыши не менялся цвет выбранного элемента: .unity-collection-view__item--selected, .unity-collection-view__item--selected:hover { background-color: rgba(255, 255, 255, 0.2); }

Эти селекторы можно добавить прямо в файл index.uss.

CafeUI.cs
using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.UIElements; //ссылка на библиотеку для работы с UI Toolkit  public class CafeUI : MonoBehaviour {     Button okButton;      public VisualTreeAsset itemsListTemplate;   //uxml-шаблон элементов списка     List<Item> items = new List<Item>();        //список товаров     ListView itemsListView;                     //список на странице      void OnEnable()     {         //Получаем ссылку на компонент UIDocument         var uiDocument = GetComponent<UIDocument>();         //Находим кнопку таким запросом, в параметр передаем имя кнопки         okButton = uiDocument.rootVisualElement.Q<Button>("okButton");         //Регистрируем событие нажатия кнопки         okButton.RegisterCallback<ClickEvent>(ClickMessage);           //Работаем со списком         //1. Инициализируем список товаров         items.Add(new Item { Name = "Пирожок", Price = 40 });         items.Add(new Item { Name = "Мороженое", Price = 60 });          //2. Находим отображаемый список на странице         itemsListView = uiDocument.rootVisualElement.Q<ListView>("list");          //3. Связываем шаблон элементов списка с самим списком         itemsListView.makeItem = () =>         {             return itemsListTemplate.Instantiate();         };          //4. Связываем данные с отображаемым списком         itemsListView.bindItem = (_item, _index) =>         {             //Связываем список товаров по индексу             var item = items[_index];             //Получаем доступ к визуальным элементам шаблона по именам, которые мы указали в шаблоне             _item.Q<Label>("name").text = item.Name;             _item.Q<Label>("price").text = $"{item.Price} руб";             //В данном случае картинка товара должна лежать в папке Resources и иметь название такое же, как название товара             _item.Q<VisualElement>("image").style.backgroundImage = Resources.Load<Texture2D>(item.Name);         };          //Здесь все стандартно         itemsListView.itemsSource = items;          //5. Регистрируем событие выбора из списка         itemsListView.onSelectionChange += ItemsListView_onSelectionChange;     }      private void ItemsListView_onSelectionChange(IEnumerable<object> obj)     {         if (obj.Count() > 0)         {             var item = obj.First() as Item;             Debug.Log($"{item.Name}: {item.Price} руб");         }     }      void ClickMessage(ClickEvent e)     {         //Реализуем тут любые действия при нажатии кнопки         Debug.Log("ok");     } } 

Результат работы

USS как CSS

USS максимально схож с CSS, благодаря чему знакомый с веб-разработкой будет чувствовать себя здесь в своей стихии. В вашем распоряжении все возможности из мира CSS:

  1. Использование переменных. Например:

Hidden text
:root {   --general-bg: #4D4D4D; } Button {     background-color: var(--general-bg); }

  1. Вложенность и комбинации селекторов (selector1 selector2; selector1 > selector2; selector1, selector2…)

  2. transform, translate, transition…

И т.д. и т.п. Все возможности перечислены в документации

Полезные ссылки

Создание собственных контролов

Встроенные переменные USS

Справочник USS

Справочник по элементам управления. Их на самом деле много, а в окне UI Builder Library выведены только несколько стандартных

Создание шаблонов. Один шаблон мы уже с вами сделали


ссылка на оригинал статьи https://habr.com/ru/post/707754/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *