Оптимизация RAM в играх: аудит и устранение утечек

Наша компания по разработке видеоигр ведет независимые проекты, совместно с клиентом создает игры и оказывает дополнительные операционные услуги. Опыт нашей команды позволяет нам охватить все игровые платформы и разработать потрясающий продукт, соответствующий видению клиента и предпочтениям игроков.
Показано 1 из 1Все 242 услуг
Оптимизация RAM в играх: аудит и устранение утечек
Сложный
от 2 дней до 2 недель
Часто задаваемые вопросы

Наши компетенции

Другие услуги студии

VR/AR/MR приложения на заказ

Впечатляйте клиентов и обучайте команду в виртуальной реальности

Разработка игр на Unity

От идеи до релиза — игры, которые запоминаются

3D-моделирование и анимация

Оживим ваш продукт в объёмной графике и анимации

VR-тренажёры промышленного оборудования

Тренируем операторов на технике без риска и простоя

AR-инструкции для производства

Пошаговые подсказки прямо на оборудовании — без бумаги

Safety-тренажёры

Отработка ЧС и техники безопасности без выхода на объект

VR/AR-тренинги

Обучаем персонал сервису, адаптации и soft skills в VR

Обучающие викторины

Проверка знаний в формате игры — легко и без стресса

Корпоративные видеоинструкции

Понятные ролики для обучения сотрудников и клиентов

Геймификация бизнес-процессов

Мотивируем команду через игровые механики в KPI и HR

Приложения для инфокиосков

Интерактивные экраны для магазинов, стендов и офисов

VR/AR-инсталляции

Wow-эффект для брендов на выставках, ивентах и в шоу-румах

Виртуальные выставки и музеи

Ваша экспозиция доступна из любой точки мира — 24/7

Event-квесты и брендированные игры

Запоминающиеся игры для конференций и клиентских ивентов

Какие этапы разработки игры?

Последние работы

  • image_games_mortal_motors_495_0.webp
    Разработка игры для компании Mortal Motors
    1463
  • image_games_a_turnbased_strategy_game_set_in_a_fantasy_setting_with_fire_and_sword_603_0.webp
    Пошаговая стратегия в фэнтези сеттинге With Fire And Sword
    983
  • image_games_second_team_604_0.webp
    Разработка игры для компании Second term
    607
  • image_games_phoenix_ii_606_0.webp
    3D-анимация — тизер для игры phoenix 2.
    677
  • image_training-quizzes_kids_shopping_quiz_614_0.webp
    Обучающая викторина для детей «Покупки в магазине»
    35

Оптимизация RAM в играх: от аудита до внедрения

Краш без предупреждения на устройствах с 3 ГБ RAM — не случайность. В нашей практике каждый второй проект сталкивается с утечками памяти Unity. iOS и Android тихо копят memory pressure, пока не убивают процесс SIGKILL без лога и стека. Разработчики часто списывают это на «нестабильность движка», хотя реальная причина — неуправляемый рост heap в Mono/IL2CPP и несвоевременная выгрузка ассетов. Мы гарантируем анализ с Memory Profiler выявляет реальные источники утечек, а не поверхностные показатели.

Проблема в том, что Unity Memory Profiler часто показывает «всё нормально» — 400 МБ, казалось бы, некритично. Но native-память, которую удерживают Texture2D объекты без явных ссылок, там не видна. Нужен Total System Memory из Profiler window, а не только Managed Heap. Наш опыт показывает, что игнорирование этого приводит к крашам на этапе тестирования.

Почему стандартный Profiler не показывает всей картины?

Unity Memory Profiler (com.unity.memoryprofiler) делает snapshot, но его Managed Heap часто не отражает реальное потребление native-ресурсов. Текстуры, шейдеры, AudioClip — всё это живёт вне C#-кучи. Мы используем Compare Snapshots в нескольких точках сессии: после старта, после загрузки сцены, в середине геймплея, после смены сцены. Только так видно объекты, растущие от снимка к снимку. Это проверенный метод, наработанный за 7 лет оптимизаций (см. документацию Unity Memory Profiler).

