Проект Unity под все платформы на все случаи жизни

Проект Unity под все платформы на все случаи жизни

Сегодня я хотел бы поделиться одним из шаблонных проектов, настройками и модулями которого пользуюсь от проекта к проекту. С помощью него я покажу один из вариантов настройки Unity-игры, которая будет работать на множестве платформ, включая Steam, Android-площадки (Google Play, RuStore), Web-площадки (платформы, которые поддерживаются Playgama (Instant) Bridge, например, Яндекс Игры, VK, OK, CrazyGames, Game Distribution и тд).

Данный шаблонный проект покрывает следующие темы:

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

Шаблонный проект доступен для скачивания по следующей ссылке.

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

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

Итак, начнем…

Работа с базовыми классами проекта

Иерархия игровых объектов
Иерархия игровых объектов
Основные папки проекта
Основные папки проекта

Для начала пару слов о структуре проекта. Если в случае с большинством папок все довольно понятно (но так или иначе мы разберем всё в соответствующих главах), то про папку Scripts стоит упомянуть отдельно. В корне этой папки хранятся все скрипты, которые наследуются от класса MonoBehaviour, и которые соответственно могут быть добавлены к игровым объектам в качестве компонент.

В папке Services находятся классы для работы с локализацией, метриками, звуком и рекламой.

В папке Settings находится все необходимое для успешного сохранения и загрузки данных игры.

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

В данной главе остановимся на двух основных классах папки Scripts, а именно SingletonServices и GameServices.

Начнем с первого и наиболее часто используемого. SingletonServices, как видно из названия работает по принципу шаблона singleton, соответственно, при инициализации объектов проекта будет создан только один такой объект класса SingletonServices. Через данный класс осуществляется доступ ко всем имеющимся вспомогательным сервисам игры, которые могут понадобиться на любой сцене. Делаю акцент на слове «любой», так как, если, к примеру, сравнивать данный класс с GameServices, то использование последнего ограничивается только игровыми сценами, исключая сцену для инициализации. Но об этом подробнее будет дальше.

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

_singletonServices.SaveSettings();

где переменная _singletonServices предварительно была инициализирована следующим образом:

SingletonServices _singletonServices = SingletonServices.Instance;

Для корректной работы SingletonServices должен быть добавлен в игровой объект на инициализационную сцену (InitScene).

Второй по значимости класс – GameServices также является singleton-ом и используется для объединения в себе классов и методов для работы с игровыми объектами. Например, скрыть меню игры и показать саму игру, и наоборот, а также сменить язык и другие. При значительном расширении проекта все же рекомендую выделять, например, работу с UI игры в отдельный скрипт и использовать GameServices только в качестве промежуточного класса для доступа до UI-контроллера. В нашем шаблонном проекте для этого нет большой необходимости.

Для корректной работы GameServices должен быть добавлен в игровой объект на сцену, идущую сразу за инициализационной (в нашем случае это MainScene). К тому же, так как GameServices использует внутри себя методы SingletonServices, он должен быть инициализирован после последнего. Для этого идем в настройки проекта и задаем порядок исполнения скриптов в следующем виде:

Порядок исполнения скриптов
Порядок исполнения скриптов

Сохранение данных игры

Как уже упоминал ранее, все скрипты для сохранения данных игры находятся в папке Scripts/Settings.

Проект Unity под все платформы на все случаи жизни

Здесь основным объектом для данных игры является GameSettings, в который для примера я добавил несколько полей для сохранения, например, выбранный язык и количество нажатий на кнопку.

Файл, который занимается непосредственно загрузкой и сохранением GameSettings называется FileSettingsAdapter. Он реализует интерфейс ISettingsAdapter, в котором находятся только два метода: загрузка и сохранение.

Если в случае с сохранением все довольно очевидно, то про сигнатуру метода загрузки стоит сказать пару слов. Сейчас она имеет следующий вид:

void LoadSettings(Action<GameSettings> callAfterLoad);

Метод ничего не возвращает, а лишь принимает в качестве параметра делегат, ссылку на метод, который должен быть вызван после того, как загрузка данных завершится. В общем случае (например, для PC и Android билдов) в такой конструкции нет необходимости. Сделано это для того, чтобы поддержать web-версию, о которой мы поговорим отдельно в одной из следующих статей.

Весь процесс загрузки настроек игры можно посмотреть в SingletonServices.Start, процесс сохранения можно посмотреть, например, в методе GameServices.IncreaseCounter.

Импорт текстур, работа со спрайтами и Sprite Atlas

