C'è una lunga storia di come siamo arrivati a questa convenzione comune, con molte sfide affascinanti lungo la strada, quindi cercherò di motivarlo in più fasi:
1. Problema: i dispositivi funzionano a velocità diverse
Hai mai provato a giocare a un vecchio gioco DOS su un PC moderno, e funziona in modo incredibilmente veloce - solo una sfocatura?
Molti vecchi giochi avevano un ciclo di aggiornamento molto ingenuo: raccoglievano input, aggiornavano lo stato del gioco e eseguivano il rendering alla velocità consentita dall'hardware, senza tenere conto di quanto tempo fosse trascorso. Ciò significa che non appena l'hardware cambia, il gameplay cambia.
In genere desideriamo che i nostri giocatori abbiano un'esperienza e un'esperienza di gioco coerenti su una vasta gamma di dispositivi (purché soddisfino alcune specifiche minime) sia che utilizzino il telefono dell'anno scorso o il modello più recente, un desktop di gioco di fascia alta o un laptop di livello medio.
In particolare, per i giochi competitivi (multiplayer o classifiche) non vogliamo che i giocatori che corrono su un determinato dispositivo abbiano un vantaggio sugli altri perché possono correre più velocemente o avere più tempo per reagire.
La soluzione infallibile qui è bloccare la velocità con cui eseguiamo gli aggiornamenti dello stato di gioco. In questo modo possiamo garantire che i risultati saranno sempre gli stessi.
2. Quindi, perché non bloccare semplicemente il framerate (ad es. Usando VSync) ed eseguire comunque gli aggiornamenti e il rendering dello stato di gioco in lockstep?
Questo può funzionare, ma non è sempre appetibile per il pubblico. C'è stato molto tempo quando correre a 30 fps è stato considerato il gold standard per i giochi. Ora, i giocatori si aspettano abitualmente 60 fps come barra minima, specialmente nei giochi d'azione multiplayer, e alcuni titoli più vecchi ora sembrano notevolmente mossi poiché le nostre aspettative sono cambiate. C'è anche un gruppo vocale di giocatori PC in particolare che si oppongono a blocchi di framerate. Hanno pagato molto per il loro hardware all'avanguardia e vogliono essere in grado di utilizzare quel muscolo di calcolo per il rendering più fluido e con la massima fedeltà di cui è capace.
Nella realtà virtuale in particolare, il framerate è il re e lo standard continua a salire. All'inizio della recente rinascita della realtà virtuale, i giochi spesso correvano intorno ai 60 fps. Ora 90 è più standard e harware come il PSVR sta iniziando a supportare 120. Questo potrebbe continuare a salire ancora. Quindi, se un gioco VR limita il suo framerate a ciò che è fattibile e accettato oggi, è probabile che rimanga indietro mentre l'hardware e le aspettative si sviluppano ulteriormente.
(Di norma, diffidare quando viene detto "i giocatori non possono percepire nulla più velocemente di XXX" poiché di solito si basa su un particolare tipo di "percezione", come riconoscere un frame in sequenza. La percezione della continuità del movimento è generalmente molto più sensibile. )
L'ultimo problema qui è che un gioco che usa un framerate bloccato deve anche essere conservativo: se ti capita di cogliere un momento del gioco in cui stai aggiornando e mostrando un numero insolitamente alto di oggetti, non vuoi perderti il frame scadenza e causare una balbuzie o un intoppo evidente. Pertanto, è necessario impostare budget per i contenuti abbastanza bassi da lasciare spazio o investire in funzioni di regolazione dinamica della qualità più complicate per evitare di agganciare l'intera esperienza di gioco alle prestazioni peggiori sull'hardware con specifiche minime.
Ciò può essere particolarmente problematico se i problemi di prestazioni si manifestano in una fase avanzata dello sviluppo, quando tutti i sistemi esistenti sono costruiti e messi a punto assumendo un framerate di rendering a blocchi che ora non si può sempre colpire. Il disaccoppiamento delle percentuali di aggiornamento e rendering offre una maggiore flessibilità per gestire la variabilità delle prestazioni.
3. L'aggiornamento a un timestep fisso non presenta gli stessi problemi di (2)?
Penso che questo sia il frutto della domanda originale: se disaccoppiamo i nostri aggiornamenti e talvolta eseguiamo il rendering di due fotogrammi senza aggiornamenti dello stato del gioco, allora non è lo stesso del rendering lockstep con un framerate inferiore, dal momento che non ci sono cambiamenti visibili su lo schermo?
Esistono diversi modi in cui i giochi utilizzano il disaccoppiamento di questi aggiornamenti con buoni risultati:
a) La velocità di aggiornamento può essere più veloce del framerate renderizzato
Come tyjkenn nota in un'altra risposta, la fisica in particolare viene spesso calpestata a una frequenza più elevata rispetto al rendering, il che aiuta a ridurre al minimo gli errori di integrazione e a fornire collisioni più accurate. Quindi, invece di avere 0 o 1 aggiornamenti tra i frame renderizzati potresti avere 5 o 10 o 50.
Ora il rendering del giocatore a 120 fps può ottenere 2 aggiornamenti per fotogramma, mentre il giocatore con rendering hardware con specifiche inferiori a 30 fps ottiene 8 aggiornamenti per fotogramma ed entrambi i loro giochi girano alla stessa velocità di gioco-tick-per-tempo reale al secondo. L'hardware migliore lo rende più fluido, ma non altera radicalmente il funzionamento del gameplay.
Qui c'è il rischio che, se la frequenza di aggiornamento non corrisponde al framerate, è possibile ottenere una "frequenza di battimento" tra i due . Per esempio. la maggior parte dei frame ha abbastanza tempo per 4 aggiornamenti dello stato del gioco e un po 'di avanzi, quindi ogni tanto abbiamo abbastanza risparmi per fare 5 aggiornamenti in un frame, facendo un piccolo salto o balbettare nel movimento. Questo può essere risolto da ...
b) Interpolare (o estrapolare) lo stato del gioco tra gli aggiornamenti
Qui lasceremo spesso vivere lo stato del gioco in un timestep fisso in futuro e memorizzeremo abbastanza informazioni dai 2 stati più recenti che possiamo rendere un punto arbitrario tra di loro. Quindi quando siamo pronti a mostrare un nuovo fotogramma sullo schermo, ci mescoliamo al momento appropriato solo a scopo di visualizzazione (cioè non modifichiamo lo stato di gioco sottostante qui)
Se fatto bene, questo rende il movimento morbido, e aiuta anche a mascherare alcune fluttuazioni nel framerate, purché non scendiamo troppo in basso.
c) Aggiunta di fluidità ai cambiamenti dello stato non di gioco
Anche senza interpolare lo stato di gioco, possiamo comunque ottenere delle vittorie fluide.
Cambiamenti puramente visivi come l'animazione dei personaggi, i sistemi di particelle o VFX e gli elementi dell'interfaccia utente come HUD, spesso si aggiornano separatamente dal timestep fisso dello stato di gioco. Questo significa che se stiamo controllando il nostro stato di gioco più volte per fotogramma, non stiamo pagando il costo con ogni tick - solo sul passaggio di rendering finale. Al contrario, ridimensioniamo la velocità di riproduzione di questi effetti in modo che corrisponda alla lunghezza del fotogramma, in modo che vengano riprodotti in modo uniforme come consente il framerate di rendering, senza influire sulla velocità o sull'equità del gioco, come discusso in (1).
Anche il movimento della telecamera può fare questo - specialmente in VR, a volte mostriamo lo stesso fotogramma più di una volta ma lo riproiettiamo per tener conto del movimento della testa del giocatore tra , in modo da poter migliorare la latenza e il comfort percepiti, anche se possiamo rendere nativamente tutto così velocemente. Alcuni sistemi di streaming di gioco (in cui il gioco è in esecuzione su un server e il lettore esegue solo un thin client) utilizzano anche una versione di questo.
4. Perché non usare quello stile (c) per tutto? Se funziona per l'animazione e l'interfaccia utente, non possiamo semplicemente ridimensionare gli aggiornamenti dello stato di gioco in modo che corrispondano al framerate corrente?
Sì * questo è possibile, ma no non è semplice.
Questa risposta è già un po 'lunga, quindi non entrerò in tutti i dettagli cruenti, solo un breve riassunto:
Moltiplicare per le deltaTime
opere per adattarsi agli aggiornamenti di lunghezza variabile per il cambiamento lineare (ad es. Movimento a velocità costante, conto alla rovescia di un timer o avanzamento lungo una sequenza temporale di animazione)
Sfortunatamente, molti aspetti dei giochi non sono lineari . Anche qualcosa di semplice come la gravità richiede tecniche di integrazione più sofisticate o sottofasi a risoluzione più elevata per evitare risultati divergenti in framerate diverse. L'input e il controllo del giocatore è esso stesso un'enorme fonte di non linearità.
In particolare, i risultati del rilevamento discreto delle collisioni e della risoluzione dipendono dalla velocità di aggiornamento, causando errori di tunneling e jitter se i frame diventano troppo lunghi. Quindi un framerate variabile ci costringe a utilizzare metodi di rilevazione di collisione continua più complessi / costosi su più dei nostri contenuti, o tollerare la variabilità nella nostra fisica. Anche il rilevamento continuo delle collisioni incontra difficoltà quando gli oggetti si muovono negli archi, richiedendo tempi più brevi ...
Quindi, nel caso generale di un gioco di media complessità, mantenere un comportamento coerente e l'equità interamente attraverso il deltaTime
ridimensionamento è da qualche parte tra molto difficile e intensivo di manutenzione a assolutamente impossibile.
La standardizzazione di una frequenza di aggiornamento ci consente di garantire un comportamento più coerente in una serie di condizioni , spesso con un codice più semplice.
Mantenere la frequenza di aggiornamento disaccoppiata dal rendering ci dà la flessibilità di controllare la fluidità e le prestazioni dell'esperienza senza alterare la logica di gioco .
Anche allora non otteniamo mai un'indipendenza framerata veramente "perfetta", ma come tanti approcci nei giochi ci offre un metodo controllabile per entrare in "abbastanza buono" per le esigenze di un determinato gioco. Ecco perché viene comunemente insegnato come utile punto di partenza.