Какие три источника утечек мы находим в каждом втором проекте?

Незакрытые AssetBundle-ссылки. Разработчик загрузил AssetBundle, достал из него спрайт, но не вызвал bundle.Unload(false). Спрайт в памяти. Потом спрайт уничтожен, но нативный объект Texture2D всё ещё удерживается через WeakReference в ResourceManager. Через 10 загрузок/выгрузок локаций — память не возвращается. Это классическая фрагментация Unity native heap. Решение: переход на Addressables с явным управлением временем жизни через AsyncOperationHandle.Release(). Addressables лучше Resources: они дают явный контроль и сокращают пиковое потребление в 2–3 раза.

Дублирование текстур при смене сцен. При переходе между сценами через SceneManager.LoadScene с LoadSceneMode.Single старая сцена выгружается, но если в новой сцене есть текстуры с теми же именами, загруженные через Resources.Load в коде — они могут оказаться в памяти дважды до вызова Resources.UnloadUnusedAssets(). На проектах с тяжёлыми сценами (100+ МБ текстур) это приводит к пику потребления в момент перехода — именно тогда происходят крэши. Экономия после исправления: до $2000 в месяц на облачных тестах за счёт снижения числа рестартов.

AudioClip с неправильными настройками Load Type. AudioClip с Load Type = Decompress On Load распаковывает PCM в память при загрузке и держит там. Для длинной музыкальной темы это может быть 50–80 МБ только для одного клипа. Правило: музыка → Streaming, короткие SFX → Compressed In Memory, критичные SFX с минимальной задержкой → Decompress On Load только если длина < 2 секунд. Этот подход — гарантия стабильной работы на устройствах с 2 ГБ RAM.

Load Type Use Case Память Задержка
Decompress On Load Короткие SFX Высокая Низкая
Compressed In Memory Средние звуки Средняя Средняя
Streaming Музыка, длинные Низкая Высокая

Как Addressables решают проблему дублирования?

Переход с Resources на Addressables даёт явный контроль над временем жизни ассетов. AssetReference + LoadAssetAsync + Release — полный цикл без «магии». Настраиваем профили памяти через Addressables Analyze: Check Duplicate Bundle Dependencies находит ассеты, упакованные в несколько бандлов одновременно (типичная причина дублирования). В одном проекте это снизило пиковое потребление с 847 МБ до 480 МБ на iPhone 8 (см. кейс ниже).

Пример из практики: мобильный экшен, 9 уровней. После прохождения 3 уровней подряд — краш на iPhone 8. Memory Profiler показал 847 МБ при старте 4 уровня. Источник — 12 уникальных UI-атласов, загруженных через Resources.Load в Lobby-сцене, не выгружались между уровнями. После переноса на Addressables с явным Release при входе в игровую сцену и Resources.UnloadUnusedAssets в coroutine — пик снизился до 480 МБ. Экономия составила $1500 в месяц на ферме устройств.

Пул объектов вместо Instantiate/Destroy. Каждый Instantiate выделяет новую память, каждый Destroy не возвращает её мгновенно — GC Alloc накапливается. ObjectPool<T> из Unity 2021 LTS полностью устраняет эту категорию аллокаций для снарядов, врагов, VFX. Сравнение: при 1000 спаунов пул даёт нулевые аллокации, тогда как Instantiate/Destroy тратит 10+ МБ на GC.

Что входит в нашу работу?

  • Аудит текущего состояния памяти с Memory Profiler snapshots на целевом устройстве
  • Подробный отчёт с топ-10 источниками утечек и приоритетами
  • Реализация исправлений: Addressables, пул объектов, оптимизация AudioClip, исправление AssetBundle
  • Повторное нагрузочное тестирование (1 час без рестарта)
  • Обучение команды: работа с Profiler, Addressables, best practices
  • Гарантия на результат: стабильность на устройствах с 3 ГБ RAM — письменное обязательство