Обсудим теперь составляющие папки Sprites и начнем с небольшого, опционального файла TextureImporter. Неизвестно по какой причине, но Unity решили изменить дефолтное поведение импортера спрайтов и стали по умолчанию Sprite Mode выставлять в значение Multiple (раньше было Single), которое означает, что в добавляемой картинке находится больше чем один спрайт, к примеру, это может быть sprite sheet (спрайт-лист) с анимацией персонажа. Так вот если в вашем проекте спрайт-листов гораздо меньше, чем обычных одиночных спрайтов, то вы можете создать TextureImporter и задать ему настройки спрайтов, которые по умолчанию будут применяться ко всем импортируемым текстурам. Для этого в настройках импортера надо нажать на кнопку выбора пресетов и создать новый с нужными параметрами импорта.

Создание пресета
Создание пресета

У созданного пресета меняем все нужные параметры (в случае с конкретно этим шаблонным проектом был изменен Sprite Mode на Single и выключена компрессия) и добавляем пресет в дефолтные.

Добавление пресета по умолчанию
Добавление пресета по умолчанию

Теперь при добавлении новых спрайтов настройки их будут соответствовать настройкам созданного TextureImporter.

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

Создание SpriteAtlas
Создание SpriteAtlas

Теперь о настройках атласа.

Настройки SpriteAtlas
Настройки SpriteAtlas

Крайне осторожно нужно обращаться с параметрами Allow Rotation и Tight Packing. По умолчанию они включены, но это периодически приводит к различным искажениям, перевертышам и артефактам, если вы на ходу из кода меняете спрайты у компонент.

Ну и конечно здесь же не забываем добавлять папки, которые будут вложены в созданный sprite atlas. В нашем случае это одна папка с именем Atlas. В директории Sprites существует также и папка NonAtlas, которую, как можно судить по названию, мы не добавляем в sprite atlas. Здесь могут лежать, к примеру, иконки, курсор и тд.

Пример получения спрайтов из sprite atlas по имени можно посмотреть в методе SoundsButtonController.Start.

Переходы между сценами

Пример перехода с инициализационной сцены на главную можно посмотреть в классе InitSceneLoader. Сразу уточню, что данный скрипт должен быть добавлен в качестве компонента в игровой объект на сцену InitScene.

Процесс ожидании инициализации сервисов и переход на новую сцену
Процесс ожидании инициализации сервисов и переход на новую сцену

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

Настройки для сборки проекта

Main-ветка проекта сейчас настроена под Android-билд, поэтому в данной статье покажу основные настройки именно под эту платформу, под оставшиеся опишу в последующих статьях.

Итак, создавая Android-проект, я в первую очередь иду в раздел Project Settings -> Player -> Publishing settings и выставляю чекбоксы на следующие файлы: Custom Main Gradle Template, Custom Gradle Properties Template и Custom Gradle Settings Template.

Настройки Android-билда
Настройки Android-билда

Можете мне поверить это избавит вас от знатной головной боли, когда речь зайдет об импортировании и использовании сторонних пакетов, например, пакет EDM он же external dependency manager, который бывает нужен для того же Firebase, Appodeal и тд.

Следом я поднимаюсь в настройки Other Settings, задаю имя пакета и выставляю Target API Level.

Проект Unity под все платформы на все случаи жизни

По умолчанию Target API Level выставлен в значение Automatic (highest installed), что означает, что Unity при билде Android-проекта будет отталкиваться от уже установленных на компьютере Android SDK и NDK, поэтому если, к примеру, на компьютере установлена 33-я версия, то собрав пакет вы не сможете его залить в Google Play Console, т.к. на текущий момент минимальное значение Target API Level должно быть 34. Но и торопиться и выставлять руками самую высокую версию я бы тоже не рекомендовал, или во всяком случае имейте возможность протестировать свое приложение на чуть больше, чем одном устройстве. Можете словить что-то подобное:

Ошибка запуска игры на 34-й версии API
Ошибка запуска игры на 34-й версии API

Если коротко, то не так давно при выставлении Target API Level в 34, игра падала при запуске именно на девайсах с 34-м уровнем API (на остальных успешно работала). Решение тут, к сожалению, не самое приятное и придется обновить Unity на более свежую версию, где был залит фикс, что не всегда бывает возможно и/или безболезненно.

.gitignore проекта

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

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

Коплю вишлисты тут и тут.

Спасибо за внимание!

99
5 комментариев

Я бля ненавижу этот движок. Че он сотворил с тарковым и сововоктиками

2
Ответить

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

1
Ответить

ага, на unity тоже возможно очень крутые проекты сделать, на последнем unity unite довольно много впечатляющих проектов показали

1
Ответить

Вот такая репутация - главная проблема юнити

Ответить

Так Тарков, это инди

Ответить