# Unity Multiplayer Technologies

This comprehensive guide covers the main strategies, tools, and best practices for developing multiplayer games in Unity, including solutions like AWS GameLift, Photon PUN, and advanced techniques to optimize the online experience.

![Unity Multiplayer Overview](https://images.unsplash.com/photo-1542751371-adc38448a05e?crop=entropy\&cs=srgb\&fm=jpg\&ixid=M3wxOTcwMjR8MHwxfHNlYXJjaHw2fHxnYW1lc3xlbnwwfHx8fDE3NTgwMzUyNzd8MA\&ixlib=rb-4.1.0\&q=85\&w=1200)

## 📋 Table of Contents

1. [Multiplayer Architectures](#multiplayer-architectures)
2. [Unity Netcode for GameObjects](#unity-netcode-for-gameobjects)
3. [Photon PUN](#photon-pun)
4. [AWS GameLift](#aws-gamelift)
5. [Mirror Networking](#mirror-networking)
6. [Anti-Lag Tips](#anti-lag-tips)
7. [Common Errors](#common-errors)
8. [Best Practices](#best-practices)

***

## 🏗️ Multiplayer Architectures

### Client-Server vs Peer-to-Peer

{% @mermaid/diagram content="graph TB
subgraph "Client-Server"
C1\[Client 1] --> S\[Authoritative Server]
C2\[Client 2] --> S
C3\[Client 3] --> S
S --> C1
S --> C2
S --> C3
end

```
subgraph "Peer-to-Peer"
    P1[Client 1] <--> P2[Client 2]
    P2 <--> P3[Client 3]
    P3 <--> P1
end" %}
```

### Hybrid Architecture (Recommended)

{% @mermaid/diagram content="graph TD
subgraph "Hybrid Architecture"
LB\[Load Balancer] --> DS1\[Dedicated Server 1]
LB --> DS2\[Dedicated Server 2]
LB --> DS3\[Dedicated Server 3]

```
    DS1 --> DB[(Database)]
    DS2 --> DB
    DS3 --> DB
    
    C1[Client 1] --> LB
    C2[Client 2] --> LB
    C3[Client 3] --> LB
    C4[Client 4] --> LB
end" %}
```

**Advantages of Hybrid Architecture:**

* ✅ Better security against cheats
* ✅ Horizontal scalability
* ✅ State consistency
* ✅ Lower latency than pure P2P

***

## 🎮 Unity Netcode for GameObjects

<figure><img src="https://3504844606-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkjovAfZK07rWR0t6DR9s%2Fuploads%2Fc4Mm4kLtoGcUFcSG8gPI%2Fimage.png?alt=media&#x26;token=b14a420c-d51c-4e2a-ae3b-47e82797ec91" alt=""><figcaption></figcaption></figure>

{% embed url="<https://github.com/Unity-Technologies/com.unity.netcode.gameobjects>" %}

### Basic Configuration

```csharp
using Unity.Netcode;

public class PlayerController : NetworkBehaviour
{
    private NetworkVariable<float> networkPositionX = new NetworkVariable<float>();
    
    public override void OnNetworkSpawn()
    {
        if (IsOwner)
        {
            // Only the owner can move
            enabled = true;
        }
    }
    
    void Update()
    {
        if (IsOwner)
        {
            float horizontal = Input.GetAxis("Horizontal");
            transform.Translate(horizontal * Time.deltaTime * 5f, 0, 0);
            
            // Synchronize position
            UpdatePositionServerRpc(transform.position.x);
        }
    }
    
    [ServerRpc]
    void UpdatePositionServerRpc(float newX)
    {
        networkPositionX.Value = newX;
    }
}
```

### Netcode Architecture

{% @mermaid/diagram content="flowchart LR
subgraph "Client Side"
CI\[Client Input] --> CP\[Client Prediction]
CP --> CR\[Client Rendering]
end

```
subgraph "Network Layer"
    CP --> NW[Network Messages]
    NW --> SA[Server Authority]
end

subgraph "Server Side"
    SA --> SV[Server Validation]
    SV --> SU[State Update]
    SU --> BC[Broadcast to Clients]
end

BC --> CR" %}
```

***

## ⚡ Photon PUN

<figure><img src="https://3504844606-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkjovAfZK07rWR0t6DR9s%2Fuploads%2FetaRn0TBE5WsQzahbf2E%2Fimage.png?alt=media&#x26;token=9bcb0907-d28a-4b62-9caa-8060127c837c" alt=""><figcaption></figcaption></figure>

{% embed url="<https://www.photonengine.com/pun>" %}

### Initial Configuration

```csharp
using Photon.Pun;
using Photon.Realtime;

public class NetworkManager : MonoBehaviourPunPV, IConnectionCallbacks
{
    void Start()
    {
        // Connect to Photon
        PhotonNetwork.ConnectUsingSettings();
        PhotonNetwork.GameVersion = "1.0";
    }
    
    public override void OnConnectedToMaster()
    {
        Debug.Log("Connected to master server");
        PhotonNetwork.JoinLobby();
    }
    
    public override void OnJoinedLobby()
    {
        Debug.Log("Joined lobby");
        // Create or join room
        RoomOptions roomOptions = new RoomOptions();
        roomOptions.MaxPlayers = 4;
        PhotonNetwork.JoinOrCreateRoom("GameRoom", roomOptions, TypedLobby.Default);
    }
}
```

### Object Synchronization

```csharp
public class PlayerMovement : MonoBehaviourPunPV, IPunObservable
{
    private Vector3 networkPosition;
    private Quaternion networkRotation;
    
    void Update()
    {
        if (photonView.IsMine)
        {
            // Local movement
            HandleMovement();
        }
        else
        {
            // Smooth interpolation for other players
            transform.position = Vector3.Lerp(transform.position, networkPosition, Time.deltaTime * 10f);
            transform.rotation = Quaternion.Lerp(transform.rotation, networkRotation, Time.deltaTime * 10f);
        }
    }
    
    public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
    {
        if (stream.IsWriting)
        {
            // Send our position
            stream.SendNext(transform.position);
            stream.SendNext(transform.rotation);
        }
        else
        {
            // Receive position from others
            networkPosition = (Vector3)stream.ReceiveNext();
            networkRotation = (Quaternion)stream.ReceiveNext();
        }
    }
}
```

### Photon Architecture

{% @mermaid/diagram content="graph TB
subgraph "Photon Cloud"
PM\[Photon Master] --> PR1\[Region US]
PM --> PR2\[Region EU]
PM --> PR3\[Region Asia]
end

```
subgraph "Game Clients"
    C1[Player 1] --> PR1
    C2[Player 2] --> PR1
    C3[Player 3] --> PR2
    C4[Player 4] --> PR2
end

subgraph "Features"
    PR1 --> F1[Matchmaking]
    PR1 --> F2[Room Management]
    PR1 --> F3[Voice Chat]
    PR1 --> F4[Custom Properties]
end" %}
```

***

## ☁️ AWS GameLift

<figure><img src="https://3504844606-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkjovAfZK07rWR0t6DR9s%2Fuploads%2FNknCgWAg3ubc6uWwaY6z%2Fimage.png?alt=media&#x26;token=a9fdd825-dee0-43ea-a1e7-3a6b30787e70" alt=""><figcaption></figcaption></figure>

### Server Configuration

```csharp
using Amazon.GameLift.Server;
using Amazon.GameLift.Server.Model;

public class GameLiftServer : MonoBehaviour
{
    void Start()
    {
        // Initialize GameLift
        var initOutcome = GameLiftServerAPI.InitSDK();
        if (initOutcome.Success)
        {
            var processParams = new ProcessParameters(
                onStartGameSession: OnStartGameSession,
                onUpdateGameSession: OnUpdateGameSession,
                onProcessTerminate: OnProcessTerminate,
                onHealthCheck: OnHealthCheck,
                port: 7777,
                logParameters: new LogParameters(new List<string>()
                {
                    "/local/game/logs/myserver.log"
                })
            );

            var processReadyOutcome = GameLiftServerAPI.ProcessReady(processParams);
        }
    }

    void OnStartGameSession(GameSession gameSession)
    {
        // Activate game session
        GameLiftServerAPI.ActivateGameSession();
    }

    bool OnHealthCheck()
    {
        return true; // Healthy server
    }

    void OnProcessTerminate()
    {
        GameLiftServerAPI.ProcessEnding();
    }
}
```

### GameLift Client

```csharp
using Amazon.GameLift;
using Amazon.GameLift.Model;

public class GameLiftClient : MonoBehaviour
{
    private AmazonGameLiftClient gameLiftClient;
    
    void Start()
    {
        // Configure client
        var config = new AmazonGameLiftConfig();
        config.RegionEndpoint = Amazon.RegionEndpoint.USWest2;
        gameLiftClient = new AmazonGameLiftClient(config);
    }
    
    public async void SearchGameSession()
    {
        var request = new SearchGameSessionsRequest();
        request.FleetId = "fleet-12345";
        request.FilterExpression = "hasAvailablePlayerSessions=true";
        
        try
        {
            var response = await gameLiftClient.SearchGameSessionsAsync(request);
            if (response.GameSessions.Count > 0)
            {
                JoinGameSession(response.GameSessions[0]);
            }
            else
            {
                CreateGameSession();
            }
        }
        catch (Exception e)
        {
            Debug.LogError($"Error searching for session: {e.Message}");
        }
    }
}
```

### GameLift Architecture

{% @mermaid/diagram content="graph TB
subgraph "AWS GameLift"
GL\[GameLift Service] --> F1\[Fleet 1 - US West]
GL --> F2\[Fleet 2 - EU West]
GL --> F3\[Fleet 3 - Asia Pacific]

```
    F1 --> I1[Instance 1]
    F1 --> I2[Instance 2]
    F2 --> I3[Instance 3]
    F2 --> I4[Instance 4]
end

subgraph "Game Clients"
    C1[Client US] --> GL
    C2[Client EU] --> GL
    C3[Client Asia] --> GL
end

subgraph "Auto Scaling"
    AS[Auto Scaling] --> F1
    AS --> F2
    AS --> F3
    CW[CloudWatch] --> AS
end" %}
```

***

## 🔗 Mirror Networking

### Basic Configuration

```csharp
using Mirror;

public class PlayerController : NetworkBehaviour
{
    [SyncVar]
    public string playerName = "Player";
    
    [SyncVar(hook = nameof(OnHealthChanged))]
    public int health = 100;
    
    void OnHealthChanged(int oldHealth, int newHealth)
    {
        Debug.Log($"Health changed from {oldHealth} to {newHealth}");
    }
    
    [Command]
    void CmdTakeDamage(int damage)
    {
        if (isServer)
        {
            health -= damage;
        }
    }
    
    [ClientRpc]
    void RpcShowDamageEffect()
    {
        // Show damage effect on all clients
        Debug.Log("Damage received!");
    }
}
```

***

## 🚀 Anti-Lag Tips

<figure><img src="https://3504844606-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FkjovAfZK07rWR0t6DR9s%2Fuploads%2FY74oIiIUBTBsEb915spp%2Fimage.png?alt=media&#x26;token=0e16aeb6-16f0-4b9e-a660-2971171af767" alt=""><figcaption></figcaption></figure>

### 1. Interpolation and Extrapolation

```csharp
public class SmoothNetworkTransform : NetworkBehaviour
{
    private Vector3 targetPosition;
    private float lerpRate = 15f;
    
    void Update()
    {
        if (!isLocalPlayer)
        {
            // Smooth interpolation
            transform.position = Vector3.Lerp(transform.position, targetPosition, lerpRate * Time.deltaTime);
        }
    }
    
    [ClientRpc]
    void RpcUpdatePosition(Vector3 newPosition, float timestamp)
    {
        // Latency compensation
        float timeDiff = Time.time - timestamp;
        targetPosition = newPosition + (GetComponent<Rigidbody>().velocity * timeDiff);
    }
}
```

### 2. Data Compression

```csharp
public struct CompressedVector3
{
    public short x, y, z;
    
    public static implicit operator Vector3(CompressedVector3 compressed)
    {
        return new Vector3(
            compressed.x / 100f,
            compressed.y / 100f,
            compressed.z / 100f
        );
    }
    
    public static implicit operator CompressedVector3(Vector3 vector)
    {
        return new CompressedVector3
        {
            x = (short)(vector.x * 100f),
            y = (short)(vector.y * 100f),
            z = (short)(vector.z * 100f)
        };
    }
}
```

### 3. Delta Compression

```csharp
public class DeltaSync : NetworkBehaviour
{
    private Vector3 lastSentPosition;
    private float sendThreshold = 0.1f;
    
    void Update()
    {
        if (isLocalPlayer)
        {
            float distance = Vector3.Distance(transform.position, lastSentPosition);
            if (distance > sendThreshold)
            {
                CmdUpdatePosition(transform.position);
                lastSentPosition = transform.position;
            }
        }
    }
}
```

### 4. Optimization Architecture

```mermaid
flowchart TD
    subgraph "Client Side Optimization"
        CP[Client Prediction] --> LI[Local Input]
        LI --> IR[Immediate Response]
        IR --> SC[Server Confirmation]
    end
    
    subgraph "Network Optimization"
        DC[Delta Compression] --> PQ[Priority Queue]
        PQ --> RU[Reliable/Unreliable]
        RU --> BC[Batch Commands]
    end
    
    subgraph "Server Optimization"
        SV[Server Validation] --> CL[Culling Distance]
        CL --> LOD[Level of Detail]
        LOD --> UP[Update Frequency]
    end
    
    CP --> DC
    BC --> SV
```

***

## ❌ Common Errors

### 1. **Synchronizing Everything**

```csharp
// ❌ BAD: Constantly synchronizing position
void Update()
{
    if (isLocalPlayer)
    {
        CmdUpdatePosition(transform.position); // Sent 60 times per second!
    }
}

// ✅ GOOD: Only synchronize significant changes
void Update()
{
    if (isLocalPlayer && Vector3.Distance(transform.position, lastSentPosition) > 0.1f)
    {
        CmdUpdatePosition(transform.position);
        lastSentPosition = transform.position;
    }
}
```

### 2. **Not Validating on Server**

```csharp
// ❌ BAD: Blindly trusting the client
[Command]
void CmdTakeDamage(int damage)
{
    health -= damage; // The client can send any value!
}

// ✅ GOOD: Validate on server
[Command]
void CmdTakeDamage(int damage)
{
    if (damage > 0 && damage <= maxDamagePerHit)
    {
        health -= damage;
    }
}
```

### 3. **Blocking the Main Thread**

```csharp
// ❌ BAD: Synchronous operation that blocks
void ConnectToServer()
{
    NetworkManager.singleton.StartClient(); // Blocks until connected
    // UI freezes...
}

// ✅ GOOD: Use asynchronous callbacks
void ConnectToServer()
{
    NetworkManager.singleton.StartClient();
    StartCoroutine(WaitForConnection());
}

IEnumerator WaitForConnection()
{
    while (!NetworkManager.singleton.IsClientConnected())
    {
        yield return new WaitForSeconds(0.1f);
    }
    OnConnectedToServer();
}
```

### 4. **Ignoring Latency**

```csharp
// ❌ BAD: Not compensating for latency
[ClientRpc]
void RpcFireProjectile(Vector3 startPos, Vector3 direction)
{
    // The projectile appears where the player was 100ms ago
    Instantiate(projectilePrefab, startPos, Quaternion.LookRotation(direction));
}

// ✅ GOOD: Compensate for latency
[ClientRpc]
void RpcFireProjectile(Vector3 startPos, Vector3 direction, float timestamp)
{
    float lagCompensation = Time.time - timestamp;
    Vector3 compensatedPos = startPos + direction * projectileSpeed * lagCompensation;
    Instantiate(projectilePrefab, compensatedPos, Quaternion.LookRotation(direction));
}
```

***

## 🎯 Best Practices

### 1. **Robust Network Architecture**

{% @mermaid/diagram content="graph TD
subgraph "Client Architecture"
UI\[UI Layer] --> GM\[Game]
GM --> NM\[Network]
NM --> PM\[Prediction]
end

```
subgraph "Server Architecture"
    SA[Server Authority] --> GS[Game State]
    GS --> VM[Validation]
    VM --> SM[Sync]
end

subgraph "Communication"
    PM <--> SA
    SM --> UI
end" %}
```

### 2. **State System**

**(Please don't just use enum as a state machine, this is a simple example)**

```csharp
public class NetworkGameManager : NetworkBehaviour
{
    public enum GameState
    {
        Waiting,
        Starting,
        Playing,
        Ending
    }
    
    [SyncVar(hook = nameof(OnGameStateChanged))]
    public GameState currentState = GameState.Waiting;
    
    void OnGameStateChanged(GameState oldState, GameState newState)
    {
        switch (newState)
        {
            case GameState.Starting:
                StartCoroutine(StartCountdown());
                break;
            case GameState.Playing:
                EnablePlayerControls();
                break;
            case GameState.Ending:
                ShowResults();
                break;
        }
    }
}
```

### 3. **Bandwidth Optimization**

```csharp
public class EfficientNetworkSync : NetworkBehaviour
{
    // Use events for important changes
    [SyncEvent]
    public event System.Action<int> OnScoreChanged;
    
    // SyncVar only for critical data
    [SyncVar]
    public int playerScore;
    
    // Custom serialization for complex data
    public override bool OnSerialize(NetworkWriter writer, bool initialState)
    {
        if (initialState)
        {
            writer.WriteInt32(playerScore);
            return true;
        }
        
        bool dataChanged = false;
        
        if (scoreChanged)
        {
            writer.WriteInt32(playerScore);
            dataChanged = true;
            scoreChanged = false;
        }
        
        return dataChanged;
    }
}
```

### 4. **Testing and Debugging**

```csharp
public class NetworkDebugger : NetworkBehaviour
{
    [Header("Debug Info")]
    public float ping;
    public int packetsReceived;
    public int packetsLost;
    
    void Update()
    {
        if (isLocalPlayer)
        {
            ping = NetworkTime.rtt * 1000f; // Convert to milliseconds
            UpdateDebugUI();
        }
    }
    
    void UpdateDebugUI()
    {
        debugText.text = $"Ping: {ping:F1}ms\nPackets: {packetsReceived}\nLost: {packetsLost}";
    }
    
    #if UNITY_EDITOR
    void OnGUI()
    {
        if (isLocalPlayer)
        {
            GUILayout.Label($"Network Stats:");
            GUILayout.Label($"RTT: {NetworkTime.rtt * 1000f:F1}ms");
            GUILayout.Label($"Bandwidth: {GetComponent<NetworkIdentity>().connectionToServer.Send}");
        }
    }
    #endif
}
```

***

## 🔧 Development Tools

### Network Profiler

```csharp
public class NetworkProfiler : MonoBehaviour
{
    private float bytesPerSecond;
    private float messagesPerSecond;
    
    void Update()
    {
        if (NetworkManager.singleton != null)
        {
            var stats = NetworkManager.singleton.GetNetworkStatistics();
            bytesPerSecond = stats.BytesReceived + stats.BytesSent;
            messagesPerSecond = stats.MessagesReceived + stats.MessagesSent;
        }
    }
}
```

### Latency Simulator

```csharp
public class LatencySimulator : NetworkBehaviour
{
    [Range(0, 500)]
    public int artificialLatency = 0;
    
    private Queue<DelayedMessage> messageQueue = new Queue<DelayedMessage>();
    
    struct DelayedMessage
    {
        public float sendTime;
        public System.Action action;
    }
    
    void Update()
    {
        while (messageQueue.Count > 0 && Time.time >= messageQueue.Peek().sendTime)
        {
            messageQueue.Dequeue().action.Invoke();
        }
    }
    
    void SendWithLatency(System.Action action)
    {
        messageQueue.Enqueue(new DelayedMessage
        {
            sendTime = Time.time + (artificialLatency / 1000f),
            action = action
        });
    }
}
```

***

## 🎮 Complete Example: Simple Multiplayer Game

```csharp
using Unity.Netcode;
using UnityEngine;

public class MultiplayerTank : NetworkBehaviour
{
    [SerializeField] private float moveSpeed = 5f;
    [SerializeField] private float rotateSpeed = 90f;
    [SerializeField] private Transform firePoint;
    [SerializeField] private GameObject bulletPrefab;
    
    private NetworkVariable<Vector3> networkPosition = new NetworkVariable<Vector3>();
    private NetworkVariable<Quaternion> networkRotation = new NetworkVariable<Quaternion>();
    
    public override void OnNetworkSpawn()
    {
        if (IsOwner)
        {
            // Configure camera for local player
            Camera.main.GetComponent<CameraFollow>().SetTarget(transform);
        }
        else
        {
            // Subscribe to network changes for other players
            networkPosition.OnValueChanged += OnPositionChanged;
            networkRotation.OnValueChanged += OnRotationChanged;
        }
    }
    
    void Update()
    {
        if (IsOwner)
        {
            HandleInput();
        }
        else
        {
            InterpolateMovement();
        }
    }
    
    void HandleInput()
    {
        float vertical = Input.GetAxis("Vertical");
        float horizontal = Input.GetAxis("Horizontal");
        
        // Movement
        Vector3 movement = transform.forward * vertical * moveSpeed * Time.deltaTime;
        transform.position += movement;
        
        // Rotation
        float rotation = horizontal * rotateSpeed * Time.deltaTime;
        transform.Rotate(0, rotation, 0);
        
        // Fire
        if (Input.GetKeyDown(KeyCode.Space))
        {
            FireBulletServerRpc();
        }
        
        // Update network position
        UpdateTransformServerRpc(transform.position, transform.rotation);
    }
    
    void InterpolateMovement()
    {
        // Smooth interpolation for other players
        transform.position = Vector3.Lerp(transform.position, networkPosition.Value, Time.deltaTime * 10f);
        transform.rotation = Quaternion.Lerp(transform.rotation, networkRotation.Value, Time.deltaTime * 10f);
    }
    
    [ServerRpc]
    void UpdateTransformServerRpc(Vector3 position, Quaternion rotation)
    {
        networkPosition.Value = position;
        networkRotation.Value = rotation;
    }
    
    [ServerRpc]
    void FireBulletServerRpc()
    {
        // Create bullet on server
        GameObject bullet = Instantiate(bulletPrefab, firePoint.position, firePoint.rotation);
        bullet.GetComponent<NetworkObject>().Spawn();
        
        // Notify all clients
        FireBulletClientRpc();
    }
    
    [ClientRpc]
    void FireBulletClientRpc()
    {
        // Visual/audio firing effects
        AudioSource.PlayClipAtPoint(fireSound, firePoint.position);
        Instantiate(muzzleFlash, firePoint.position, firePoint.rotation);
    }
    
    void OnPositionChanged(Vector3 oldPos, Vector3 newPos)
    {
        // Additional logic when position changes
    }
    
    void OnRotationChanged(Quaternion oldRot, Quaternion newRot)
    {
        // Additional logic when rotation changes
    }
}
```

***

## 📚 Additional Resources

### Official Documentation

* [Unity Netcode](https://docs.unity3d.com/Packages/com.unity.netcode.gameobjects@latest)
* [Photon PUN](https://app.gitbook.com/u/MEMajwavXHTf31Or1eOCfRTlifP2)
* [AWS gamelift](https://app.gitbook.com/u/MEMajwavXHTf31Or1eOCfRTlifP2)

### Testing Tools

* **Network Emulation**: Unity Network Test Tool
* **Load Testing**: Unity Cloud Build + Custom Scripts
* **Analytics**: Unity Analytics + Custom Metrics

### Communities

* Unity Multiplayer Discord
* Photon Community Forums
* AWS GameTech Community

***

## 🚀 Next Steps

1. **Experiment** with each solution using small projects
2. **Measure performance** of your game under different network conditions
3. **Implement metrics** to monitor player experience
4. **Iterate** based on real user feedback

![](https://images.unsplash.com/photo-1556075798-4825dfaaf498?crop=entropy\&cs=srgb\&fm=jpg\&ixid=M3wxOTcwMjR8MHwxfHNlYXJjaHwxMHx8c3VjY2Vzc3xlbnwwfHx8fDE3NTgwMzUyNzd8MA\&ixlib=rb-4.1.0\&q=85\&w=1200)

***

*Multiplayer development is a complex but rewarding field. The key is to start simple, iterate quickly, and always prioritize the player experience.*