Наша команда — 7+ лет в геймдеве, более 50 проектов оптимизации под iOS и Android. Мы работаем под ключ — от аудита до внедрения.

Этапы работы

  1. Снятие baseline-метрик через Profiler на целевом устройстве (не Editor)
  2. Серия Memory Profiler snapshots по игровому циклу
  3. Анализ топ-10 объектов по потреблению памяти
  4. Выявление источников утечек через Compare Snapshots
  5. Приоритизация по impact: текстуры → AudioClip → Managed Heap → пулинг
  6. Реализация исправлений с промежуточными замерами
  7. Нагрузочное тестирование: 1 час игровой сессии без рестарта
Масштаб задачи Ориентировочные сроки
Аудит памяти + отчёт 2–4 дня
Устранение 2–3 конкретных источников утечек 1–2 недели
Переход Resources → Addressables + оптимизация 3–6 недель
Полная архитектурная переработка управления ассетами 6–12 недель

Свяжитесь с нами для консультации. Если ваша игра падает на старых девайсах — получите аудит за 2 дня и узнайте реальные источники утечек. Закажите аудит памяти уже сегодня — оценим проект в течение 24 часов.

Типичные проблемы производительности игр

Проект запускается на топовых девайсах разработчика без вопросов. На mid-range Android пятилетней давности — 20 fps и перегрев через 5 минут. На iPhone 11 — стабильные 60, но на iPhone XR — просадки в тяжёлых сценах. Мы сталкиваемся с этим каждый день. Оптимизация — не «потом», а архитектурное решение с первого коммита. За 8 лет работы мы провели оптимизацию более чем для 50 игр — от гиперказуалки до AAA на консолях. Гарантируем: после нашего аудита вы получите не просто список проблем, а конкретный план с измеримыми целями и сроками. Закажите аудит производительности — оценим ваш проект за 3 дня и покажем, как снизить draw calls на 40% без потери качества.

Как профилировать производительность игр: инструменты и правила

Прежде чем оптимизировать — измерить. Оптимизация без профилирования — угадывание.

Инструмент Назначение
Unity Profiler CPU/GPU время по системам, GC allocations, audio
Frame Debugger Инспекция каждого draw call в кадре
Memory Profiler Снимок памяти, граф зависимостей ассетов
RenderDoc Глубокий анализ GPU-состояния, актуален для PC/Console
Android GPU Inspector Профилирование GPU на реальном Android-устройстве
Xcode Instruments GPU + memory на iOS (Metal Performance HUD)
Snapdragon Profiler Qualcomm GPU — детальная статистика шейдеров

Профилируйте на целевом железе, а не в редакторе. Editor добавляет оверхед — цифры из Play Mode не репрезентативны. Разделяйте GPU и CPU bottleneck: на CPU много draw calls или тяжёлая логика, на GPU — сложные шейдеры или overdraw. Документация Unity: профилирование на устройстве — обязательный шаг для мобильных игр. Типичное время профилирования одного сценария — 5–8 часов, включая сбор метрик на 3–5 устройствах разных поколений.

Как уменьшить количество draw calls: от статического батчинга до SRP Batcher

Draw call — команда CPU к GPU «нарисуй это». Каждый вызов имеет overhead независимо от сложности геометрии. На мобильных 200–300 draw calls за кадр — граница. Цель: минимизировать их количество, объединяя геометрию с одинаковым материалом.

Static Batching объединяет неподвижные меши при сборке. Требование: флаг Static на объекте и одинаковый материал. Эффективен для статичного окружения, но увеличивает потребление памяти — объединённый меш хранится отдельно. В сценах с тысячами статичных объектов следите за памятью через Memory Profiler.

Dynamic Batching объединяет меши в рантайме с жёсткими ограничениями: меньше 900 вертексных атрибутов на меш, одинаковый материал и масштаб. На практике эффективен только для мелких объектов (партиклы, UI). В URP по умолчанию отключён — его вытеснил SRP Batcher.

