Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

Introduzione

Tick di fisica e frame renderizzati

Un concetto fondamentale da comprendere in Godot è la distinzione tra tick di fisica (a volte chiamati iterazioni o frame di fisica) e frame renderizzati. La fisica procede a una frequenza di tick fissa (impostata in Impostazioni del progetto > Fisica > Comuni > Tick di fisica al secondo), che normalmente è di 60 tick al secondo.

Tuttavia, il motore non renderizza necessariamente alla stessa frequenza. Sebbene molti monitor si aggiornino a 60 Hz (cicli al secondo), molti altri lo fanno a frequenze completamente diverse (ad esempio 75 Hz, 144 Hz, 240 Hz o superiori). Anche se un monitor può essere in grado di visualizzare un nuovo frame, ad esempio 60 volte al secondo, non vi è alcuna garanzia che la CPU e la GPU siano in grado di fornire frame a questa frequenza. Ad esempio, quando si utilizza il V-Sync, il computer potrebbe essere troppo lento per 60 FPS e raggiungere solo i 30 FPS, nel qual caso i frame visualizzati passeranno a 30 FPS (causando scatti).

Ma qui c'è un problema. Cosa succede se i tick di fisica non coincidono con i frame? Cosa succede se la frequenza dei tick di fisica è sfasata rispetto al frame rate? O peggio, cosa succede se la frequenza dei tick di fisica è inferiore al frame rate renderizzato?

Questo problema è più facile da comprendere se consideriamo uno scenario estremo. Se si imposta la frequenza di tick di fisica a 10 tick al secondo, in un gioco semplice con un frame rate renderizzato di 60 FPS, se tracciamo un grafico delle posizioni di un oggetto rispetto ai frame renderizzati, possiamo vedere che le posizioni sembreranno "scattare" ogni 1/10 di secondo, anziché fornire un movimento fluido. Quando la fisica calcola una nuova posizione per un nuovo oggetto, questo non viene renderizzato in questa posizione per un solo frame, ma per 6 frame.

../../../_images/fti_graph_fixed_ticks.webp

È possibile osservare questo scatto in altre combinazioni di tick/frame rate come glitch o tremolio, causati da questo effetto a gradini dovuto alla discrepanza tra il tempo di tick di fisica e il tempo di frame renderizzati.

Cosa possiamo fare se frame e tick non sono sincronizzati?

Bloccare la frequenza dei tick/frame insieme?

La soluzione più ovvia è eliminare il problema, assicurandosi che ci sia un tick di fisica che coincida con ogni frame. Questo era l'approccio utilizzato sulle vecchie console e sui computer con hardware fisso. Se si sa che ogni giocatore utilizzerà lo stesso hardware, è possibile assicurare che sia abbastanza veloce per calcolare tick e frame, ad esempio a 50 FPS, e si avrà la certezza che funzionerà bene per tutti.

Tuttavia, i giochi moderni spesso non sono più progettati per hardware fisso. Si pensa spesso di pubblicarli su computer desktop, dispositivi mobili e altro ancora. Tutti questi dispositivi variano enormemente in prestazioni, oltre ad avere diverse frequenze di aggiornamento dei monitor. Dobbiamo pensare a un modo migliore per affrontare il problema.

Adattare la frequenza dei tick?

Invece di progettare il gioco con una frequenza fissa dei tick di fisica, potremmo consentire alla frequenza dei tick di adattarsi all'hardware dell'utente finale. Potremmo, ad esempio, utilizzare una frequenza fissa di tick adatta a quell'hardware, o persino variare la durata di ogni tick di fisica per corrispondere a una specifica durata di frame.

