Prediction/Synchronization

Scott Marchant
Figuur 1: Prediction in CounterStrike (Source Multiplayer Networking. (2019). [Afbeelding]. https://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking)

Inhoud

  1. Inleiding
  2. Verschillende prediction technieken
    2.1 Deterministic Lockstep
    2.2 Snapshot Interpolation
    2.3 State Synchronization
    2.4 Welke heb ik gekozen en waarom
  3. Proces
    3.1 Research
    3.2 Opzet en Unet
    3.3 Movement en prediction
    3.4 Server Reconciliation
  4. Eindresultaat
  5. Toekomst
  6. Bronnen

1. Inleiding

Hoe maak ik prediction in een multiplayer game? Ik ben al vrij lang geïnteresseerd in het maken van een multiplayer game. Om hier dan iets meer over te leren vond ik een grootte kans. Hoe je bijvoorbeeld er voor zorgt dat je lag compenseert en je eigen synchronisatie code maakt.

Ik heb hier gebruik gemaakt van Unet. De multiplayer oplossing van Unity, omdat ik hier al een klein beetje ervaring mee heb. De bedoeling is dat synchronizatie problemen opgelost wordt en latency verminderd word.

Figuur 2: Een simpele Client-Server set-up. (Gambetta, G. (2020). Simpele client-server structuur. https://www.gabrielgambetta.com/client-side-prediction-server-reconciliation.html)
Figuur 3: Synchronizatie problemen door lag. (Gambetta, G. (2020). Synchronizatie problemen. https://www.gabrielgambetta.com/client-side-prediction-server-reconciliation.html)

2. Verschillende prediction technieken

Er zijn drie verschillende vormen van prediction. Hier ga ik uitleggen welke drie er zijn, wat ze inhouden en voor welke ik heb gekozen en waarom. Je hebt Deterministic lockstep, Snapshot Interpolation of State Synchronization.

2.1 Deterministic lockstep

Hierbij word er alleen gekeken naar de player-input. Dit word veel gebruikt bij strategie games. Omdat het deterministic is zorgt het ervoor dat er niks willekeurigs kan gebeuren. Dit zorgt er dan ook weer voor dat het niet gebruikt kan worden voor race of first person shooter games. Deze vorm neemt weinig bandbreedte in. Dit is het beste voor twee tot vier spelers. (Fiedler, 2014)

2.2 Snapshot Interpolation

Hier word gebruikt gemaakt van de positie en de rotatie van de speler. Die word als een snapshot meegegeven aan de server. De server stuurt dit dan weer naar de rest van de clients. Dit is een goede techniek om te gebruiken maar neemt veel bandbreedte in. Met deze techniek kan je meer spelers kwijt dan bij deterministic lockstep. (Fiedler, 2014)

2.3 State Synchronization

Dit is een techniek waarbij je eigenlijk de twee vorige met elkaar combineert. Hierbij gebruik je de input en de state van de speler om prediction uit te voeren. (Fiedler, 2015)

2.4 Welke vorm gekozen?

Ik heb gekozen voor state synchronization. Deterministic lockstep viel direct al weg, omdat ik prediction wou gebruiken in een fast-paced game. Snapshot interpolation is na deterministic lockstep de beste keuze alleen neemt dit veel bandbreedte in beslag. Bij state synchronization doe je een deel van beide en kan je kiezen hoeveel je update, hierdoor word er minder bandbreedte gebruikt.

3. Proces

3.1 Research

Toen ik hier aan begon ging ik kijken hoe je het kan toepassen op unet. Later kwam ik er achter dat Unet zijn eigen versie van synchronisatie had genaamd NetworkTransform. Door hier weer een beetje onderzoek naar te doen kwam ik er achter dat hun eigen synchronisatie veel bandbreedte in neemt. Door middel van je eigen code te schrijven kan je dat dus tegen gaan. Later kwam ik uit bij een GDC talk over prediction van Glenn Fiedler die drie technieken uitlegde. Zo kwam ik bij state synchronization uit.

3.2 Opzet en Unet

Om te beginnen moest ik Unet installeren in het project om een multiplayer connectie te kunnen starten. Hiervoor hebben ze verschillende componenten waarbij je makkelijk een netwerk op kan zetten en de HUD ervoor.

Figuur 4: De NetworkManager met components

Daarna komt de plane voor het terrein en de blokjes als de spelers.

3.3 Movement en Prediction

Movement voor een game is vrij simpel op te zetten. Als je op W klikt gaat je blokje naar voren, met S gaat hij naar achter. A en D zorgen voor de rotatie. Alleen in dit geval moet het over het netwerk gaan. Omdat ik de state synchronization techniek wil gebruiken moeten we de input meenemen in de state die we versturen.

void FixedUpdate()
    {
        if (isLocalPlayer)
        {
            PlayerInput playerInput = GetPlayerInput();
 
            if (playerInput != null)
            {
                pendingMoves.Add(playerInput);
                UpdatePredictedState();
                CmdMoveOnServer(playerInput);
            }
        }
        SyncState();
    }

Ik zet de input van de local speler in een lijst, daarna word de state van de local speler geupdate en word de input door gestuurd naar server state. De server state stuurt dat dan weer door naar de clients.

void SyncState()
   {
       if (isServer)
       {
           transform.position = state.position;
           transform.rotation = state.rotation;
           return;
       }
 
       PlayerState stateToRender = isLocalPlayer ? predictedState : state;

De synchronisatie is er nu, maar het kan nog steeds gebeuren dat vanwege lag de synchronisatie niet goed gaat. Daarvoor hebben we server reconciliation.

3.4 Server Reconciliation

[SyncVar(hook = "OnServerStateChanged")]
    public PlayerState state;
public void OnServerStateChanged(PlayerState newState)
    {
        state = newState;
        if (pendingMoves != null)
        {
            while (pendingMoves.Count >
                  (predictedState.timestamp - state.timestamp))
            {
                pendingMoves.RemoveAt(0);
            }
            UpdatePredictedState();
        }
    }

Als de Playerstate veranderd word de OnServerStateChanged functie aangeroepen. Dit zorgt er voor dat hij kijkt naar de tijd van de predicted state en alle input die daarvoor kwam weg gooit uit de lijst. zodat alle andere clients kunnen synchroniseren met de rest. Daarna om alles smooth te laten verlopen heb ik een lerpeasing en lerpspacing.

transform.position = Vector3.Lerp(transform.position,
    stateToRender.position * Settings.PlayerLerpSpacing,
    Settings.PlayerLerpEasing);
transform.rotation = Quaternion.Lerp(transform.rotation,
    stateToRender.rotation,
    Settings.PlayerLerpEasing);

4. Eindresultaat

Het eind resultaat is dus een fast-paced movement synchronisatie. Die door middel van prediction en server reconciliation lag reduceert. De code die ik heb geschreven kan je ook herbruiken voor andere games. Zo heb ik dat getest op het project van de lava shader van een mede-student.

Figuur 5: Eindresultaat. Het blokje dat beweegt is een client die geconnect is aan de server.

5. Toekomst

Als ik verder zou gaan aan dit. Zou ik dead reckoning willen implementeren om zo verder latency te verbergen. En ik zou er dan een shooter of race-game om heen bouwen in plaats van een simpel blokje op een plane.

6. Bronnen

Arellano, C. (2015, 22 september). UNET Unity 5 Networking Tutorial Part 1 of 3 – Introducing the HLAPI. Geraadpleegd op 11 maart 2021, van https://www.gamasutra.com/blogs/ChristianArellano/20150922/254218/UNET_Unity_5_Networking_Tutorial_Part_1_of_3__Introducing_the_HLAPI.php

Fiedler, G. (2014, 29 november). Deterministic Lockstep. Geraadpleegd op 11 maart 2021, van https://gafferongames.com/post/deterministic_lockstep/

Fiedler, G. (2014, 30 november). Snapshot Interpolation. Geraadpleegd op 11 maart 2021, van https://gafferongames.com/post/snapshot_interpolation/

Fiedler, G. (2015, 5 januari). State Synchronization. Geraadpleegd op 11 maart 2021, van https://gafferongames.com/post/state_synchronization/

Gambetta, G. (2020). Client-Side Prediction and Server Reconciliation – Gabriel Gambetta. Geraadpleegd op 15 maart 2021, van https://www.gabrielgambetta.com/client-side-prediction-server-reconciliation.html

Physics for Game Programmers : Networking for Physics Programmers. (2015, 2 maart). [Videobestand]. Geraadpleegd van https://www.gdcvault.com/play/1022195/Physics-for-Game-Programmers-Networking

Source Multiplayer Networking. (2019). [Afbeelding]. Geraadpleegd van https://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking

Related Posts