Масштабирование рабочих процессов Unity: Уроки средних и крупных проектов

Эта запись в блоге — первая из серии статей от Mega Cat Studios, в которых они делятся своим опытом работы с Unity и решениями для реальных задач коммерческой разработки игр. Ознакомьтесь с другими публикациями этой серии, в которых рассматриваются ввод данных, а также дизайн уровней и окружения:
- Попадание в яблочко: Как управляемая событиями Input System в Unity обеспечивает управление в Backyard Baseball 2026
- Как переосмыслить классическую спортивную игру для нового поколения с помощью дизайна уровней, построения мира и визуальных эффектов
Надеемся, вы почерпнёте для себя несколько отличных советов!
У вас есть потрясающая идея, и код пишется так быстро, как вы только можете печатать. С каждым коммитом обретает форму новая функция. Но именно та скорость, с которой формируются ваши идеи, вскоре может привести к тому, что вы столкнётесь с огромным, полным ошибок беспорядком.
В Mega Cat Studios мы начинаем все наши проекты с энтузиазмом, поэтому понимаем соблазн работать быстро и без оглядки, стараясь сделать всё как можно скорее. Для прототипа такой подход вполне приемлем, и, по правде говоря, мы его рекомендуем! Мудрый разработчик знает, когда нужно отдавать приоритет скорости итерации, а когда — стабильности. Потому что, когда вы выходите из фазы прототипа, этот подход «быстро и без оглядки» становится обузой.
Мы много раз проходили через этот переход в Mega Cat Studios и с каждым проектом узнаём что-то новое. Мы хотели бы поделиться некоторыми уроками, которые мы извлекли, чтобы подготовить наш самый недавний проект, Backyard Baseball, к запуску.
Урок 1: Структура для масштабирования

Проблемы масштабирования редко возникают из-за плохого кода; чаще они появляются из-за непродуманной архитектуры. Если разработчик или художник не может найти ассет за 10 секунд, рабочий процесс нужно менять. Несколько советов по настройке вашего проекта для масштабирования:
- Организуйте по типу и назначению: Мы группируем по типу, а затем по назначению. Тип включает такие категории, как арт, код и аудио. Назначение — это то, для чего они используются. Интуитивно понятная организация снижает сложность адаптации; новый художник должен точно знать, где находится спрайт «Character Idle», не спрашивая об этом.
- Держите сцены простыми: Мы отказались от «мега-сцен» и вместо этого выбираем небольшие сцены, такие как главная сцена, содержащая данные сохранения и критически важные системы, а также титульный экран и сцены бейсбольного поля, которые загружаются аддитивно в зависимости от того, находится ли игрок в матче. Аналогичным образом мы используем префабы для автономных функций, поэтому изменения с меньшей вероятностью будут сериализованы в файле сцены, который их содержит. Это позволяет художнику работать над окружением, пока дизайнер настраивает игровой процесс на том же «уровне» без конфликтов файлов (подробнее об этом позже).
- Настройте систему Addressables: Вместо традиционных папок Resources мы используем систему Addressables для загрузки ассетов только по мере необходимости, что позволяет экономно расходовать память. Кроме того, загрузка ассета с помощью его ключа Addressables более понятна и менее подвержена ошибкам, чем загрузка через путь к файлу в определенном месте папки Resources.
Сложность заключается не в понимании этих передовых методов. Сложность в том, чтобы придерживаться их с самого начала и сохранять эту дисциплину даже спустя годы.
Знайте, на каком этапе разработки вы находитесь и что является приоритетом на этой стадии.
«На этапе прототипирования, поскольку кода почти нет, допустимо сначала сделать его функциональным, а уже потом модульным», — говорит Паоло Роксас, разработчик Backyard Baseball. «Мы хотим увидеть, как проект будет развиваться, прежде чем усложнять его».

Урок 2: Работайте с Unity, а не против нее

«Нет ничего мощнее, чем создание строительных блоков — будь то компоненты, ScriptableObjects или пользовательские классы, — которые являются сфокусированными, лаконичными и автономными», — говорит Дэвид Чавес Арментерос, руководитель отдела разработки в Mega Cat Studios. Unity в своей основе поддерживает модульность. Чтобы оставаться гибкими, мы следуем трем классическим принципам:
1. Единственная ответственность: Каждый скрипт или класс должен иметь одну четко определенную роль.
2. Слабая связанность: Системы должны взаимодействовать через интерфейсы или события, а не через прямые ссылки, и только при необходимости. Мы рекомендуем составлять графики зависимостей между системами перед их реализацией в коде, чтобы избежать циклической или запутанной архитектуры.
3. Подключаемость: Комбинируйте небольшие, узкоспециализированные модули для создания сложного поведения, вместо того чтобы писать громоздкие «божественные» скрипты.
Урок 3: Используйте ограничения, чтобы обрести свободу

