Лучшие практики для Unity 3D проекта

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

Всего было написано 6 проектов (1 основной проект и 5 вспомогательных пакетов):

  • Clean Game Example — пример простого шутера. Пример содержит: главное меню и несколько игровых уровней. В главном меню мы можем выбрать уровень, персонажа или настроить некоторые параметры. В самой же игре мы можем подобрать, выкинуть или поменять оружие, стрелять и убивать врагов, и соответственно пройти или проиграть уровень.

  • Clean Architecture Game Framework — фреймворк, который задает архитектуру всего проекта и решает некоторые мелкие проблемы.

  • Addressables Extensions — обертка над Addressables, делающая загрузку и выгрузку ресурсов более удобной.

  • Addressables Source Generator — инструмент для генерации исходного кода, содержащего все адреса и метки адресуемых ассетов. Благодаря этому инструменту, вы всегда можете быть уверенным, что в вашем проекте используются только правильные адреса и метки.

  • Colorful Project Window — расширение окна проекта, которое подсвечивает: пакеты, модули, исходники и ассеты. Благодаря этому инструменту вы моментально видите все необходимые пакеты, модули и их исходники и ассеты.

  • UIToolkit Theme Style Sheet — библиотека стилей для UIToolkit, написанная при помощи Stylus.

Проект Clean Game Example

Assets

Assets

Packages

Packages

Addressables

Addressables

Проект состоит из ассетов (основных модулей) и пакетов (дополнительных модулей). Все основные модули расположены в красной папке Project. Все зеленые папки содержат исходники и отображают их namespace. Все желтые папки содержат ассеты и отображают их адреса. Папки с исходниками так же содержат *.asmref файлы, которые привязывается их к конкретным модулям. А сами модули определены в папке Assemblies. Проект так же содержит дополнительный модуль Project.Infrastructure, который расположен отдельно от основных модулей и содержит все общее или все, что мне хотелось скрыть с глаз долой.

Модуль Project

Project

Project

Корневой модуль содержит точку входа, а так же некоторые инструменты.

Модуль Project.UI

Project.UI

Project.UI

Модуль UI содержит аудио тему, экран пользовательского интерфейса, роутер управляющий состоянием приложения и все виджеты.

Модуль Project.UI.Internal

Project.UI.Internal

Project.UI.Internal

Модуль UI.Internal содержит все вьюшки. Данный модуль не имеет зависимостей на другие модули, таким образом вьюшки тоже не имеют лишних зависимостей.

Модуль Project.App

Project.App

Project.App

Модуль App содержит все сущности и сервисы уровня приложения. Так же отвечает за запуск и остановку игры.

Модуль Project.Entities

Project.Entities

Project.Entities

Модуль Entities содержит базовые игровые сущности: игра и игрок.

Модуль Project.Entities.Actors

Project.Entities.Actors

Project.Entities.Actors

Модуль Entities.Actors содержит сущности, которые могут действовать под управлением игрока или самостоятельно.

Модуль Project.Entities.Things

Project.Entities.Things

Project.Entities.Things

Модуль Entities.Things содержит сущности, которыми актеры могут владеть.

Модуль Project.Entities.Worlds

Project.Entities.Worlds

Project.Entities.Worlds

Модуль Entities.Worlds содержит миры и разные объекты окружения.

Модуль Project.Infrastructure

Project.Infrastructure

Project.Infrastructure

Модуль Infrastructure содержит, в первую очередь, все общие абстракции, а так же все, что мне хотелось скрыть от глаз.

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

Пакет Clean Architecture Game Framework

Фреймворк задает архитектуру проекта, а так же предоставляет некоторые простые утилиты. Я был вдохновлен идеями чистой архитектуры, поэтому название выбрал соответствующие. Сам фреймворк состоит из трех модулей (слоев):

Модуль Clean.Architecture.Game.Framework.Core

Clean.Architecture.Game.Framework.Core

Clean.Architecture.Game.Framework.Core

Модуль Core содержит все самое важное.

  • UnityEngine.Framework

  • UnityEngine.Framework.UI

    • UIThemeBase — аудио тема.

    • UIScreenBase — пользовательский интерфейс. Пользовательский интерфейс состоит из иерархии бизнес юнитов (UIWidgetBase) и иерархии визуальных юнитов (UIViewBase). Т.е. к экрану крепится иерархия виджетов, каждый виджет может содержать (или не содержать) вьюшку. За добавление вьюшки в иерархию визуальных юнитов, обычно отвечает корневой виджет, но могут и другие виджеты-контейнеры добавлять дочерние вьюшки в себя. По идее получается как в Uber Ribs.

    • UIRouterBase — менеджер состояния приложения. Роутер отвечает за: загрузку главного меню, загрузку игры, перезагрузку игры, выгрузку игры и закрытие всего приложения.

    • UIWidgetBase — бизнес юнит пользовательского интерфейса.

    • UIViewBase — визуальный юнит пользовательского интерфейса.

Модуль Clean.Architecture.Game.Framework

