Сегодня мы поделимся впечатлениями от работы с нашумевшим фреймворком от Unity — UIToolkit, известным также как UIElements. Мы рассмотрим его основные особенности без глубокого погружения в код. Стоит уточнить, что у команды ранее не было опыта работы с веб-версткой и очевидные для профессионалов этой сферы вещи, для нас могут быть не очевидны.


image


Заметка от партнера IT-центра МАИ и организатора магистерской программы “VR/AR & AI” — компании PHYGITALISM.


Предыстория


Изначально в Unity была IMGUI система, которая полностью создавалась из кода. Такая система была неудобной, как минимум, по двум причинам: много кода, результат только после компиляции в рантайме. На смену IMGUI пришла UGUI система, которой мы привыкли пользоваться. Новая система содержала в себе основные компоненты для работы с UI (Canvas, Image, Button и тп.). Хоть и новая система была гораздо удобнее IMGUI, но проблем всё ещё было достаточно: сложность вёрстки (в том числе и адаптивной), много элементов в иерархии, сложно проектируемая архитектура, медленная работа и т.д.


Многие разработчики, понимая проблемы UGUI, создали свои расширения для Unity Asset Store. Например, нодовые системы типа DoozyUI или простая фича, которая могла бы работать из коробки, — скругление краев изображений типа Procedural UI Image.


Концепция


Вместе с Unity 2019.1 выходит новая система — UIElements (впоследствии переименованная в UIToolkit, однако первый вариант встречается чаще) — новая структура пользовательского интерфейса для Unity. Важно отметить, что акцент во фреймворке сделан на расширении для Editor, поддержка Runtime внедряется последовательно и входит в roadmap.


image

Слайд из презентации к докладу Building UI for games with the new UI Builder — Unite Copenhagen.


Идея построения интерфейса заимствована из хорошо известной вебу верстки на основе HTML/XAML/XML и CSS файлов. В UIElements реализованы аналоги: UXML используют для построения иерархии объектов, а USS для создания классов стилей, т.е. всего, что отвечает за размер, расположение и форму элемента. От оригинальных форматов структурно они особенно не отличаются, синтаксически отличаются слабо, а функционально упрощены, особенно USS (этот вопрос чуть позже рассмотрим подробнее).


image

Слайд из презентации к докладу Building UI for games with the new UI Builder — Unite Copenhagen.


Задачи для UIElements


Наш план был амбициозен: нужно использовать preview пакет в продакшн. Возникает закономерный вопрос, что заставило решиться на подобное?


У нас есть довольно большой проект, который разрабатывается несколько лет и представляет из себя большой корпус визуализации данных, и помимо 3D в нем очень много подобного и одновременно разнообразного 2D контента, представленного по большей части графиками и диаграммами в разных конфигурациях. Можете представить себе количество вариантов префабов, степень вложенности объектов и комплексность GUI системы. Привычный для Unity UGUI опирается на не всегда удобные и зачастую проигрывающие по перфомансу компоненты типа Canvas, CanvasGroup, LayoutGroup. Добавление и удаление элементов вызывает неявный ребилд канвасов, а верстка не формализована. Нам это не нравится.


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


Во-первых, работа в runtime. И действительно изначально Пакет создавался для работы в редакторе и в этом случае он так или иначе справляется с этой задачей. Однако анонсирование поддержки runtime привлекло довольно много внимания на конференции Unite в 2018 году.


Во-вторых, создание переиспользуемых и расширяемых компонентов UI. Количество элементов и их вариаций в проекте значительно выросло, и управлять ими в контексте в системе UGUI становится все сложнее.


В-третьих, снижение нагрузки 2D GUI на систему. А кому не хотелось бы оптимизировать UI и снизить количество вызовов отрисовки? Вот и нам захотелось.


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


С одной стороны, подобные проекты в Unity встречаются нечасто, но с другой — проблемы на деле привычны и знакомы каждому, кто участвовал в разработке проектов на Unity с богатым графическим интерфейсом. Удобство и перфоманс — однозначно слабые места UGUI.