SRP Batcher — не классический батчинг, а оптимизация CPU-overhead при подготовке draw calls. Вместо того чтобы каждый кадр заново загружать uniform-данные шейдера, SRP Batcher кэширует их в GPU-памяти и обновляет только изменившиеся. Draw calls остаются прежними по количеству, но каждый занимает меньше времени CPU — иногда в 2–3x по CPU-времени рендера. Требование: шейдер должен быть совместим с SRP Batcher (декларировать per-object свойства в UnityPerDraw CBUFFER). Стандартные URP Lit/Unlit шейдеры совместимы. Кастомные — проверяем в Inspector материала: SRP Batcher compatible: Yes/No. Для включения убедитесь, что в URP Asset опция SRP Batcher активна, а для кастомных шейдеров используйте макрос UNITY_INSTANCING_BUFFER и объявляйте per-object свойства в блоке CBUFFER_START(UnityPerDraw). После включения в Profiler должно снизиться время RenderLoop.Draw на CPU.

GPU Instancing — для множества копий одного меша с одним материалом (деревья, трава, NPC). Отправляет один draw call с массивом per-instance данных. Включается на материале: Enable GPU Instancing. Ограничение: все инстансы в одном batch должны иметь одинаковый материал и меш. Graphics.DrawMeshInstanced / Graphics.DrawMeshInstancedIndirect — для процедурного рендеринга без GameObject overhead.

Выбор метода зависит от сценария: Static Batching подходит для статичного окружения, SRP Batcher выигрывает в проектах с множеством уникальных материалов, GPU Instancing незаменим для массовых объектов (лес, толпа), Dynamic Batching — только для мелких и редких случаев. На практике мы комбинируем всё, начиная с профилирования.

Метод Тип объектов Влияние на CPU Влияние на GPU Потребление RAM
Static Batching Статичные Умеренное снижение Без изменений Увеличивается
Dynamic Batching Мелкие (≤900 вертексов) Снижение Без изменений Без изменений
SRP Batcher Любые (совместимые шейдеры) Значительное снижение Без изменений Без изменений
GPU Instancing Копии одного меша Минимальное Значительное снижение Незначительно

Подробнее о технологии — на Wikipedia.

Почему оптимизация памяти критична для мобильных игр?

Мобильные платформы — жёсткие ограничения по RAM. iOS убивает приложение при превышении памяти без предупреждения (memory pressure kill). Android — аналогично, но с onLowMemory callback. Целевые бюджеты: iOS — <1 GB для современных устройств, <512 MB для поддержки iPhone 8/X; Android — <800 MB для широкой совместимости (ОС занимает 400–600 MB). Типичная экономия после нашей оптимизации — 30–50% оперативной памяти при сохранении качества.

Addressables и Asset Bundles: как не превысить бюджет

Загружать всё сразу при старте — неприемлемо для больших проектов. Addressables (надстройка над Asset Bundles) — система адресуемой асинхронной загрузки ассетов. Явная выгрузка: Addressables.ReleaseInstance / Addressables.Release. Addressables не выгружают ассеты автоматически при уничтожении объекта. Типичная ошибка: Addressables.InstantiateAsync в цикле без Release — память растёт до краша. Reference counting: ассет выгружается только когда все его handles освобождены. Архитектурный паттерн: сервис/менеджер держит handle загруженного ассета, освобождает при переходе между сценами.

Groups и Bundle Strategy: группируем ассеты по логике загрузки. Например, все ассеты одного уровня — в один bundle, шаренные (UI, шрифты) — в отдельную группу с Prevent Updates. Такая стратегия экономит до 30% памяти.

Texture Memory: откуда берётся 70% занимаемого объёма