Определения сборок (AsmDefs) — это конструкции C#, которые группируют ваш код. Их заявленное преимущество — сокращение времени компиляции, но их секретная суперсила — обеспечение модульности.
Нико Гауденци, ведущий разработчик и ненавистник спагетти-кода, ручается за них.
«В Backyard Baseball слой ввода и слой игрового процесса находятся в разных DLL. Игровой процесс полностью абстрагирован от деталей Input System».
Это спасает инженеров от самих себя, превращая каждую зависимость в обдуманное решение. Если бы нам действительно пришлось, мы могли бы переписать всю Input System — от обработки геймпада до Netcode — не рискуя нарушить физику игрока или поведение ИИ. Скорее, это позволяет одному инженеру работать в отдельной области кодовой базы, не вызывая случайных каскадных изменений в другой системе, и сокращает объем кода, который разработчику нужно держать в голове для реализации функций и исправления ошибок.
Урок 4: Тестируйте умнее, а не усерднее
По мере роста проектов может возникнуть «эффект домино»: Небольшое изменение здесь ломает что-то там. Хорошая архитектура очень помогает, но это не панацея.
Прежде чем изменения в функциях попадут в лапы наших уважаемых котов из отдела контроля качества для ручного тестирования, игра проходит тщательный анализ в ходе серии автоматизированных модульных тестов.
«Тесты работают как список требований», — говорит Нико. «Они описывают, чего ожидать, и предоставляют ключевые сценарии использования».
Когда персонаж в Backyard Baseball подает фастбол, крадет базу или выбивает хоум-ран, мы хотим получить конкретные игровые результаты: например, убедиться, что мяч летит со скоростью, соответствующей подаче, тайминг бегущего совпадает с механикой кражи базы, а полевые игроки правильно реагируют на удар. Персонаж должен правильно взаимодействовать с землей, мяч должен двигаться с правильной скоростью, а более детальные системы, такие как флаги контроллера игрока, отслеживающие действия вроде замаха, удара или спринта между базами, должны работать.
Когда мы вносим изменения в силу Пабло Санчеса или, что еще важнее, корректируем общий код, управляющий взмахами биты, нам нужно убедиться, что каждое взаимодействие, от тайминга контакта до траектории мяча, работает согласованно во всей игре.
Часто ломается то, чего совсем не ожидаешь, и именно поэтому тестирование так важно.
Благодаря интеграции этой системы в наш рабочий процесс мы узнаем о нарушении конкретного требования в тот же момент, что сокращает время на тестирование и устранение неполадок, которые отнимают столько же сил, сколько поиск иголки в стоге сена.
Test Runner в Unity работает наиболее эффективно, когда ваши системы модульны, что является еще одной причиной использования Assembly Definitions.
Урок 5: Подготовьте свои ассеты

«Человеку свойственно ошибаться, а прощать — божественно».
Но создать системы, предотвращающие человеческие ошибки с самого начала, — это просто легендарно.
После долгих часов написания кода и отладки неизбежно, что какой-нибудь уставший разработчик сделает несколько неверных кликов или случайно отправит в репозиторий изменения, которые предназначались только для временного использования. Хотя это и простительно (говорю я, будучи одним из тех, кто иногда бывает уставшим), ассет с плохо настроенными параметрами импорта может иметь огромные последствия. А поскольку многие разработчики работают на мощных компьютерах, кошмарный сценарий заключается в том, что проблема производительности остается незамеченной до поры до времени, например, когда сцена стадиона с персонажами, анимацией и эффектами начинает вызывать замедления или нестабильность на менее мощном оборудовании.
Чтобы снизить этот риск, можно ограничить круг лиц, имеющих доступ к ассетам, но это приводит к «узкому месту» из-за множества причин, по которым игровой контент требует корректировки:
- Модель слишком велика, чтобы поместиться в настройки камеры.
- У этого аудиоклипа меньшая громкость, поэтому для него нужен особый эффект.
- Теперь, когда шейдер изменился, каждую текстуру нужно подправить.
В такой игре, как Backyard Baseball, где визуальная идентичность имеет первостепенное значение, модели и визуальные эффекты подвергаются сотням правок, пока мы доводим внешний вид и ощущения до совершенства перед выпуском.
«Никакие технические спецификации не отменяют того факта, что разнообразие контента означает необходимость иметь дело с небольшими, но существенными различиями между разными ассетами», — говорит Нико.
Автоматизация помогает и здесь:
- AssetPostprocessor: Мы пишем собственную логику импорта, которая обеспечивает соблюдение стандартов проекта.
- OnValidate: Мы используем метод OnValidate для сообщения об отсутствующих ссылках в редакторе, который всегда срабатывает перед сборкой.
Впрочем, не позволяйте всем этим разговорам об автоматизации отвлечь вас от простых ручных исправлений, если они выполняются быстрее.
«Никогда не тратьте 10 дней на автоматизацию задачи, выполнение которой вручную занимает 10 минут», — предостерегает Дэвид.
Урок 6: Освойте человеческий фактор (сотрудничество)