Clean.Architecture.Game.Framework

Clean.Architecture.Game.Framework

Основной модуль содержит разные дополнения. Самое интересное это:

  • UIRootWidgetBase и UIRootWidgetViewBase — корневой виджет отвечает за размещение дочерних виджетов на экране.

  • IDependencyContainer — по своей сути это просто service locator.

Модуль Clean.Architecture.Game.Framework.Internal

Clean.Architecture.Game.Framework.Internal

Clean.Architecture.Game.Framework.Internal

Модуль Internal содержит разные утилиты и прочие внутренности. Самое интересное это:

  • Assert — позволяет делать проверки в более удобной форме, чем стандартный Debug.Assert.

  • Option — тип который иногда бывает очень полезным.

Пакет Colorful Project Window

Как вы уже заметили мое окно проекта очень удобно разукрашено в разные цвета. Я подсвечиваю: пакеты (синий), модули (красный), исходники (зеленый) и ассеты (желтый). Это значительно упростило навигацию по проекту, снижает когнитивную нагрузку и сохраняет много нервов.

Итоги

Думаю у меня получилось написать проект с удобной структурой и архитектурой. Но даже такой простой проект очень быстро разросся и стал трудно поддерживаемым. И это даже притом, что в последние годы в Unity появилась поддержка модульности (Assembly Definition) и зависимостей (Package Manager). А не так давно и этого не было ((

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

  • Наверное самая основная проблема Unity это MonoBehaviour. В классах наследованных от MonoBehaviour нельзя использовать конструктор, аргументы конструктора и соответственно константные поля и свойства. Это превращает программирование в реальный идиотизм. Кстати, на мое большое удивление, в Unreal тоже нельзя передать аргументы в конструктор актера. Это очень странно и неудобно. Возможно я чего-то не понимаю.

  • Сторонние объекты не могут слушать события GameObject’а. Только MonoBehaviour может слушать и обрабатывать Unity событие. В теории можно было бы полностью отказаться от MonoBehaviour и использовать обычные POCO классы, но тогда мы не сможем обрабатывать многие события.

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

  • Нет возможности временно выгрузить все лишние из проекта. Например, в Visual Studio можно выгрузить любые проекты из решения. Это позволяет разработчику сфокусироваться на конкретном проекте и выкинуть из головы все остальное. Особенно это полезно, когда решение становится достаточно большим и изменение одного проекта влечет к проблемам в других проектах. К сожалению в Unity это не предусмотрено. И нам приходится постоянно держать в голове весь код и все ассеты. Я не знаю как с этим в других движках, но думаю, что так же.

  • UIToolkit это ад. Я сильно пожалел, что использовал UIToolkit в данном проекте. Идея с таблицами стилей оказалась очень не удачной. Во первых нужно много времени, чтобы разобраться во всех свойствах стилей. Во вторых нужно еще больше времени, чтобы разобраться как устроены элементы, чтобы написать для них стили. Я потратил крайне много времени и нервов на написание стилей для нескольких элементов. И все это может сломаться в любом следующем обновлении. Хуже того, Unity интегрировала этого монстра в сам редактор. А это значит, что он теперь с нами навсегда. А было бы разумнее максимально облегчить редактор и рантайм.

  • Просто удивительное отношение компании к качеству своего продукта. Наверное многие знают, что в Unity константы некоторых цветов всегда были немного неправильными. Спустя много-много лет разработчики все таки исправили константу Color.green. Я уже подумал, что наконец-то в Unity взялись за головы, но потом заметил, что Color.yellow до сих пор остается не желтой, а оттенком желтого (Yellow. RGBA is (1, 0.92, 0.016, 1), but the color is nice to look at!). Я помню, как какой-то Unity разработчик писал, что они никогда не будут исправлять Color.green т.к. для них очень важна обратная совместимость. Как видим обратная совместимость для них все же не очень важна. И качество их продукта по всей видимости тоже не очень важно. Вы можете представить, чтобы в каком-то ПЛАТНОМ графическом редакторе был не черный цвет, а почти черный? А вы можете представить, чтобы это продолжалось в течении 20 лет? Весь Unity это сплошное наследие. Конечно же в некоторых случаях обратная совместимость, к большому сожалению, очень важна, но точно не в этом случае. Я недавно открывал баг репорт (достаточно серьёзная проблема в UIToolkit), но Unity его просто закрыли со словами «There are no fixes planned for this Bug». Наверное на то были свои причины, но все же… Как обычно многое могло бы быть лучше, но как говорится, пипл схавает.

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

Ссылки

  • https://github.com/Denis535/CleanGameExample/tree/v1.0.7

  • https://openupm.com/packages/com.denis535.clean-architecture-game-framework

  • https://openupm.com/packages/com.denis535.addressables-extensions

  • https://openupm.com/packages/com.denis535.addressables-source-generator

  • https://openupm.com/packages/com.denis535.colorful-project-window

  • https://assetstore.unity.com/packages/tools/gui/uitoolkit-theme-style-sheet-273463

© Habrahabr.ru