Разработка физического движка взаимодействий мобильной игры
На мобильных устройствах физика — самое дорогое удовольствие в бюджете CPU. Полноценная симуляция PhysX или Bullet в 120 Гц на Snapdragon 680 заканчивается троттлингом на третьей минуте сессии. Поэтому мобильная игровая физика — это всегда компромисс между достоверностью и производительностью, и найти правильный баланс для конкретного жанра — нетривиальная задача.
Архитектура физики под мобильный бюджет
Фиксированный timestep и его цена
Unity обновляет физику в FixedUpdate с интервалом Time.fixedDeltaTime (по умолчанию 50 Гц). На мобильном устройстве, работающем в 30 FPS, это значит два физических шага на кадр. Если игра идёт в 20 FPS из-за тепловой защиты, Unity делает 2-3 шага PhysX за один отрисованный кадр — CPU нагрузка растёт нелинейно.
Оптимальная стратегия: снижать Fixed Timestep до 0.033 (30 Гц) для мобильных билдов и компенсировать это более точными коллайдерами. Для игр, где физика не критична для геймплея (паззлы, гиперкежуал), можно опустить до 0.05 (20 Гц) и интерполировать позиции объектов визуально через Rigidbody.interpolation = RigidbodyInterpolation.Interpolate.
Когда PhysX избыточен
Для 2D-игр на Unity встроенная физика Box2D (через Rigidbody2D, Physics2D) значительно легче PhysX. Но даже Box2D на 200+ динамических объектах начинает давить на CPU. В Godot 4 аналогично — RigidBody2D + GodotPhysics2D эффективнее, чем 3D-физика для плоских игр.
Для гиперкежуал и аркадных механик часто выгоднее полностью отказаться от движкового физического движка и написать детерминированную физику вручную:
// Упрощённая физика прыжка без Rigidbody
void Update() {
if (isGrounded && Input.GetTouch(0).phase == TouchPhase.Began)
velocity.y = jumpForce;
velocity.y -= gravity * Time.deltaTime;
transform.position += velocity * Time.deltaTime;
if (transform.position.y <= groundLevel) {
transform.position = new Vector3(transform.position.x, groundLevel, 0);
velocity.y = 0;
isGrounded = true;
}
}
Детерминированная физика предсказуема, дебажится в одно касание и работает одинаково на любом устройстве. Для мультиплеерных игр это ещё и гарантия синхронизации состояний.
Взаимодействие объектов: где обычно ломается
Стекирование объектов
PhysX плохо симулирует стопки из 10+ динамических объектов на мобильном железе — начинается джиттер и объекты «проваливаются» сквозь друг друга. Решение: Rigidbody.maxDepenetrationVelocity снижать до 1-3 м/с (вместо default 10), Physics.defaultSolverIterations до 4-6 (default 6), Physics.defaultSolverVelocityIterations до 1.
Thin colliders и туннелирование
Быстрые объекты — пули, снаряды — на низком FPS за один шаг могут пролететь сквозь тонкую стену. Rigidbody.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic решает проблему, но вдвое дороже по CPU. Альтернатива: для снарядов использовать raycast вместо физического тела — Physics.SphereCast с радиусом снаряда от предыдущей позиции до текущей.
Производительность на разных устройствах
Реальная картина из профайлера (Unity Profiler + Android Profiler) в проекте с 3D-аркадой (80 физических объектов, Snapdragon 665):
| Конфигурация | Fixed Timestep | CPU на физику | FPS |
|---|---|---|---|
| Default PhysX | 50 Гц | 4.2 мс/кадр | 38 |
| Reduced iterations | 30 Гц | 2.1 мс/кадр | 56 |
| Кастомная физика | N/A | 0.6 мс/кадр | 60 |
Кастомная физика — для конкретного жанра, а не универсальное решение. Но когда игровая механика это позволяет, выигрыш очевиден.
Физика в React Native играх (flame/Godot export)
Для React Native игра пишется либо на Godot с экспортом в Web/Android, либо через движок на JavaScript (Phaser.js через WebView). Phaser использует Matter.js для 2D-физики — он легче Box2D по потреблению памяти, но уступает по точности. Matter.Runner.run() с fps: 30 на большинстве Android-бюджетников достаточно.
Для Flutter + flame: forge2d (порт Box2D на Dart) даёт полноценную 2D-физику. World.stepDt вызывается в game.update(dt), частота обновлений контролируется игровым лупом flame. Критично: forge2d работает в метрах, а flame — в пикселях. Коэффициент пересчёта (worldScale) нужно задавать один раз и придерживаться его везде.
Процесс разработки
Начинаем с профилирования целевых устройств — бюджетный Android и топовый iPhone дают разную картину. Выбираем физический движок или кастомную реализацию исходя из жанра, количества объектов и целевого FPS. Пишем физические взаимодействия, профилируем в реальных условиях (throttle mode, без зарядки), итерируем.
Типичные сроки: базовые физические взаимодействия — 1-2 недели, сложные системы (разрушаемые объекты, гибкие тела, fluid simulation) — 3-6 недель. Стоимость рассчитывается индивидуально после анализа механик.







