Con quale frequenza aggiornare un client di gioco sul mondo?


10

Usando socket.io , ho una comunicazione simile a quella di altri MMORPG, una connessione costante con i messaggi.

Nel mio progetto finora, il client invia la posizione del giocatore e il frame di animazione ad ogni frame di aggiornamento. Quando il server riceve quel messaggio, lo trasmette a tutti i client, che quindi sposta l'immagine di conseguenza.

Sarebbe un'idea migliore "collezionarli" e trasmetterli, diciamo, una volta in 1/10 di secondo?

Inoltre, il client dovrebbe inviare molti messaggi diversi (exp guadagnato, cliccato sull'elemento) non appena si verificano o piuttosto solo uno raccolto? Il primo sarebbe implementato più facilmente.

Risposte:


16

Affronterò questo argomento da una discussione di alto livello e poi lavorerò sulle tue domande. Per motivi di divulgazione, non ho esperienza personale sull'uso di socket.io, ma molta esposizione allo spazio problematico per quanto riguarda i MMORPG.

La progettazione dell'architettura di rete di un motore MMORPG e / o la selezione di un progetto di middleware o open source per fornire la funzionalità è una delle decisioni più impegnative influenzate dal design del gioco, dal budget e dall'esperienza tecnica del team. La scelta definitiva influenzerà altre decisioni architettoniche (e talvolta anche decisioni di progettazione).

Come sviluppatori di MMORPG, prevediamo un grande successo (spesso noto anche come successo catastrofico) in cui grandi numeri attivano spie e sirene. Uno dei grandi numeri spaventosi che emerge è negli algoritmi N-quadrati (N ^ 2 di seguito), nella tua domanda la prima cosa che mi è saltata fuori è che sembrava che il design richiedesse a un'entità di trasmettere informazioni a tutte le altre entità connesse. Questo è il classico esempio di un problema N ^ 2.

Gli MMO in genere affrontano il problema N ^ 2 attaccando il problema in diversi modi; sistemi di consapevolezza (situazionale, spaziale, ecc.) in cui un'entità è a conoscenza di alcuni sottogruppi di tutte le altre entità, suddividendo i giocatori in "frammenti" diversi, partizionando i giocatori in "zone" e / o instanziando, implementando meccanismi di gioco che scoraggiano troppi giocatori che si riuniscono insieme (tempeste di teletrasporto di Asheron Call), ecc.

La maggior parte dei MMORPG e molti motori FPS hanno architetture di rete abbastanza sofisticate che supportano una varietà di funzionalità tra cui:

  • percorsi di comunicazione affidabili e inaffidabili (TCP, implementazioni personalizzate di pacchetti UDP e UDP affidabili)
  • modellatura della larghezza di banda (priorità, durata, ecc.)
  • replica automatica di dati sul campo / viabili e chiamate di funzione
  • insiemi di dati atomici (ovvero dati comunicati insieme)
  • aggiornamenti discreti (ovvero dove ogni transizione è importante)
  • correzione della latenza
  • una varietà di trucchi per rendere il cliente reattivo

Trovo che la documentazione Unreal Networking e Valve Networking Documentation forniscano un buon spunto su una varietà di questioni.

Quindi, ora affrontiamo le domande.

Sarebbe un'idea migliore "collezionarli" e trasmetterli, diciamo, una volta in 1/10 di secondo?

È difficile fornire una semplice risposta sì o no qui ... perché dipende dalla scala (numero di entità osservanti), frequenza degli aggiornamenti e dimensione degli aggiornamenti. Ad esempio, raccoglierli tutti potrebbe essere terribilmente sbagliato se la dimensione degli aggiornamenti potrebbe far esplodere un buffer da qualche parte.

I client per MMORPG e giochi FPS sono generalmente progettati in modo tale da visualizzare qualcosa che "sembra" giusto anche se non ricevono un aggiornamento per molti più frame di aggiornamento di quanto sia "normale". Quando si utilizza la comunicazione inaffidabile (UDP), ci si può aspettare di perdere un certo numero di aggiornamenti nel vuoto, i client possono compensare inviando aggiornamenti più frequenti di quelli che potrebbero essere utilizzati con un trasporto affidabile.

Da una rapida revisione della documentazione di socket.io, sembra che supporti percorsi di comunicazione sia affidabili che inaffidabili (volatili nella sua terminologia).

Mi avvicinerei a questo prima affrontando, con quale frequenza sono necessari gli aggiornamenti ...

Se un giocatore si muove in linea retta a una velocità costante, una frequenza di aggiornamento inferiore va bene perché i clienti osservanti possono prevedere con elevata precisione dove si troverà il giocatore in qualsiasi momento. Quando un giocatore sta girando in un cerchio stretto o sta facendo rapidi cambi di direzione, sono necessari aggiornamenti molto più frequenti. Al contrario, quando un giocatore non si muove affatto, non vi è alcun motivo per inviare aggiornamenti di movimento.

Indipendentemente da ciò, probabilmente non è (in genere) necessario inviare aggiornamenti ogni frame dal client al server. Il server stesso può scegliere di inviare messaggi a ciascun frame dove li ha o di ritardarli (vedere modellazione della larghezza di banda, definizione delle priorità e durata dell'aggiornamento).

Altri tipi di aggiornamenti hanno caratteristiche diverse ... ad esempio, considera un campo "salute" che viene modificato quando un giocatore o una creatura viene danneggiato. Un modo per implementare questo è trasmettere immediatamente ogni cambiamento quando succede, ma questo porta a sprecare elaborazione e larghezza di banda se il valore viene cambiato più volte in un frame o frame consecutivi (le architetture di rete che implementano il shaping della larghezza di banda risolvono questo problema fondendo gli aggiornamenti su solo il più recente viene inviato a un client osservatore quando ha larghezza di banda disponibile).

il cliente dovrebbe inviare molti messaggi diversi (exp guadagnato, cliccato sull'elemento) non appena si verificano o piuttosto solo uno raccolto?

Ancora una volta, nessuna semplice risposta sì o no funzionerà qui. A seconda di cosa intendi esattamente per raccolta ... entrambi possono avere ragione in circostanze diverse e dipendere anche dall'implementazione del livello di rete.

La raccolta di messaggi per un'entità specifica da inviare in quanto un messaggio può (a seconda dell'implementazione) ridurre l'overhead della larghezza di banda per l'invio di un messaggio (riducendo i costi) al contrario (a seconda dell'implementazione, come le mappature dei campi / valori comunicate dalle stringhe) può aumentare la larghezza di banda requisiti rispetto a un tipo di messaggio specifico più semplice.

Esaminando la documentazione di socket.io, mi sembra che l'overhead del messaggio si trovi all'estremità superiore dello spettro, il che favorirebbe la raccolta di aggiornamenti da inviare come messaggio aggregato rispetto a molti singoli aggiornamenti.

Ti consiglio di rivedere tutti gli aggiornamenti che prevedi di replicare, ad esempio la maggior parte dei MMORPG e degli FPS non si preoccupano di inviare i giocatori su eventi X cliccati ai clienti osservandoli a meno che ciò non comporti un cambiamento di stato per un oggetto di cui erano a conoscenza. .


3
+1 Ottima risposta. Suggerirei inoltre di iniziare in modo semplice e di ottimizzare da lì. I messaggi di spam, sicuramente, si scatenano e vedono la tua larghezza di banda che inizia a vacillare. Questo è il motivo per cui è bene aver organizzato prove di stress. Implementate l'approccio più ingenuo possibile e fate alcuni test; fai delle ottimizzazioni; lo stress test di nuovo, ottimizzi ulteriormente, risciacqui, ripeti. Se è assolutamente banale implementare un'ottimizzazione il primo giorno, vai avanti; ma tieni presente che anche le ottimizzazioni più insignificanti possono portare a presupposti che potrebbero essere successivamente smentiti in un ambiente di produzione.
Ingegnere

5

Ecco un esempio del mondo reale: RuneScape "spunta" una volta ogni ~ 0,6 secondi. Puoi leggerlo qui . Immagino che semplifichi le cose dal loro punto di vista, poiché nei loro script probabilmente specificano tempi e ritardi nelle zecche anziché in millisecondi. E, naturalmente, con una percentuale di tick così grande / lenta, gli utenti con connessioni lente non presentano un enorme svantaggio rispetto agli altri giocatori.


0

Un modo di lavorare consiste nel disaccoppiare le effettive modifiche ai dati dalla trasmissione di tali modifiche. Quando un oggetto cambia, apporta la modifica e imposta un flag "sporco" e, quando arriva il momento di trasmettere eventuali modifiche, invia solo i dati per gli oggetti contrassegnati e cancella il flag. I valori che vengono modificati più volte vengono automaticamente uniti qui perché l'aggiornamento invia lo stato solo al momento della trasmissione. Puoi anche contrassegnare oggetti secondari o singole proprietà in modo da trasmettere solo ciò che è cambiato.

Oh, e di solito non invieresti i frame di animazione al server o agli altri client. Lo stato preciso dell'animazione viene generalmente considerato un dettaglio della presentazione e viene lasciato a ciascun client per la risoluzione, e invece devi solo assicurarti che ogni client sappia quale animazione è attualmente in riproduzione.


Non lo farei? Che dire delle pose o qualcosa del genere, devo essere sicuro che tutti i clienti vedano lo stesso?
Lanbo,

È abbastanza poco pratico garantire che ogni cliente veda esattamente la stessa cosa perché le informazioni richiedono tempo per viaggiare, client diversi hanno una latenza di rete diversa, eseguono il rendering a velocità diverse, ecc. Quindi, cercare di far mostrare a tutti esattamente lo stesso frame contemporaneamente è di solito un sforzo inutile. Invece potresti provare ad assicurarti che inizino la stessa animazione all'incirca nello stesso momento, e questo è abbastanza buono.
Kylotan,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.