Код мобильной игры: архитектура, движки и примеры реализации

Играть в текстовые ролевые игры бесплатно

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

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

16,6 мс
бюджет кадра при 60 FPS — всё, что дольше, видно глазом
4 слоя
минимум: ядро, логика, представление, платформа
≈ 60%
устройств на рынке — средний и бюджетный сегмент
1 поток
главный поток мобильного приложения — узкое место кода

Из чего состоит код мобильной игры

Мобильная игра отличается от десктопной не только управлением. У неё жёстче ограничения по теплу, памяти и заряду, а пользователь может свернуть приложение в любой момент — в том числе посреди боя. Поэтому код мобильной игры строится так, чтобы логика была отделена от отрисовки, а любое состояние можно было сохранить и восстановить за доли секунды.

Практически любой проект — от гиперказуальной головоломки до 3D-шутера — раскладывается на одни и те же уровни ответственности.

Слои кода мобильной игры
Платформенная обвязка
жизненный цикл приложения, разрешения, уведомления, покупки, рекламные и аналитические SDK, доступ к файлам
Представление
рендер, анимации, частицы, интерфейс, звук — всё, что видит и слышит игрок
Игровая логика
правила мира, состояния сущностей, экономика, прогрессия, искусственный интеллект, сохранения
Ядро
игровой цикл, планировщик задач, менеджер ресурсов, математика, система событий
Физика и ввод
коллизии, перемещение тел, обработка касаний, гироскопа, геймпада — с фиксированной частотой
Стрелка зависимости направлена вниз: ядро ничего не знает о рендере, логика — о кнопках на экране. Как только представление начинает напрямую менять состояние мира, проект теряет тестируемость и получает цепочку невоспроизводимых багов.

Игровой цикл: сердце любой мобильной игры

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

// Упрощённый цикл: Unity, C#
void Update()
{
    float dt = Time.deltaTime;   // сколько прошло с прошлого кадра

    input.Tick(dt);              // 1. ввод и UI
    world.Step(dt);              // 2. логика и анимация
    renderer.Draw(world);        // 3. отрисовка
}

Проблема такой схемы — переменный шаг. На слабом устройстве кадр может занять 40 мс вместо 16, и физика «прыгнет» вперёд, ломая коллизии. Надёжнее разделить два ритма: логику считать фиксированными шагами, а отрисовку — как получится, с интерполяцией между состояниями.

accumulator += frameTime;

while (accumulator >= FIXED_DT) {   // FIXED_DT = 1/60 с
    physics.Step(FIXED_DT);          // детерминированный шаг
    accumulator -= FIXED_DT;
}

alpha = accumulator / FIXED_DT;      // доля «недоигранного» шага
renderer.Interpolate(alpha);         // плавная картинка без рывков

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

Движки и языки: чем пишут код мобильных игр

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

Движок Язык Сильная сторона Ограничение
Unity C# Огромная экосистема, готовые плагины, быстрый прототип Сборка мусора и лишний вес пустого проекта
Unreal Engine C++, Blueprints Качественная графика из коробки, сильный инструментарий Требователен к железу и к размеру дистрибутива
Godot GDScript, C# Лёгкий, открытый, простой в изучении Меньше готовых интеграций и специалистов на рынке
Cocos2d-x C++, Lua, JS Минимальный оверхед для 2D и гиперказуальных жанров Инструменты и редактор скромнее, чем у лидеров
Собственный движок C++, Rust, Swift/Kotlin, C# Полный контроль над производительностью и размером Дорого: месяцы на то, что уже сделано за других

Отдельная ветка — нативная разработка на Swift и Kotlin с графикой через Metal, Vulkan или OpenGL ES. Она оправдана, когда нужен предельно маленький вес, мгновенный запуск и нестандартный рендер: например, аркада с процедурной графикой или утилита-анимация.

Как организовать код мобильной игры внутри движка

Движок даёт инструменты, но не диктует структуру. Три подхода встречаются чаще всего, и у каждого своя зона комфорта.

Подход 1
Наследование объектов

Классы вроде Enemy наследуются от Character, а тот — от Entity.

Плюс: понятно с первого дня.
Минус: глубокая иерархия и дублирование кода.
Подход 2
Компоненты

Сущность — пустой контейнер, поведение добавляется блоками: здоровье, движение, стрельба.

Плюс: гибкая сборка, приятная работа в редакторе.
Минус: порядок обновления компонентов легко потерять.
Подход 3
Сущность — компонент — система

Данные лежат в плотных массивах, логика — в отдельных системах, которые эти массивы обходят.

Плюс: максимум на тысячах объектов, дружелюбен к кэшу.
Минус: выше порог входа, сложнее отлаживать.

На практике чистых схем почти не бывает. Разумный компромисс — держать в компонентах то, что различается между объектами, а тяжёлые вычисления выносить в системы с линейным обходом массивов.

