Оптимизация игры на Unity: как повысить производительность и FPS
Оптимизация игры на Unity ускорение FPS лучшие практики
Кому и когда нужна оптимизация игры на Unity: распознаём «красные флаги»
Оптимизация имеет смысл, когда у вас уже есть играбельный прототип или почти готовый проект на Unity, а не только идея в голове. Статья пригодится инди‑разработчикам, небольшим студиям и техлидам, которые видят падения FPS, фризы, долгую обработка кадров и вылеты, особенно на мобильных устройствах.

Характерные «красные флаги»:
- • FPS резко проседает в сценах с большим количеством объектов: толпа врагов, плотный лес, массивный UI, много эффектов. Количество draw calls растёт, gpu не успевает, кадр рендерится по 40–60 мс вместо целевых 16–33 мс.
- • Фризы при спавне противников, загрузке новых зон, появлении экранов интерфейса или открытии window с инвентарём. Если каждое Instantiate / Destroy порождает лаг, значит, пора пересматривать процесс работы с объектами.
- • Вылеты на слабых устройствах при длительной сессии: утечки памяти, чрезмерное использование текстур высокого разрешения, тяжёлые сцены, которые не выгружаются.
Важно отличать технические проблемы от завышенных ожиданий. Если вы запускаете красивый 3D‑шутер с продвинутой обработка постэффектов на старом Android — иногда проще снизить качество в Quality Settings: уменьшить тени, отключить часть эффектов, снизить разрешение рендера, вместо того чтобы переписывать полдвижка.
Системная оптимизация нужна, когда:
- • даже на средних устройствах при средних настройках FPS падает ниже 25–30;
- • любая новая сцена добавляет ещё 10–20% просадки;
- • отключение «красивостей» почти не помогает, значит, проблема в коде, физике или архитектуре.
Реалистичный ориентир: при грамотной работе с рендером, памятью и структурой сцен можно получить +20–50% FPS без тотального ухудшения графики. В случаях, когда проект совсем не думал об оптимизации, прирост бывает и в 2–3 раза за счёт уменьшения draw calls и переезда на batching.
С чего начать оптимизацию игры на Unity: измеряем FPS и ищем реальные узкие места
Первый шаг — отказаться от оптимизации «на глаз». На практике легко потратить недели на переписку шейдеров, когда основная проблема — сотни методов Update на каждом gameobject или агрессивный GC. Вместо догадок нужны цифры: время кадра, нагрузка на CPU и GPU, динамика памяти.
Минимальный набор метрик, за которыми стоит следить:
- • FPS и время кадра (Frame Time). Цель очевидна: удерживать кадр в пределах 16,6 мс для 60 FPS или 33,3 мс для 30 FPS.
- • CPU Time vs GPU Time. Если CPU загружен сильнее — проект CPU‑bound (логика, физика, скрипты). Если gpu time стабильно выше — игра упёрлась в рендеринг.
- • Память: total allocated, used heap, пики аллокаций и частые сборки мусора (GC). Резкие всплески — симптом лишних new в горячем коде.
Основной инструмент Unity — встроенный Profiler. Откройте его через меню Window → Analysis → Profiler. Важные вкладки для FPS:
- • CPU: смотрите, какие функции и системы «съедают» миллисекунды. Часто всплывают избыточные Update, костыльные корутины, тяжёлый AI.
- • GPU: полезно, когда включен поддерживаемый renderer. Здесь видно, какие этапы рендера нагружают видеочип: opaque, transparent, постобработка, тени.
- • Rendering: количество draw calls, количество отрисованных объектов, overdraw. Если draw calls переваливают за 1000–1500 на мобилках — почти всегда есть резерв для batching.
- • Memory: объём загруженных текстур, моделей, аудио, динамика GC. Здесь хорошо заметны сцены, которые «не отпускают» память.
Frame Debugger помогает увидеть, какие объекты реально попадают в кадр и генерируют draw calls. Включите Window → Analysis → Frame Debugger, нажмите Enable и шагами просматривайте, что именно отрисовывается. Вы удивитесь, сколько невидимых object всё ещё рендерятся из‑за неправильных слоёв или сломанных масок.
Критически важно профилировать не только в Editor, а в реальном билде на целевой платформе. Editor добавляет свой оверхед, а на мобильных многое работает иначе: другие драйверы gpu, особенности памяти, троттлинг. Используйте Attach to Player в profiler, чтобы получать данные прямо с устройства.
Как понять, где «бутылочное горлышко»?
- • Признаки CPU‑bound: вкладка CPU забита пользовательскими скриптами, Physics, Animation, а GPU time в два раза меньше. В Update/FixedUpdate много сложной логики, сотни объектов с тяжёлыми компонентами.
- • Признаки GPU‑bound: GPU time стабильно выше CPU, FPS почти не меняется при отключении логики, но растёт при упрощении материалов и отключении постобработки.
Разбор на примере. Сцена: арена, 200 врагов, игрок (player) и множество снарядов. Profiler показывает: Physics — 10 мс, Rendering — 6 мс, GC — регулярные пики до 5 мс. При таком раскладе первым делом оптимизируем столкновения и структуру физических объектов, затем вычищаем лишние аллокации (спавн пуль, списки целей), и только потом думаем о текстур и материалах. Любой процесс оптимизации должен идти от замеров к целевым правкам, а не наоборот.
Графика и рендеринг: как поднять FPS за счёт уменьшения draw calls и нагрузки на GPU
Большинство популярных запросов по теме звучит одинаково: «как увеличить FPS в Unity», «как уменьшить draw calls», «какие настройки renderer трогать в первую очередь». Здесь основной рычаг — организация отрисовки: batching, материалы, освещение и постобработка.
Draw calls — это команды на отрисовку, которые CPU отправляет на GPU. Каждая команда имеет свой фиксированный оверхед. Сотни мелких объектов тормозят сильнее, чем несколько крупных, даже при одинаковом общем количестве треугольников. Unity предлагает несколько способов уменьшить количество draw calls:
- • Static batching. Работает для объектов, отмеченных как static (флажок Static в инспекторе). Движок объединяет геометрию, если у объектов одинаковые материалы. Эффективно для зданий, скал, декора, который не двигается.
- • Dynamic batching. Включается в Player Settings. Подходит для маленьких динамических объектов с общими материалами. Но у него есть жёсткие лимиты по количеству вершин и особенностям шейдера; при злоупотреблении может даже ухудшать производительность.
- • SRP Batcher. Если вы используете URP или HDRP, включайте SRP Batcher — он оптимизирует работу renderer с материалами и уменьшает количество дорогостоящих переключений состояний GPU. На проектах с сотнями материалов даёт заметный прирост.
Лучше всего batching работает, когда вы уменьшаете разнообразие материалов и шейдеров. Полезные практики:
- • Объединяйте текстуры в атласы. Для 2D используйте Sprite Atlas, для 3D — общие текстуры для групп моделей. Чем меньше переключений материалов между объектами, тем дешевле сцены для GPU.
- • Уменьшайте количество уникальных материалов. Вместо десяти почти одинаковых материалов для разных врагов используйте одну базу с разными параметрами в маске цвета или текстуре.
- • Избегайте дорогих шейдеров на массовых объектов. Эффекты с несколькими проходами, сложной обработка освещения, динамическими отражениями оставьте для ключевых героев, а не для травы или мусорных баков.
Освещение — ещё один тяжёлый блок для рендера. Что имеет смысл сделать:
- • Активно использовать запечённый свет (lightmaps). Статические источники и static‑объекты можно «запечь» в текстуры и не считать свет в реальном времени на каждом кадр.
- • Ограничивать количество динамических источников света с включёнными тенями. Для мобильных обычно хватает одной–двух теневых ламп в сцене, остальные работают без теней.
- • Настроить тени в Quality Settings: уменьшить Shadow Distance, снизить Resolution, уменьшить число Cascades. Это даёт серьёзную разгрузку GPU без тотального ухудшения картинки.
Постобработка (post‑processing) легко убивает даже хороший gpu, особенно на слабых телефонах. Наиболее тяжёлые эффекты:
- • Bloom с высоким качеством и несколькими проходами;
- • Ambient Occlusion в экранном пространстве;
- • Depth of Field;
- • Motion Blur.
Для мобильных устройств обычно разумно:
- • оставить только один–два эффекта, которые действительно улучшают восприятие (например, лёгкий Bloom и Tone Mapping);
- • предусмотреть отдельный пресет постобработки для low‑настроек или возможность полностью отключать её в settings;
- • использовать эффекты реже и локально: не весь экран, а только камеру UI или отдельные сцены.
Кейс из практики. 3D‑игра с открытыми уровнями, 3000–3500 draw calls на кадр, множество материалов. После объединения текстур в атласы, пересмотра материалов, включения static batching и SRP Batcher количество draw calls срезали до 600–800, а GPU Time сократился примерно вдвое. На средних Android‑устройствах это дало прирост FPS с 25–30 до стабильных 45–55 без радикального упрощения моделей.
Сцены, объекты, физика и анимации: управляем сложностью мира
Даже при аккуратном рендере можно «убить» игру избыточной сложностью мира: слишком тяжёлые сцены, тысячи активных объектов, физика для всего и анимации на каждом кусте. Здесь важно управлять тем, что реально участвует в кадре и логике.
Организация сцен:
- • Разделяйте большие уровни на несколько под‑сцен (additive scenes): геймплей, окружение, UI, звук. Подгружайте и выгружайте их по мере необходимости, чтобы в памяти не висело всё сразу.
- • Отключайте далёкие области, если игрок их не видит. Trigger‑зоны, которые активируют и деактивируют группы объектов, часто дают больший выигрыш, чем попытки оптимизировать каждый gameobject по отдельности.
LOD и дальность отрисовки:
- • Используйте LOD Group для 3D‑моделей. Высокополигональная версия нужна только рядом с камерой; дальше достаточно упрощённых моделей или даже billboard‑спрайтов.
- • Настройте Clip Planes у камеры: уменьшая far clip, вы снижаете количество объектов, которые попадают в draw. Для мобильных игр часто достаточно 100–300 метров видимости вместо бесконечности.
- • Для массовых декоративных объектов можно вообще отключать рендер после определённой дистанции — игрок всё равно не заметит разницы.
Object Pooling — обязательный приём для игр с активным спавном. Частые Instantiate/Destroy создают мусор в памяти и вызывают паузы, когда GC очищает этот мусор.
- • Принцип прост: вы создаёте пул объектов один раз (например, 100 пуль), а потом только активируете/деактивируете их. Никаких новых аллокаций в горячей фазе боя.
- • Типичные кандидаты в пул: пули, враги, снаряды, эффекты, всплывающие UI‑элементы, даже временные подсказки для player.
Физика:
- • Уменьшайте количество активных Rigidbody и Collider. Не каждое декоративное ведро обязано участвовать в симуляции.
- • Настройте Layer‑based collision matrix (Project Settings → Physics), чтобы отключить ненужные столкновения: «пули — пули», «декор — декор» и т.п. Это сильно сокращает количество проверок.
- • Пересмотрите Fixed Timestep. Часто 50 FixedUpdate в секунду избыточны, особенно для мобильных. Снижение до 30–40 шагов уменьшает нагрузку, а геймплей остаётся приемлемым.
Анимации тоже влияют на производительность:
- • Ограничьте количество активно работающих Animator в кадре. Для массовок подойдёт упрощённая анимация через скрипт (например, покачивание растений по синусоиде), вместо полноценного аниматора на каждый куст.
- • Комбинируйте несколько однотипных персонажей в одну анимационную систему, если это возможно, или используйте baked‑анимации, когда скелетная анимация не нужна.
Практический пример. В шутере фризы возникали в момент массового спавна врагов: по 20–30 объектов за кадр. Profiler показывал всплески GC и скачки Physics. Решение: ввели пул врагов (перенастроили спавн под переиспользование), пересмотрели collision matrix (убрали ненужные взаимодействия между слоями «враги» и «декор»), сократили Fixed Timestep. Фризы исчезли, время кадра стабилизировалось, а общее количество активных объектов в сцене заметно уменьшилось.
Код и память: как не создавать «мусор» в каждом кадре
По запросам «оптимизация кода Unity», «как снизить GC» чаще всего всплывают одни и те же проблемы: избыточный Update, бесконечные аллокации и LINQ в горячем пути. Здесь небольшие изменения дают огромный эффект.
Типичные ошибки в скриптах:
- • Лишняя логика в Update, LateUpdate, FixedUpdate. В одной сцене легко набирается 50–100 компонентов, каждый из которых проверяет по одному условию раз в кадр.
- • Создание объектов и коллекций в игровом цикле: new List каждую секунду, строки через конкатенацию, вызовы ToString в UI‑обновлениях, LINQ‑запросы в Update.
- • Boxing при работе с коллекциями и интерфейсами, особенно с value‑типами (struct).
Как бороться с GC:
- • Избегайте аллокаций в горячем коде. Коллекции, которые нужны каждый кадр, предсоздавайте и переиспользуйте; вместо new внутри цикла — очистка уже существующего списка.
- • Используйте StringBuilder для частых операций со строками (лог, отладочные UI‑метки), вместо конкатенации.
- • Включите вкладку Memory в profiler и отслеживайте, какие методы вызывают всплески аллокаций. Это часто один‑два скрипта, которые «стреляют себе в ногу».
Корутины удобны, но легко превращаются в хаос, когда каждая мелочь запускает свою корутину.
- • Используйте их для действительно долгих операций (загрузка сцен, анимации интерфейса, поэтапная генерация мира), а не для проверки одного булевого флага раз в секунду.
- • Следите за количеством одновременно работающих корутин: сотни и тысячи — сигнал, что архитектура трещит по швам.
Архитектурные приёмы:
- • Разделяйте игровые системы так, чтобы можно было отключать целые подсистемы: AI, спавн, обработка урона в неактивных зонах. Необязательно, чтобы каждая сущность сама в Update решала, что ей делать.
- • Используйте централизованные менеджеры и события. Вместо 30 скриптов с Update, которые по очереди проверяют «пришло ли время», заведите одну систему, которая управляет расписанием задач.
- • Вводите простые флаги «активен ли объект для логики» и избегайте тяжёлых проверок состояния на каждом кадре.
Мини‑пример. Было: около 30 компонентов с Update, каждый следит за своим таймером или условием. FPS на мобильных проседал: CPU тратил по 8–10 мс только на пользовательский код. Стало: один GameManager с единственным Update, который хранит список активных задач, а сами объекты подписываются на события при входе в нужную зону. Вкладка CPU в profiler показывала сокращение нагрузки почти вдвое, а сборки мусора стали редкими и короткими.
Оптимизация игры на Unity под мобильные устройства: особенности, о которых часто забывают
Мобилки — главный источник вопросов: «почему в Editor всё летает, а на телефоне лагает», «какие settings выставить для Android и iOS». Здесь одновременно ограничены CPU, GPU и память, плюс есть троттлинг — устройство через несколько минут может резко сбросить частоты.
Ключевые особенности:
- • Слабее процессор и видеочип, более медленная память и I/O.
- • Большой разброс по мощности: от бюджетных устройств до флагманов.
- • Чувствительность к перегреву и троттлингу: тяжёлый рендеринг в течение 10–15 минут легко превращает стабильные 40 FPS в 20.
Практические приёмы:
- • Настройте разные Quality Settings под категории устройств: low/medium/high. На слабые — минимальные тени, низкое разрешение текстур, упрощённые шейдеры, отключённый пост‑процессинг.
- • Используйте render scale: уменьшение внутреннего разрешения рендера до 0,7–0,8 от экрана даёт существенный выигрыш в GPU time с минимальной потерей качества.
- • Для Android и iOS отдавайте предпочтение IL2CPP, если важна производительность скриптов и защита кода. Mono удобно для отладки, но медленнее и тяжелее.
Управление ресурсами на мобилках критично:
- • Внедряйте Addressables или Asset Bundles, чтобы подгружать модели, текстур и аудио по мере необходимости, а не держать всё в одной сцене.
- • После смены сцены вызывайте UnloadUnusedAssets и аккуратно планируйте моменты, когда можно триггерить GC (например, в паузе или на экране загрузки).
- • Следите за пиками памяти. Если used heap близок к лимиту, устройство начнёт выгружать приложение при любом скачке.
Тестировать mobile‑билды нужно на реальных устройствах, а не только через Unity Remote. Желательно иметь хотя бы три профиля: одно слабое, одно среднее и одно топовое устройство. Так вы увидите, где упираетесь в gpu, а где — в память.
Типичный сценарий: игра идёт гладко в Editor и на мощном ПК, но на среднем Android FPS падает до 15–20, появляются вылеты. Что проверить в первую очередь?
- 1. Память: размер текстур, аудио, количество загруженных сцен. Часто culprit — огромные UI‑атласы и 4K‑текстуры для мелких объектов.
- 2. Постобработку и тени: отключите всё лишнее и замерьте FPS. Если прирост большой — делайте мобильный пресет.
- 3. Количество draw calls и overdraw: сократите сложные прозрачные элементы UI, уменьшите количество слоёв и масок.
- 4. Скрипты: проверьте, нет ли тяжёлых методов в Update и всплесков GC раз в несколько секунд.
Как выстроить процесс оптимизации: чек‑лист и приоритизация задач
Оптимизация, оставленная «на потом», почти всегда превращается в дорогой рефакторинг. Гораздо выгоднее встроить её в разработка как постоянный процесс и регулярно проверять, как изменения влияют на производительность.
Рабочий порядок действий:
- 1. Снимите базовые замеры на целевых платформах. Выберите одну или две типичных сцены (боевой эпизод, крупный город, главный экран) и замерьте FPS, CPU/GPU time, память.
- 2. Определите, где основные потери: CPU, GPU, Memory, Physics, Animation, UI. Это хорошо видно в profiler по вкладкам и высоте спайков.
- 3. Составьте список задач с оценкой эффекта и трудозатрат: «оптимизация рендера UI», «переезд на static batching», «введение пула для врагов».
- 4. Начните с «крупных камней»: draw calls, тяжёлые шейдеры, физика, аллокации в Update. Затем переходите к точечным улучшениям.
- 5. После каждого крупного изменения повторяйте профилирование. Это помогает не сдвинуть проблему из одной области в другую незаметно.
Полезный чек‑лист для команды:
- • Профилируете ли вы проект регулярно, а не только перед релизом?
- • Есть ли целевые бюджеты по draw calls, поликаунту, памяти для ключевых сцены?
- • Понимаете ли вы, какие зоны и уровни самые тяжёлые и почему?
- • Есть ли список «запрещённых приёмов» для продакшена (LINQ в Update, Instantiate без пула, огромные текстуры без необходимости)?
Хорошая практика — вести таблицу «было/стало» по FPS и Frame Time для разных настроек и устройств. Это помогает аргументировать решения (например, почему следует убрать конкретный эффект или заменить шейдер) и не терять достигнутые улучшения при последующих изменениях.
Когда выгоднее отдать оптимизацию на аутсорс и что мы можем сделать
Иногда быстрее и дешевле привлечь внешних специалистов по Unity, чем пытаться в одиночку разобраться с renderer, памятью, мобайл‑сборками и тонкостями batching. Это особенно верно, когда команда сфокусирована на контенте и геймдизайне, а игра уже близка к релизу.
Имеет смысл звать внешнюю команду, если:
- • нужен рывок по производительности перед важным обновлением или запуском;
- • внутри нет опыта глубокой оптимизации Unity: работа с profiler, настройка pipeline, уменьшение draw calls и аллокаций;
- • собственная команда перегружена задачами геймдизайна, уровней, монетизации, и у неё нет ресурса на техразбор.
Как обычно выглядит работа по оптимизации:
- • аудит проекта: анализ билдов на целевых платформах, изучение сцен, скриптов, настроек renderer и Player Settings;
- • подробный отчёт с описанием узких мест, приоритезацией и прогнозом по выигрышу;
- • пошаговое внедрение изменений, проверка на реальных устройствах и финальная доводка.
Наша команда разрабатывает и оптимизирует проекты на Unity под мобильные и десктопные платформы, помогает с архитектурой, рендером, памятью, Addressables и сборками. Помимо игр мы делаем мобильные приложения, веб‑сервисы, CRM‑системы, сайты и интернет‑магазины — везде, где важна стабильная работа и отзывчивый интерфейс.
Если вы чувствуете, что упёрлись в потолок FPS, а profiler больше пугает, чем помогает, опишите нам свой проект: платформы, тип игры, основные проблемы (фризы, вылеты, просадки FPS, сложные сцены). В ответ вы получите понятный план шагов с приоритетами и оценкой эффекта. При необходимости мы возьмём на себя и оптимизацию, и дальнейшую разработку — от одной проблемной сцены до полного цикла работы над продуктом.