Questo funziona, ma c'è un problema. La fisica (e la logica di gioco, che spesso è eseguita anche nel _physics_process) funziona meglio e più coerentemente quando è eseguita a una frequenza di tick fissa e predeterminata. Se si tenta di eseguire la fisica di un gioco di corsa progettato per 60 TPS (tick al secondo) a, ad esempio, 10 TPS, la fisica si comporterà in modo completamente diverso. I controlli potrebbero essere meno reattivi, le collisioni/traiettorie potrebbero essere completamente diverse. Si potrebbe testare il gioco a fondo a 60 TPS, per poi scoprire che non funziona bene sui computer degli utenti finali quando è eseguito a una frequenza diversa di tick.

Ciò può rendere difficile garantire qualità a causa di bug difficili da riprodurre, soprattutto nei giochi AAA, dove problemi di questo tipo possono essere molto costosi. Ciò può anche essere problematico per l'integrità competitiva nei giochi multigiocatore, poiché eseguire il gioco a determinate frequenze di tick può essere più vantaggioso rispetto ad altre.

Bloccare la frequenza dei tick, ma usare l'interpolazione per rendere fluidi i frame tra i tick di fisica

Questo è diventato uno degli approcci più diffusi per affrontare il problema, sebbene sia facoltativo e normalmente disabilitato.

Abbiamo stabilito che la soluzione fisica/logica di gioco più preferibile, per coerenza e prevedibilità, è una frequenza di tick di fisica fissata in fase di progettazione. Il problema è la discrepanza tra la posizione fisica registrata e il punto in cui "vorremmo" che un oggetto fisico sia mostrato su un frame per rendere un movimento fluido.

La risposta risulta semplice, ma all'inizio può essere un po' difficile da assimilare.

Invece di tenere traccia solo della posizione attuale di un oggetto fisico nel motore, teniamo traccia sia della posizione attuale dell'oggetto, sia della posizione precedente sul tick di fisica precedente.

