How We Architect Multiplayer Games in Unity
The architecture decisions you make in week one will determine whether your multiplayer game works at 100 players or breaks at 8. Here's how we actually think about these problems.
The Authority Question
Every multiplayer game needs to answer: who decides what's true? When two players disagree about whether a shot hit, whose client is right?
Client Authority
The client that performs an action decides the outcome. If I shoot you, my client determines if it hit. This feels responsive - no waiting for server confirmation. But it's trivially exploitable. Any client can claim any outcome. In casual co-op, this might be acceptable. In competitive games, it's not.
Server Authority
A server (dedicated or player-hosted) decides all outcomes. Clients send inputs, server sends back authoritative state. This prevents cheating at the protocol level - clients can't claim false outcomes. But it adds latency between input and result, which feels terrible in fast games without additional work.
Our Default: Server Authority with Client Prediction
For any game where fairness matters, we use server authority. To make it feel responsive, clients predict outcomes locally and reconcile when the server disagrees. This is harder to implement but essential for shooters.
The exception is co-op PvE. When players aren't competing against each other, the cheating risk is lower and simpler architectures can work. For TOGETHER: OR WE DIE, we're evaluating where we can relax authority requirements to reduce complexity.
State Synchronization
Every networked object consumes bandwidth and processing. A naive approach - sync everything every frame to everyone - doesn't scale. Here's how we handle it:
Interest Management
Players only receive updates about things relevant to them. If an enemy is across the map and can't be seen or heard, why send updates about them? We implement spatial hashing and visibility checks to filter what each client receives.
Update Frequency Tiering
Not everything needs to update at the same rate. Player positions might need 60Hz updates for smooth movement. Health bars can update when they change. Environmental state might only sync when players enter an area.
Delta Compression
Send what changed, not the full state. If a player's position moved but their health and ammo stayed the same, only send position. This requires tracking what each client knows and computing deltas, but dramatically reduces bandwidth.
Lag Compensation: The Hard Part
In a shooter, player A shoots at player B's position on their screen. But player A's screen shows where B was 50-100ms ago (network latency). By the time A's shot reaches the server, B has moved.
Three approaches, each with tradeoffs:
Favor the Shooter
The server rewinds time to when the shooter fired and checks if the shot would have hit then. This makes shooting feel responsive - if you aimed correctly, you hit. But getting shot after reaching cover feels unfair to the target.
Favor the Target
Shots are validated against current server state. Targets feel safe behind cover. But shooters have to lead their targets based on their latency, which feels wrong.
Hybrid / Capped Rewind
We typically favor the shooter but cap how far back we'll rewind (100-150ms). High-latency players still have some disadvantage, but the game feels playable. This matches what most successful shooters do.
Choosing a Netcode Solution
For most Unity projects, we don't recommend writing netcode from scratch. The options:
Photon Fusion is our recommendation for competitive games. Tick-based simulation, built-in lag compensation, good documentation. The per-CCU pricing model works for most indie budgets but can get expensive at scale.
Netcode for GameObjects is Unity's official solution. Improving rapidly, good integration with other Unity services (Relay, Lobby). We use it for projects that want to stay in the Unity ecosystem.
Custom solutions make sense when you have specific requirements that existing solutions can't meet, or when you're building something large enough that the licensing costs of commercial solutions become prohibitive.
Infrastructure Decisions
Where do servers run? How do players find each other? These aren't Unity questions, but they're part of multiplayer architecture.
For dedicated servers, we typically deploy to cloud providers (AWS GameLift, Google Cloud, or simpler solutions like DigitalOcean depending on scale). The server code needs to be headless - no rendering, minimal dependencies.
For matchmaking, we use existing services (Photon, Unity, Steam) rather than building custom. Matchmaking is a solved problem; there's no value in reinventing it.
Planning Multiplayer?
Architecture mistakes are expensive to fix later. Let's discuss your requirements early, before you've locked yourself into an approach that doesn't scale.
START A CONVERSATION