Наш опыт использования


На изучение и интеграцию UIElements мы потратили не один спринт. В проекте мы используем версию 2019.4, и новые функции фреймворка из обновления версии 2020.1 не тестировали.


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


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


Flexbox Я понимаю, что большинству знакомых с версткой не составит проблем сверстать адаптивную панель флексбоксами, однако средний Unity разработчик это не умеет, просто потому обычно ему это не нужно. Как можно заметить, USS все же отличается от CSS и при верстке это нужно учитывать, то есть и опытному в этом вопросе нужно разобраться со спецификой UIElements. Но хочу сказать, когда приходит понимание того, как это работает, верстать становится гораздо легче, и рабочая панель UIBuilder — довольно удобный инструмент для верстки и настройки стилей. И верстать на UIElements все же удобнее, чем верстать на UGUI, т.к. на UGUI флексбоксов просто нет. Однако требует навыков из совершенно разных областей. Многие заметят, что верстка — не задача программиста. Об этом скажу чуть позже.


Материалы и best practices Которых нет. Где можно найти полезную информацию, в документации? Она поверхностная и сырая. В статьях и туториалах? Есть единицы официальных туторов без глубокого погружения, других нет. На форуме? Да, там делятся конкретными проблемами, и свою проблему нужно найти и надеяться, что ей есть решение. Но давайте будем честными: это встречается вообще-то нередко. Я регулярно встречаюсь с ситуациями, когда и хорошо известная технология или подход не хранит все-все-все пограничные случаи и все best practices в одном месте, это всегда процесс, всегда — поиск. Так что аргумент довольно спорный.


Разнообразие путей создания и использования элементов Можно использовать разную степень вовлеченности UIElements в логику UI, начиная с создания компонентов полностью через код в лучших традициях IMGUI и заканчивая вариантом, при котором представление является максимально пассивным, и мы работаем напрямую с версткой через код. Мне кажется, наиболее интересным будет промежуточный вариант, когда у нас есть некий компонент, реализующий всю специфику представления через систему UIElements и работающий с более высоким уровнем через привычные более абстрактные методы, — это позволит при желании относительно безболезненно подменять реализации и не зависеть от UIElements на более высоком уровне. Хотя, конечно, подходы могут быть разные.


Работа со стилями Это, конечно, большой плюс. Работа со стилями доставила много удовольствия после UGUI, пусть и не сразу все было понятно. Но здесь мы столкнулись с рядом проблем и нюансов. Дизайн для нас разрабатывается в Figma, и мы уже были готовы с радостью настроить парсинг в Unity, но это оказалось не так просто. Мало того, что Figma плохо работает с flexbox, так еще желание парсить CSS накладывает определенные обязательства на верстку дизайна и на проработку иерархии, что уже не зависит от разработчика и слабо поддается контролю. При этом USS от CSS отличается более ограниченным набором функций, и это тоже нужно учитывать при верстке, иначе будут ошибки. А ведь это мы еще не начали работу в Unity и UIBuilder. В UIElements есть так называемые inlined styles, благодаря чему информация о стиле элемента содержится напрямую в UXML. Эта вроде как существующая фича на деле крайне нежелательна и не рекомендуется к использованию, как минимум потому что в билде не поддерживается. Но без должного опыта и сноровки это легко пропустить. И это еще не все. При использовании стилей с одинаковыми свойствами на одном элементе, вы получается конфликт стилей, о котором узнаете через получение рандомного результата. Тем не менее, работой со стилями многие остались довольны именно по той причине, что это вносит больше ясности и удобства, чем UGUI.


