Multiplayer Netcode Guide — Unity Netcode, Mirror, and Photon
In this tutorial, you'll learn about Multiplayer Netcode Guide. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
Multiplayer netcode is the layer of code that synchronizes game state across multiple machines over a network — every player's position, health, inventory, and actions must be communicated quickly and accurately while dealing with latency, packet loss, and bandwidth limits. The three most popular Unity netcode solutions are Unity Netcode for GameObjects (first-party), Mirror (open-source), and Photon PUN (cloud-hosted), each using C# for server-authoritative or client-authoritative logic.
In this tutorial, you'll learn the client-server model, implement server-authoritative movement with NetworkTransform, synchronize variables with NetworkVariable, send RPC calls for actions, handle late-joining players, and understand lag compensation through client-side prediction and server reconciliation. By the end, you'll have a multiplayer character that can move, shoot, and respawn across the network.
Why Netcode Matters
Netcode separates a multiplayer game from a single-player one — it determines whether your game feels responsive or laggy, whether cheating is trivially easy or genuinely hard, and whether 10 or 1000 players can coexist. At DodaTech, real-time synchronization techniques are used in Doda Browser for collaborative browsing sessions and shared tab state across devices.
Learning Path
flowchart LR A[Unity C# Scripting] --> B[Multiplayer Netcode
You are here] B --> C[Game Optimization] B --> D[Game Design] style B fill:#f90,color:#fff
NetworkManager Setup
Unity Netcode for GameObjects uses a NetworkManager component that handles connection, spawning, and scene management.
using Unity.Netcode;
using UnityEngine;
public class NetcodeBootstrap : MonoBehaviour
{
void OnGUI()
{
GUILayout.BeginArea(new Rect(10, 10, 300, 300));
if (!NetworkManager.Singleton.IsClient && !NetworkManager.Singleton.IsServer)
{
if (GUILayout.Button("Host")) NetworkManager.Singleton.StartHost();
if (GUILayout.Button("Client")) NetworkManager.Singleton.StartClient();
if (GUILayout.Button("Server")) NetworkManager.Singleton.StartServer();
}
GUILayout.EndArea();
}
}
Attach this to a GameObject alongside a NetworkManager component with a NetworkManagerHUD. Run one instance as Host, another as Client, and they connect automatically.
NetworkVariable and RPC
NetworkVariable automatically synchronizes values across all clients. Remote Procedure Calls (RPCs) trigger actions on specific targets.
using Unity.Netcode;
using UnityEngine;
public class PlayerNetcode : NetworkBehaviour
{
public NetworkVariable<int> Health = new NetworkVariable<int>(100);
public NetworkVariable<Vector3> SpawnPosition = new NetworkVariable<Vector3>();
public override void OnNetworkSpawn()
{
if (IsServer)
{
SpawnPosition.Value = transform.position;
Health.OnValueChanged += (oldVal, newVal) =>
{
Debug.Log($"Health changed: {oldVal} -> {newVal}");
if (newVal <= 0) Respawn();
};
}
}
[ServerRpc]
public void TakeDamageServerRpc(int damage)
{
Health.Value -= damage;
}
[ClientRpc]
public void PlayImpactEffectClientRpc(Vector3 position, Vector3 normal)
{
// Spawn VFX on all clients
Instantiate(Resources.Load("ImpactEffect"), position, Quaternion.LookRotation(normal));
}
[ServerRpc]
public void RequestRespawnServerRpc()
{
Health.Value = 100;
transform.position = SpawnPosition.Value;
}
private void Respawn()
{
Invoke(nameof(RequestRespawnServerRpc), 3f);
}
}
NetworkVariable syncs automatically. ServerRpc is called by clients but runs on the server (authoritative). ClientRpc is called by the server but runs on all clients.
Server-Authoritative Movement
For anti-cheat, movement should be server-authoritative — the client sends input, the server computes the final position, and clients interpolate the result.
using Unity.Netcode;
using UnityEngine;
public class ServerAuthoritativeMovement : NetworkBehaviour
{
public float moveSpeed = 5f;
private Vector3 pendingInput = Vector3.zero;
void Update()
{
if (!IsOwner) return;
Vector3 input = new Vector3(
Input.GetAxis("Horizontal"),
0,
Input.GetAxis("Vertical")
);
if (input != Vector3.zero)
SubmitInputServerRpc(input);
}
[ServerRpc]
private void SubmitInputServerRpc(Vector3 input)
{
Vector3 move = transform.right * input.x + transform.forward * input.z;
transform.position += move * moveSpeed * Time.fixedDeltaTime;
}
}
The client only sends input direction. The server moves the GameObject and the NetworkTransform component syncs the position back to all clients.
Practice Questions
- Why is server-authoritative movement preferred over client-authoritative for competitive games?
- What is the difference between a
ServerRpcand aClientRpcin Unity Netcode? - How does a
NetworkVariablediffer from a regular variable in a NetworkBehaviour?
Frequently Asked Questions
What is the difference between Unity Netcode for GameObjects, Mirror, and Photon?
Unity Netcode for GameObjects is first-party, free, and integrated into the Unity Editor. Mirror is a mature open-source fork of the deprecated UNET. Photon PUN is a cloud-hosted solution that handles server infrastructure for you — it is the easiest to set up but costs money at scale.
How do I handle network latency in a multiplayer game?
Use client-side prediction (move locally before server confirms), server reconciliation (correct position when server update arrives), and entity interpolation (smooth remote character positions between updates). These three techniques together hide latency in most real-time games.
What bandwidth optimization techniques reduce network usage?
Delta compression (send only changed values), interest management (only send data relevant to each client), state synchronization frequency (update at 10-20 Hz instead of 60 Hz), and bit packing (use NetworkVariable with minimum bit width).
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro