Интеграция Mirror для мультиплеера мобильной игры (Unity)
Mirror — open-source сетевой фреймворк для Unity, наследник устаревшего UNET. В отличие от Photon, Mirror не привязан к облаку: транспорт подключается отдельно (kcp2k, Telepathy, WebSockets, Steam), сервер можно поднять на собственной инфраструктуре. Для мобильных проектов это означает полный контроль над latency, трафиком и стоимостью хостинга — но и полную ответственность за инфраструктуру.
NetworkManager и жизненный цикл
NetworkManager — точка входа. Переопределяй только то, что нужно:
public class GameNetworkManager : NetworkManager
{
public override void OnServerAddPlayer(NetworkConnectionToClient conn)
{
var startPos = GetStartPosition();
var player = Instantiate(playerPrefab, startPos.position, startPos.rotation);
NetworkServer.AddPlayerForConnection(conn, player);
}
public override void OnClientDisconnect()
{
base.OnClientDisconnect();
// Reconnect logic для мобильного
StartCoroutine(TryReconnect());
}
}
NetworkManager.singleton — статический доступ к экземпляру. Критичная ошибка: создание нескольких NetworkManager в сцене. Mirror выдаёт warning, но не краш — и потом поведение непредсказуемо.
SyncVar, SyncList и Commands
[SyncVar] синхронизирует переменную с сервера на все клиенты автоматически при изменении:
public class PlayerHealth : NetworkBehaviour
{
[SyncVar(hook = nameof(OnHealthChanged))]
public int health = 100;
void OnHealthChanged(int oldVal, int newVal)
{
healthBar.fillAmount = newVal / 100f;
}
[Command]
public void CmdTakeDamage(int amount)
{
// Выполняется только на сервере
health = Mathf.Max(0, health - amount);
}
}
[Command] — клиент вызывает метод на сервере. [ClientRpc] — сервер вызывает на всех клиентах. [TargetRpc] — на конкретном клиенте. Это тройка, которую нужно освоить до всего остального.
SyncList<T> синхронизирует коллекцию: инвентарь, очередь баффов, список убийств. Callbacks OnChange срабатывает при любом изменении списка.
Выбор транспорта для мобильного
| Транспорт | Протокол | Мобильный |
|---|---|---|
| kcp2k | UDP-based | Рекомендуется, низкая latency |
| Telepathy | TCP | Надёжный, выше задержка |
| WebSockets (Mirror) | TCP/WS | Нужен для WebGL, не оптимален для native |
| Ignorance | UDP (ENet) | Хорошая альтернатива kcp2k |
Для мобильного нативного приложения — kcp2k или Ignorance. kcp2k встроен в Mirror из коробки с версии 50+. Настройка: NoDelay = true, Interval = 10ms, FastResend = 2.
Headless-сервер и инфраструктура
Mirror-сервер — это тот же Unity билд с флагом -batchmode -nographics. Для мобильной игры с постоянными матчами нужен выделенный сервер: один инстанс Unity headless обслуживает одну комнату (типично для action-игр) или несколько (стратегии с малым количеством игроков).
Оркестрация: Game Server Hosting (Multiplay) от Unity Gaming Services или самостоятельно на Kubernetes с автоскейлингом по нагрузке. Для небольших проектов — VPS на DigitalOcean/Hetzner с ручным управлением серверными инстансами через systemd.
Mirror + Telepathy на TCP можно держать за обычным load balancer. Mirror + kcp2k (UDP) — нужен UDP passthrough, не все load balancer'ы поддерживают.
Типичные проблемы при интеграции
NetworkIdentity не найден. Префаб не зарегистрирован в NetworkManager.spawnPrefabs. Спавн через NetworkServer.Spawn упадёт в runtime.
Authority confusion. isServer, isClient, isLocalPlayer, hasAuthority — четыре разных булевых. Код в Update() без проверки isLocalPlayer выполняется на всех клиентах, включая чужих игроков.
Disconnect на мобильном. При уходе в фон iOS закрывает сокет через ~10 секунд. Mirror не восстанавливает соединение автоматически. Реализуй OnApplicationPause → при pauseStatus = false (возврат из фона) → проверка NetworkClient.isConnected → reconnect.
Сроки
Базовая Mirror-интеграция с синхронизацией позиций, health и базовыми Command/RPC: 1-2 недели. Полноценная система с headless-сервером, матчмейкингом, reconnect и мобильной оптимизацией: 1-2 месяца. Стоимость рассчитывается индивидуально.