Возможности анимации По сути, их нет. Мы использовали DOTween Pro для создания анимации, но нужно учитывать, в проекте используется довольно простая 2D анимация, и потребности практически полностью покрываются за счет DOTween.
С обновлением в 2020.1 версии появилась собственная анимация, однако довольно простая по функциям и значительно проще DOTween’а, хотя скорее вcего именно им вдохновлялись разработчики. Вот несколько примеров из UIToolkitUnityRoyaleRuntimeDemo:


public void AnimatedScale(float endScale, float tweenDuration)
        {
            experimental.animation
               .Scale(endScale, Mathf.RoundToInt(tweenDuration * 1000))  
               .Ease(Easing.OutQuad);
        }

       public void MoveTo(Vector2 screenPosition)
        {
            transform.position = 
              new Vector3(
                 screenPosition.x, 
                 screenPosition.y, 
                 transform.position.z);
        }

       public void AnimatedMoveTo(Vector2 endPosition, float tweenDuration)
        {
            experimental.animation
                .Position(
                    new Vector3(
                        endPosition.x, 
                        endPosition.y, 
                        transform.position.z),
            Mathf.RoundToInt(tweenDuration * 1000)).Ease(Easing.OutQuad);
        }

Listview в UIElements — очень полезный инструмент, позволяющий без потери производительности держать сотни кликабельных объектов в списке. В UGUI “из коробки” такого функционала нет, однако есть довольно неплохие ассеты типа EnhancedScroller, которые его реализуют. Разумеется, платные.
Однако в его использовании мы нашли одну интересную особенность. По сути, его функционал оказался не дописан. Например, в качестве системы инпута EventSystem может использовать два типа Event Generation: Read Input (используется по умолчанию) и IMGUI Events.


image

С опцией Read Input кликабельность Listview не будет работать, т.к. метод ProcessMouseEvents() класса UIElementsEventSystem не обрабатывает и не отсылает информацию о количестве кликов. При переключении на режим IMGUI Events список начинает работать. Интересная деталь, которая может оказаться полезной. Эту часть можно доработать самостоятельно, но оставим это ребятам из Unity Technologies.


Парсер фреймворка на данный момент испытывает определенные трудности. Выглядеть это может по-разному. Например, есть ошибка при записи и последующем чтении CSS свойств, которые являются дефолтными для компонента. Подробнее можно посмотреть ЗДЕСЬ. Также кастомные свойства наследованного от VisualElement компонента некорректно отображаются в инспекторе, что затрудняет работу с UIBuilder. Или, например, невозможность работы с inlined стилями, которые с одной стороны существуют, а с другой — никак не работают. А если уж и работают, то точно не в билде. По крайней мере, пока. Подробнее можно почитать ЗДЕСЬ. Однако работа над UIElements ведется крайне активно, и многие ошибки исправляются.


Специалисты по верстке Сложно найти верстальщиков, а по сути — невозможно, потому как с таким стеком никто не работает. Будьте готовы к тому, что программисты будут верстать и сетовать, либо учите верстальщика работе в Unity и специфике UIElements.


Кроссплатформенность, то, за что зачастую выбирают Unity. К сожалению, на данный момент не реализована полноценная кроссплатформенность. Тестируемый проект на Windows планируется к портированию на WebGL, но именно эта платформа не работает, в билде искажая визуал. Из-за чего и пришлось отказаться от использования UIElements в этом проекте. О проблеме известно и с ней планируют работать, подробности можно посмотреть в ЭТОМ треде форума. Поэтому, конечно, необходимо максимально подробно ознакомиться с документацией и форумом на предмет потенциальных проблем на целевой платформе.


Вывод


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


Нужно ли изучать и пробовать этот фреймворк, несмотря на все недостатки, о которых мы говорили выше?


image

Скриншот из треда Unity Forum.


Мы пришли к выводу, что на данный момент UIElements можно использовать только в ограниченном типе проектов мобильных и PC платформ, мы планируем тестировать его продакшн применение на небольших проектах, технические требования которых удовлетворяют текущим возможностям UIElements. Разработчики “тонко” намекают, что именно его будут развивать, а не UGUI.