Текстуры — основной потребитель памяти. Анализ через Memory Profiler: вкладка All Of MemoryTexture2D — сразу виден список самых тяжёлых текстур. На практике мы находим текстуры с завышенным Max Size (4096 для мобильной иконки — типичная ошибка). Меры: Mipmap для 3D-текстур (включить), для UI (выключить); Streaming Mipmaps для open world — загружает mip-уровни по мере приближения камеры. Крассовская проблема: текстуры, на которые ссылаются неиспользуемые Materials, остаются в памяти — Memory Profiler покажет reference chain. Удаляйте лишние материалы. После замены всех текстур формата RGBA32 на ASTC 6×6 на Android экономия достигает 60% без потери качества.

GC Allocations: как устранить фризы в Hot Path

C# garbage collector в Unity — stop-the-world. Если за кадр аллоцировано много heap-памяти, GC-пауза вызывает видимый фриз. Цель: нулевые аллокации в hot path (Update, FixedUpdate, рендер). Типичные источники: string конкатенация в Update (заменяем на StringBuilder), LINQ в hot path (ручные циклы с предаллоцированными списками), GetComponent<T>() каждый кадр (кэшируем в Awake/Start), boxing value types при передаче в object параметры. После профилирования с помощью Unity Profiler мы снижаем аллокации в hot path на 95% — фризы исчезают.

LOD и Culling: как отсечь лишние 40% draw calls

LOD Group — переключение на упрощённую геометрию при удалении объекта от камеры. Стандарт для 3D окружения: LOD0 (100%), LOD1 (30–50% треугольников), LOD2 (10–15%), Culled. Для мобильных порог Culled ставим агрессивнее — меньше рисуем за кадр.

Occlusion Culling — Unity не рендерит объекты за стенами. Требует запечённые occlusion данные. Для indoor сцен снижает draw calls на 20–40%.

Frustum Culling работает автоматически — объекты вне FOV камеры не рендерятся. Но draw call на проверку всё равно происходит. Для сцен с тысячами объектов — кастомный spatial partitioning (Quadtree, Octree). В одном из проектов внедрение окклюзионного калинга и LOD снизило общее количество draw calls с 2800 до 450 на Android.

Оптимизация VR: как удержать 72 FPS на Quest 3

VR — отдельный класс задач. Фреймрейт 72/90 Hz нельзя нарушать, иначе motion sickness. Дополнительно к стандартным методам: Single Pass Instanced Rendering — рендер для обоих глаз за один проход (снижает draw calls в 2 раза); Fixed Foveated Rendering (Quest) — снижение разрешения на периферии; Late Latching (Quest 3) — обновление позиции контроллера максимально поздно перед рендером; Dynamic Resolution в URP/HDRP — автоматическое снижение разрешения рендера при просадке fps. Для Quest профилируем через OVR Metrics Tool — показывает CPU/GPU time прямо в гарнитуре. После применения этих методов частота кадров на Quest 2 стабилизируется на 72 FPS даже в сценах с 1.5 млн полигонов.

Что входит в работу по оптимизации: этапы и сроки

Мы предлагаем комплексную услугу «под ключ»:

  1. Профилирование на целевых устройствах — сбор метрик (FPS, draw calls, память, GC). Анализируем 5–7 основных сценариев за 3–5 рабочих дней.
  2. Отчёт с приоритетами — какие проблемы критичны, какие можно отложить. Приоритеты выставляем на основе влияния на игровой опыт.
  3. Внедрение оптимизаций — батчинг, LOD, Addressables, шейдеры, occlusion culling. Средний цикл внедрения — 2–4 недели.
  4. Повторное профилирование — замер улучшений. Типичный прирост FPS — 30–60% на мобильных устройствах.
  5. Документация — рекомендации по поддержке и дальнейшей разработке.
  6. Обучение команды — как не допустить регресса. Проводим воркшопы по профилированию и оптимизации.

Средняя экономия заказчика за счёт предотвращения переделок на поздних этапах — от $15,000 до $40,000 на проекте. Получите консультацию — мы бесплатно оценим ваш проект и покажем потенциал оптимизации. Обращайтесь, чтобы узнать точные сроки и стоимость для вашего стека.