Системы Version Control, такие как Git, — это первое, что приходит на ум при координации сотен изменений от десятков разработчиков каждый день. Дэвид рекомендует эти проверенные методики для всех наших проектов в Mega Cat:
- Маленькие, атомарные изменения: Избегайте «мега-коммитов», которые затрагивают множество систем одновременно. Изолируйте работу над отдельными ветками функций, пока они не станут стабильными и не пройдут проверку. Сохраняйте отдельные изменения в отдельных коммитах для хорошо задокументированной истории Version Control, что также упрощает cherry picking и другую магию Git при необходимости.
- Ежедневные слияния из основной ветки: Поддерживайте ветки функций, отделов и долгоживущие ветки в актуальном состоянии относительно основной ветки, так как это может уменьшить размер и сложность финальных слияний, помогая предотвратить масштабные конфликты.
- Проверка запросов на слияние: Это первая линия обеспечения качества, где вы находите ошибки, внедряете стандарты проекта и обеспечиваете согласованность с общей системой.
«В крупных проектах Unity проверка кода — это не просто формальность», — советует Дэвид. «Это ключевая часть предотвращения конфликтов и общего качества проекта».
Просто убедитесь, что те, кто проверяет код, обладают опытом в реализуемой области и знают лучшие практики программирования, чтобы они могли точно оценить правильность и удобство сопровождения.
Существуют специальные советы и рекомендации, которые делают работу с Version Control в Unity максимально плавной. Сцены и префабы составляют основу вашего проекта, поэтому оптимизируйте их не только для производительности процессора, но и для совместной работы разработчиков.
Мы всегда предпочитаем небольшие, аддитивные и вложенные компоненты большой сцене или монолитному префабу. Таким образом, разработчики могут работать параллельно без конфликтов.
Это важно, поскольку конфликты слияния в сценах и префабах сложнее всего разрешить, так как их данные нелегко читаются разработчиками. Чтобы упростить этот процесс, мы сериализуем эти файлы как текст, а не как бинарные данные, и включаем функцию автоматического слияния в нашей конфигурации Git для YAML-файлов. Благодаря этому Git с большей вероятностью самостоятельно разрешит конфликты слияния и сэкономит время разработчиков для важной работы по созданию новых функций.
Но несмотря на все это:
«Предотвращение конфликтов обычно является лучшей стратегией, чем попытки их разрешить», — говорит Нико.
Четкое владение ассетами может значительно помочь в этом.
«Определите, кто может изменять конкретные сцены или префабы», — говорит Дэвид. «Тогда члены команды запрашивают изменения вне зоны своей ответственности, вместо того чтобы редактировать ассеты напрямую».
Нико описывает похожую процедуру как «систему семафоров». По сути, это электронная таблица, в которой разработчики отмечают, когда они изменяют ассет, фактически «блокируя» его. Если другому разработчику нужно внести изменения в этот файл, ему необходимо подождать, пока разработчик, заблокировавший файл, не отправит свои изменения в репозиторий и не «разблокирует» его.
Как всегда, найдите ту процедуру, которая лучше всего подходит вашей команде.
Создание будущего

В Mega Cat Studios мы поняли, что масштабирование проекта Unity — это не столько «усердное написание кода», сколько архитектурная дисциплина. Уважая компонентную природу Unity, обеспечивая соблюдение границ с помощью Assembly Definitions и организуя ассеты с прицелом на будущее, мы сохраняем большую часть творческого потока фазы прототипирования, не превращая проект в технический долг до запуска.
Хотя эти уроки важны, помните, что идеальной кодовой базы не существует. Разработка программного обеспечения — это эпическая битва, в которой рекомендуемые шаблоны программирования и практические соображения сталкиваются ежедневно. Если следование одному из этих принципов тормозит разработку, не принося при этом ощутимой пользы, это знак того, что вам нужно внимательнее относиться к специфике вашей команды, а не к рекомендациям из учебников. В конце концов, каждый проект, каждая команда и каждый человек уникальны.
Мы стараемся находить этот баланс каждый день в Mega Cat Studios. С каждым новым проектом, по мере того как наша библиотека игр продолжает расти, мы надеемся стать лучшими разработчиками Unity и лучшими коллегами.
