Skip to content

Latest commit

 

History

History
228 lines (177 loc) · 9.69 KB

File metadata and controls

228 lines (177 loc) · 9.69 KB
name unity-multiplayer-mobile-webgl
description Build real-time multiplayer games in Unity (C#) targeting mobile and WebGL2 browsers using Colyseus or Nakama game servers with WebSocket transport. Covers client-server architecture patterns, state synchronization (prediction, interpolation, rollback), Schema-based serialization, room/match lifecycle, matchmaking, reconnection, and cross-platform deployment. Use this skill whenever building Unity multiplayer games, integrating Colyseus or Nakama, handling WebGL2 networking constraints, optimizing multiplayer performance for mobile or browser, choosing between game server frameworks, or deploying multiplayer backends. Also trigger when the user mentions real-time networking in Unity, WebSocket client setup, authoritative servers, lobby/room systems, or network state sync — even if they don't say "multiplayer" explicitly.
license Apache-2.0
metadata
author version
adevra
1.0

Unity Multiplayer for Mobile & WebGL2

This skill guides you through building real-time multiplayer Unity games that ship to both mobile (iOS/Android) and WebGL2 browsers. The two primary server frameworks covered are Colyseus (automatic state sync, room-based) and Nakama (full game backend with auth, storage, matchmaking).

Decision tree — start here

1. Choose your server framework

Pick Colyseus when:

  • You need automatic state synchronization (schema-based binary deltas)
  • Your game is primarily room-based (lobbies, matches, sessions)
  • You want the fastest path to a working prototype
  • You'll handle auth/storage/leaderboards yourself or don't need them

Pick Nakama when:

  • You need built-in authentication (device, email, social, Steam)
  • You need persistent storage, leaderboards, tournaments, friends, groups, chat
  • You want a production-grade backend from day one
  • You're comfortable with manual state management via opcodes

→ For Colyseus details: read references/colyseus-integration.md → For Nakama details: read references/nakama-integration.md

2. Understand the WebGL2 hard constraints

WebGL builds cannot use raw TCP/UDP sockets, cannot use C# threads, and cannot host servers. All networking must go through WebSocket (or WebRTC). In production, HTTPS pages require wss:// connections.

The standard cross-platform pattern:

#if UNITY_WEBGL && !UNITY_EDITOR
    // WebSocket transport only — no UDP, no threads
    m_Driver = NetworkDriver.Create(new WebSocketNetworkInterface());
#else
    // Native platforms can use UDP for lower latency
    m_Driver = NetworkDriver.Create(new UDPNetworkInterface());
#endif

→ Full constraints and workarounds: read references/webgl2-constraints.md

3. Choose your WebSocket library

Library Cost WebGL Key strength
NativeWebSocket Free Bundled with Colyseus SDK, v2.x auto-dispatches to main thread
Best WebSockets ~€18 WSS, compression, profiler integration
unity-websocket Free MonoBehaviour API, built-in ping/RTT, zero WebGL code changes
Unity Transport (UTP) Free Official Unity package, dual UDP+WebSocket listen

Avoid WebSocketSharp — it uses System.Net.Sockets and does not work in WebGL builds.

→ Full comparison and code examples: read references/websocket-networking.md

4. Pick your architecture pattern

Genre Pattern What syncs
FPS / Action Client-side prediction + server rewind State + input
MMO / RPG Snapshot interpolation State only
RTS / Fighting Deterministic lockstep / rollback Input only
Turn-based Request-response Actions only
Casual / Social Colyseus auto-sync or Nakama relay State or messages

→ Deep dive on each pattern: read references/architecture-patterns.md

Quick-start: Colyseus + Unity

Server (TypeScript):

import { defineServer, defineRoom, Schema, type, MapSchema } from "colyseus";

class Player extends Schema {
  @type("number") x: number = 0;
  @type("number") y: number = 0;
}

class GameState extends Schema {
  @type({ map: Player }) players = new MapSchema<Player>();
}

class GameRoom {
  onCreate() { this.setState(new GameState()); }
  onJoin(client) {
    this.state.players.set(client.sessionId, new Player());
  }
  onMessage(client, type, message) {
    const player = this.state.players.get(client.sessionId);
    if (type === "move") { player.x = message.x; player.y = message.y; }
  }
  onLeave(client) { this.state.players.delete(client.sessionId); }
}

defineServer({ rooms: { game: defineRoom(GameRoom) } });

Unity Client (C#):

using Colyseus;

var client = new Client("ws://localhost:2567");
var room = await client.JoinOrCreate<GameState>("game");

// Listen to player additions
var callbacks = Callbacks.Get(room);
callbacks.OnAdd(state => state.players, (sessionId, player) => {
    SpawnPlayer(sessionId, player);
    callbacks.Listen(player, p => p.x, (val, prev) => MovePlayer(sessionId));
});

// Send input
await room.Send("move", new { x = transform.position.x, y = transform.position.z });

→ Full Colyseus integration guide: references/colyseus-integration.md → Server template: assets/colyseus-room-template.ts → Client template: assets/unity-colyseus-manager.cs

Quick-start: Nakama + Unity

Server (TypeScript runtime):

const matchInit: nkruntime.MatchInitFunction = (ctx, logger, nk, params) => {
  return { state: { players: {}, tick: 0 }, tickRate: 20, label: "" };
};

const matchLoop: nkruntime.MatchLoopFunction = (ctx, logger, nk, dispatcher, tick, state, messages) => {
  for (const msg of messages) {
    const data = JSON.parse(new TextDecoder().decode(msg.data));
    state.players[msg.sender.userId] = { x: data.x, y: data.y };
  }
  dispatcher.broadcastMessage(1, JSON.stringify(state.players));
  return { state };
};

Unity Client (C#):

using Nakama;

var client = new Client("http", "127.0.0.1", 7350, "defaultkey",
    UnityWebRequestAdapter.Instance);
var session = await client.AuthenticateDeviceAsync(SystemInfo.deviceUniqueIdentifier);

var socket = client.NewSocket(useMainThread: true);
await socket.ConnectAsync(session);
var match = await socket.CreateMatchAsync();

// Send state
await socket.SendMatchStateAsync(match.Id, 0,
    System.Text.Encoding.UTF8.GetBytes(JsonUtility.ToJson(position)));

// Receive state
socket.ReceivedMatchState += (matchState) => {
    var data = System.Text.Encoding.UTF8.GetString(matchState.State);
    ApplyState(matchState.UserPresence.UserId, JsonUtility.FromJson<Position>(data));
};

→ Full Nakama integration guide: references/nakama-integration.md → Server template: assets/nakama-match-handler-template.ts → Client template: assets/unity-nakama-manager.cs

Performance checklist

When optimizing for mobile + WebGL2, follow this priority order:

  1. Reduce what you send — Delta compression, quantization, smallest-three quaternion encoding. Only sync changed data. Use NetworkVariable for persistent state, RPCs for events.
  2. Reduce how often you send — 20-30 ticks/second is optimal. Use interpolation to smooth gaps. Colyseus defaults to 20Hz patchRate.
  3. Reduce who receives — Interest management / area-of-interest filtering. Far entities get lower update rates or are culled entirely.
  4. Use binary serialization — MessagePack for C# is the best general choice (10-100x faster than JSON, compact). Avoid JSON in production hot paths.
  5. Pool everything — Object pooling for network messages, byte arrays, collections. WebGL GC runs only once per frame; mid-frame allocations risk OOM.
  6. Batch on mobile — Minimize cellular radio activations. Front-load transfers, avoid polling patterns. Offer 30 FPS option.

→ Detailed optimization guide: references/performance-optimization.md

Deployment overview

Both Colyseus and Nakama deploy via Docker. For production:

  • Nginx reverse proxy with WebSocket upgrade headers
  • SSL/TLS required for WebGL clients (wss://)
  • Colyseus scaling: Redis for presence + driver, PM2 in fork mode
  • Nakama scaling: CockroachDB cluster, multi-node Nakama with gossip discovery

→ Full deployment guides: references/deployment-guides.md

Reference files index

Read these as needed — don't load all at once:

File When to read
references/architecture-patterns.md Choosing network topology, state sync strategy, or ECS networking
references/colyseus-integration.md Setting up Colyseus server, Unity SDK, rooms, schema, matchmaking
references/nakama-integration.md Setting up Nakama server, Unity SDK, auth, multiplayer, storage
references/websocket-networking.md Choosing/configuring WebSocket library, reconnection logic
references/webgl2-constraints.md Understanding WebGL2 limitations, conditional compilation patterns
references/performance-optimization.md Network bandwidth, serialization, GC, mobile battery, object pooling
references/deployment-guides.md Docker, Nginx, SSL, Redis, scaling, managed hosting options

Asset templates index

File Purpose
assets/colyseus-room-template.ts Production-ready Colyseus room with auth, reconnection, clock sync
assets/nakama-match-handler-template.ts Authoritative Nakama match with tick loop, presence tracking
assets/unity-colyseus-manager.cs Unity singleton managing Colyseus connection, room join, state callbacks
assets/unity-nakama-manager.cs Unity singleton managing Nakama client, socket, auth, match lifecycle
assets/unity-websocket-client.cs Standalone WebSocket client with exponential backoff reconnection