Пример: компонент и система движения
struct Position { float x, y; };
struct Velocity { float x, y; };

// Данные — рядом друг с другом, обход — линейный
void MovementSystem(float dt)
{
    for (int i = 0; i < activeCount; ++i) {
        positions[i].x += velocities[i].x * dt;
        positions[i].y += velocities[i].y * dt;
    }
}

Такой блок легко заменить на SIMD-версию или перенести в отдельный поток — именно поэтому крупные проекты с тысячами объектов переходят на систему из компонентов и систем.

Пример: пул объектов вместо постоянного создания
// Плохо: вызов каждые 100 мс порождает мусор в куче
Instantiate(bulletPrefab, muzzle.position, rotation);

// Хорошо: объект берётся из заранее созданного пула
Bullet b = pool.Rent();
b.Reset(muzzle.position, rotation);

// ... и возвращается обратно
pool.Return(b);

Сборка мусора — главный источник микрорывков на Android. Пул объектов убирает регулярные аллокации и делает время кадра ровнее.

Бюджет кадра: куда уходит время на среднем смартфоне

При 60 кадрах в секунду на всё про всё отводится около 16,6 миллисекунды. Распределение ниже — усреднённая картина для 2D-проекта среднего масштаба.

Рендер — 5,5 мс
Логика — 4,0 мс
Физика — 2,5 мс
ОС и платформа — 2,6 мс
Интерфейс — 1,2 мс
Аудио — 0,8 мс
Запас отсутствует: стоит логике занять 10 мс вместо 4 — и кадры начнут выпадать именно в самые напряжённые моменты, когда на экране больше всего объектов.
Ресурс Комфортный уровень Причина деградации
Память до 1,5 ГБ на среднем устройстве Атласы текстур без сжатия, утечки подписок на события
Аллокации не более 1–2 КБ на кадр Создание объектов в цикле обновления, строковые операции
Вызовы отрисовки до 150–200 на кадр Отдельный материал на каждый объект вместо общего атласа
Накопитель рост приложения до 150–200 МБ Аудио в несжатом виде, дубликаты ресурсов в сборке

Размер приложения как часть кода

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

Unreal Engine — ≈ 60 МБ
Unity — ≈ 20 МБ
Godot — ≈ 15 МБ
Нативная сборка на C++ — ≈ 8 МБ
Значения ориентировочные и относятся к пустому проекту без игровых ассетов. Реальный вес определяется контентом: текстурами, звуком и видео.

iOS и Android: где код ведёт себя по-разному

Аспект iOS Android
Управление памятью Предсказуемое, единые правила для всех устройств Сильно зависит от производителя и оболочки
Графический интерфейс Metal, единый набор расширений Vulkan и OpenGL ES, десятки разных чипсетов
Потоки Стабильное поведение, строгие требования к главному потоку для UI Агрессивные энергосберегающие ограничения на ядрах
Фон и сворачивание Быстрая заморозка процесса Процесс может быть завершён без предупреждения

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

Что проверяют перед выпуском

Логика отделена от рендера
Правила игры можно прогнать в тесте без запуска графики — это ускоряет поиск ошибок в разы.
Нет аллокаций в горячем цикле
Профилировщик показывает ровную линию вместо пиков сборки мусора каждые несколько секунд.
Ассеты упакованы в атласы
Меньше вызовов отрисовки и меньше переключений состояния графического конвейера.
Сохранение устойчиво к падению
Запись ведётся атомарно: сначала во временный файл, затем замена основного.
Протестировано на слабом устройстве
Флагман не показывает проблем: реальную нагрузку видно на аппарате трёх-четырёхлетней давности.
Сторонние SDK изолированы
Реклама, аналитика и покупки спрятаны за собственным интерфейсом — их можно заменить без переписывания игры.
Типичная ошибка Как проявляется Как исправляют
Логика внутри обработчиков интерфейса Правила расходятся между экранами, баги воспроизводятся редко Единая модель состояния, интерфейс только читает её
Обработка касаний в каждом кадре Задержка управления, «плавающие» нажатия Событийная обработка, опрос только активных жестов
Физика на переменном шаге Объекты проскакивают препятствия на просадках кадров Фиксированный шаг с накоплением времени
Строковые ключи в горячем коде Лишние аллокации, рост времени кадра Числовые идентификаторы, кэширование результатов
Коротко о главном

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

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

История развивается так, как решаете вы ✍️

Это не книга с готовым финалом и не игра с фиксированными ответами. В нашем телеграм-боте вы сами ведёте диалог с ИИ-персонажем: задаёте тон, принимаете решения, меняете ход событий. Захотели романтики — будет романтика. Захотели интриги — сюжет повернёт туда. Каждая партия — уникальная.

Попробовать прямо сейчас 👉 https://clck.ru/3Ta8kQ

Играть в текстовые ролевые игры бесплатно