Perché abbiamo bisogno della posizione precedente (in realtà dell'intera trasformazione, inclusa rotazione e scala)? Con un po' di magia matematica, possiamo usare un'interpolazione per calcolare quale sarebbe la trasformazione dell'oggetto tra quei due punti, nel nostro mondo ideale di movimento fluido e continuo.

../../../_images/fti_graph_interpolated.webp

Interpolazione lineare

Il modo più semplice per garantirlo è l'interpolazione lineare, o lerping, che potresti aver già utilizzato in precedenza.

Consideriamo solo la posizione, e una situazione in cui sappiamo che la coordinata X del tick di fisica precedente era 10 unità e che la coordinata X del tick di fisica attuale è 30 unità.

Nota

Anche se la matematica è spiegata qui, non c'è da preoccuparsi per i dettagli, poiché questo passaggio sarà eseguito automaticamente. In realtà, Godot potrebbe utilizzare forme di interpolazione più complesse, ma l'interpolazione lineare è la più semplice da spiegare.

La frazione di interpolazione della fisica

Se i nostri tick di fisica si verificano 10 volte al secondo (per questo esempio), cosa succede se il nostro frame renderizzato avviene a 0,12 secondi? Possiamo fare un po' di calcoli per capire dove si troverebbe l'oggetto per ottenere un movimento fluido tra i due tick.

Innanzitutto, dobbiamo calcolare quanto è avanti l'oggetto nel tick di fisica. Se l'ultimo tick è avvenuto a 0,1 secondi, ci troviamo a 0,02 secondi (0,12 - 0,1) avanti un tick che sappiamo durerà 0,1 secondi (10 tick al secondo). La frazione tra il tick è quindi:

fraction = 0.02 / 0.10
fraction = 0.2

Questa è chiamata frazione di interpolazione della fisica, ed è calcolata comodamente da Godot. Può essere recuperata su qualsiasi frame chiamando Engine.get_physics_interpolation_fraction.

Calcolare la posizione interpolata

Una volta ottenuta la frazione di interpolazione, possiamo inserirla in un'equazione standard di interpolazione lineare. La coordinata X sarebbe quindi:

x_interpolated = x_prev + ((x_curr - x_prev) * 0.2)

Quindi sostituendo x_prev con 10 e x_curr con 30:

x_interpolated = 10 + ((30 - 10) * 0.2)
x_interpolated = 10 + 4
x_interpolated = 14

Spieghiamolo in dettaglio:

  • Sappiamo che la X parte dalla coordinata sul tick precedente (x_prev) che è 10 unità.

  • Sappiamo che dopo il tick completo, sarà stata aggiunta la differenza tra il tick attuale e il tick precedente (x_curr - x_prev) (che è di 20 unità).

  • L'unica cosa che dobbiamo variare è la proporzione di questa differenza che aggiungiamo, a seconda di quanto siamo avanti nel tick di fisica.

Nota

Sebbene questo esempio interpoli la posizione, lo stesso si può fare con la rotazione e la scala degli oggetti. Non è necessario conoscere i dettagli, perché Godot farà tutto automaticamente.

Trasformazioni smussate tra tick di fisica?

Mettendo insieme il tutto, dimostriamo che dovrebbe essere possibile ottenere una stima fluida e precisa della trasformazione degli oggetti tra il tick attuale e il tick precedente.

Ma aspetta, potresti aver notato una cosa. Se interpoliamo tra i tick attuali e i tick precedenti, non stiamo stimando la posizione dell'oggetto ora, ma la posizione dell'oggetto nel passato. Per essere precisi, stiamo stimando la posizione dell'oggetto tra 1 e 2 tick nel passato.

Nel passato

Cosa significa? Questo schema funziona, ma significa che stiamo effettivamente introducendo un ritardo tra ciò che vediamo sullo schermo e dove dovrebbero essere gli oggetti.

In pratica, la maggioranza delle persone non noterà questo ritardo, o meglio, in genere non è inaccettabile. Ci sono già ritardi significativi nei giochi, ma solitamente non li notiamo. L'effetto più significativo è un leggero ritardo nell'input, che può essere importante nei giochi con rapide contrazioni. In alcune di queste situazioni di input rapidi, si potrebbe voler disattivare l'interpolazione della fisica e utilizzare uno schema diverso, oppure utilizzare una frequenza di tick elevata, che mitiga questi ritardi.

Perché guardare al passato? Perché non prevedere il futuro?

Esiste un'alternativa a questo schema: invece di interpolare tra il tick precedente e tick attuale, usiamo la matematica per estrapolare nel futuro. Cerchiamo di prevedere dove sarà l'oggetto, piuttosto che mostrare dove si trovava. Questa soluzione è possibile e potrebbe essere offerta come opzione in futuro, ma presenta alcuni notevoli svantaggi:

  • La previsione potrebbe non essere corretta, soprattutto quando un oggetto entra in collisione con un altro oggetto durante il tick di fisica.

  • Quando una previsione è errata, l'oggetto può estrapolarsi in una posizione "impossibile", come all'interno di un muro.

  • Purché la velocità di movimento sia lenta, queste previsioni errate potrebbero non essere un grosso problema.

  • Quando una previsione è errata, l'oggetto potrebbe dover saltare o scattare indietro sul percorso corretto. Ciò può essere visivamente irritante.

Interpolazione con intervallo di tempo fisso

In Godot, l'intero sistema è definito interpolazione della fisica, ma è anche possibile sentirlo definito come "interpolazione a intervallo di tempo fisso", poiché interpola tra oggetti che si muovono con un intervallo di tempo (tick di fisica al secondo). In un certo senso, il secondo termine è più accurato, perché si può utilizzare anche per interpolare oggetti che non dipendono dalla fisica.

Suggerimento

Sebbene l'interpolazione della fisica sia solitamente una buona scelta, ci sono eccezioni in cui è possibile scegliere di non utilizzare l'interpolazione fisica integrata in Godot (o di usarla in modo limitato). Un esempio sono i giochi multigiocatore su Internet. I giochi multigiocatore spesso ricevono informazioni basate su tick o tempi da altri giocatori o da un server e queste potrebbero non coincidere con i tick di fisica locali, quindi una tecnica di interpolazione personalizzata può spesso essere più adeguata.