Cosa succede quando Time.time diventa molto grande in Unity?


37

Come si dice qui :

time.time

L'ora all'inizio di questo riquadro (sola lettura). Questo è il tempo in secondi dall'inizio del gioco.

E come so il tempo è memorizzato in float. Quindi, la mia domanda è: cosa accadrà quando il valore del tempo diventa molto grande? Può traboccare? Perderà precisione e causerà bug? Il gioco andrà in crash o cosa?



5
@AlexandreVaillancourt Penso che qui ci sia una domanda utile se la interpretiamo come "Ci sono problemi quando Time.time diventa molto grande?" piuttosto che concentrarsi letteralmente solo sul "trabocco". Ho modificato la domanda in questo modo per cercare di rispondere al tuo feedback.
DMGregory

2
@DMGregory Ma alla domanda è già stata data una risposta ed è stata accettata ... Sono d'accordo che le tue modifiche rendono una domanda più utile, solo un po 'in ritardo.
MichaelHouse

Ho provato a pronunciare la modifica in modo che la risposta accettata sia ancora una risposta corretta alla versione modificata. (Dopotutto, parla già di problemi di precisione) Non mi offenderei se fosse ripristinato però.
DMGregory

Risposte:


62

Esiste un rischio reale di perdita di precisione nel tempo derivante dall'utilizzo di un galleggiante a precisione singola .

Manterrà comunque l'accuratezza al secondo più vicino per 97 giorni . Ma nei giochi ci preoccupiamo spesso della precisione nell'ordine della durata di un frame.

Un valore di tempo di galleggiamento a precisione singola in secondi inizia a perdere la precisione dei millisecondi dopo circa 9 ore .

Questo non è già al di fuori del regno delle possibilità per una singola sessione di gioco, o lasciare il gioco in esecuzione "AFK" mentre sei al lavoro / a scuola o dormendo. (Questo è uno dei motivi per cui un modo comune per testare un gioco è quello di eseguirlo durante la notte e verificare che continui a giocare correttamente al mattino)

Sulle console moderne, in cui i giochi vengono spesso sospesi e ripresi tra le sessioni di gioco anziché essere completamente chiusi, non è inaspettato che una "singola sessione" dal punto di vista del gioco superi le 9 ore di autonomia anche quando si gioca a raffiche brevi.

Supponendo che Unity deltaTimesia calcolato da una fonte di tempo di maggiore precisione, che è confermata dagli esperimenti di Peter in un'altra risposta, fare affidamento su deltaTimetempistiche su scala di fotogrammi è relativamente sicuro (basta essere cauti nell'accumulare durate molto lunghe sommando i deltaTimevalori - aggiungendo piccoli incrementi a un galleggiante più grande è una ricetta classica per la perdita di precisione, anche quando si compensa con algoritmi esperti .

Dal momento che fixedDeltaTimemantiene lo stesso valore impostato, anziché cambiare dinamicamente da un fotogramma all'altro, puoi anche impostare un comportamento sensibile al tempismo in FixedUpdate per ottenere maggiori garanzie di coerenza. deltaTimerestituirà automaticamente il delta fisso appropriato in questi metodi. Mentre può esserci una frequenza di battuta tra aggiornamenti fissi e di frame, è possibile appianare questo attraverso l'interpolazione .

Quello che vuoi evitare è calcolare le durate sottraendo un timestamp da un altro . Dopo ore di gioco ciò può causare una cancellazione catastrofica, portando a una precisione molto minore di quella che si ottiene all'inizio della corsa. Se il confronto dei timestamp è importante per i tuoi sistemi, puoi generare il tuo valore temporale ad alta risoluzione usando altri metodi, come System.Diagnostics.Stopwatchinvece di Time.time.


Unreal sceglie anche di esporre il tempo di gioco come float a precisione singola, sia in codice che in schemi . Puoi aggirarlo con i timestamp in tempo reale e facendo la tua dilatazione del tempo, lo stesso che faresti in Unity. (Fai attenzione al fatto che il tipo di data e ora di Unreal si basa sull'epoca a 32 bit ). Le estensioni esistono per C # in Unreal, se lo desideri.
DMGregory

Per [ms], inizi a perdere precisione intorno a 16 484-32 768 bande. Cioè, potresti perdere la precisione dopo 4h30min , dopo 9h sicuramente non puoi fidarti di questo.
xmedeko,

Tecnicamente tra 4,5-9 ore, hai una precisione di 0,98 millisecondi: ho scelto di contrassegnare il punto in cui l'errore supera 1,00 millisecondi. Ma il punto è ben preso: la precisione diminuisce lungo la strada, quindi il codice che richiede maggiore precisione può iniziare a comportarsi in modo anomalo anche prima.
DMGregory

16

Il valore massimo di float è 3,40282347 * 10 ^ 38, che equivale a 10 ^ 31 anni se misurato in secondi (o 10 ^ 28 anni se misurato in millisecondi). Fidati di me, non traboccerà.

L'unica cosa che può apparire è inesattezza. Ma un singolo numero in virgola mobile di precisione ha una precisione di 7-8 cifre decimali. Se lo si utilizza per misurare i secondi, è preciso per circa 194 giorni. Se si misura millisecondi è preciso solo per 4,5 ore. Quindi dipende completamente dall'accuratezza di cui hai bisogno e potresti dover trovare modi alternativi se devi essere preciso al millisecondo (che probabilmente non lo fai).


7
Wolfram Alpha fornisce un'idea di impostazione del tono di quanti anni potrebbe essere degno di nota: è di circa 20 magnitudini in più rispetto all'età attuale dell'universo. Non venti volte più in alto, ma l'età attuale più altri venti zero.
doppelgreener,

1
@ Draco18s Dipende dal modo in cui l'unità misura deltaTime, ma se prendono un punto temporale per ciascun fotogramma e sottraggono quei due suppongo che la risposta sia un sì definitivo.
LukeG,

7
Questa risposta non è affatto giusta. Puoi eseguire un esperimento. È possibile incrementare il float uno per uno nel loop. Dopo aver raggiunto 10.000.000, smetterà di aumentare. Il motivo è che non è possibile aggiungere correttamente numeri piccoli a numeri grandi quando si ha a che fare con float. La risposta giusta è stata data da un @DMGregory
Seagull

2
@Seagull Da nessuna parte scrivo sull'incremento. È un dato di fatto che il numero massimo rappresentabile da un float è (circa) 3,4 * 10 ^ 38, quindi esclude lo straripamento in senso stretto (come inteso dal PO). Non vedo perché la risposta sia errata come lo è?
LukeG,

2
@Seagull Penso che la risposta di LukeG sia corretta (anche prima dei miglioramenti nelle modifiche) - la sua risposta e la mia si riferiscono solo a diverse classi di esigenze. Mi piace cercare la precisione del float e limitare i casi, mentre LukeG affronta il problema in modo un po 'più ampio, con meno enfasi sulle esigenze di precisione.
DMGregory

12

Di quanta precisione ha bisogno il tuo gioco?

Un float a 32 bit ha 24 bit di precisione. In altre parole, al momento t, la precisione è ± 2-23 × t. (Questo non è esattamente corretto, ma è vicino e i dettagli esatti non sono tremendamente interessanti.)

  • Se è necessaria una precisione di 1 ms, dopo 1ms ÷ 2 -23 = 8400 s = 2h 20m, un float a 32 bit non è più accettabile. Una precisione di 1 ms non è necessaria per la maggior parte dei giochi, ma è considerata necessaria per le applicazioni musicali.

  • Se il tuo gioco gira su un monitor a 144 Hz e desideri una grafica accurata al frame, dopo 1 ÷ 144 Hz ÷ 2 -23 = 58000 s = 16h, un float a 32 bit non è più accettabile. 16h è molto tempo per eseguire un gioco, ma non è inconcepibile. Scommetto che una percentuale significativa di noi ha corso una partita per 16 ore in un solo tratto, anche solo perché abbiamo lasciato il gioco in esecuzione e dormito un po '. A quel punto, le animazioni e gli aggiornamenti di fisica sarebbero stati ridotti a meno di 144Hz.

Se Unity's Time.deltaTime viene calcolato da un orologio di precisione più elevata, è possibile utilizzarlo e ottenere comunque la massima precisione. Non so se questo sia vero o no.

Nota a margine

Giochi e altri programmi su Windows spesso usano GetTickCount()per capire l'ora. Poiché utilizza un numero intero a 32 bit per contare i millisecondi, si avvolge circa ogni 50 giorni, se si lascia il computer così a lungo. Ho il sospetto che molti giochi si blocchino, si schiantino o si comportino in modo bizzarro se li stessi giocando in occasione dei 50 giorni.

I numeri interi, come quelli di Windows GetTickCount(), hanno il rischio di overflow, mentre i float, come quelli di Unity Time.time, hanno il rischio di perdere precisione.


10

Le altre risposte ipotizzano solo, quindi ho scritto un semplice progetto Unity e l'ho lasciato funzionare per un po '. Dopo un paio d'ore questo è il risultato:

  • UnityEngine.Time.timeperde precisione piuttosto rapidamente. Come previsto, dopo aver eseguito il gioco per 4 ore, i valori saltano di 1 millisecondo e successivamente la precisione aumenta.
  • UnityEngine.Time.deltaTimemantiene la precisione iniziale inferiore al millisecondo anche dopo che Unity è in funzione da ore. Quindi è praticamente garantito che deltaTimederivi da un contatore ad alta risoluzione dipendente dalla piattaforma (che di solito trabocca dopo 10-1000 anni). Rimane un piccolo rischio che deltaTimeperderà comunque precisione su alcune piattaforme.

L'inesattezza del tempo è un problema per tutto ciò che dipende UnityEngine.Time.time. Un esempio specifico è la rotazione (ad esempio di un mulino a vento), che di solito si basa UnityEngine.Mathf.Sin(UnityEngine.Time.time*rotationSpeed). Ancora di più è un problema_Time che viene esplicitamente utilizzato per le animazioni dagli shader. Quanto deve essere elevata l'imprecisione prima che inizi a essere evidente? Una precisione di 20-30 ms (2 giorni) dovrebbe essere il punto in cui le persone che sono a conoscenza del problema e prestano molta attenzione, noteranno. A 50-100 ms (5-10 giorni) le persone sensibili che non conoscono il problema inizieranno a capirlo. Da 200-300 ms (20-30 giorni) il problema sarà evidente.

Finora non ho ancora testato l'accuratezza di _SinTime, _CosTimee Unity_DeltaTime, quindi non posso commentare queste.

Questo è lo script che ho usato per i test. È collegato a un UI.Textelemento, le Impostazioni giocatore del progetto consentono l'esecuzione del progetto mentre è in background e VSync in Impostazioni qualità è impostato su Ogni secondo V vuoto:

public class Time : UnityEngine.MonoBehaviour {
    void Start () {
        LastTime = (double)UnityEngine.Time.time;
        StartTime =  System.DateTime.Now;
        LastPrintTime =  System.DateTime.Now;
    }
    double LastTime;
    System.DateTime StartTime;
    System.DateTime LastPrintTime;
    void Update () {
        double deltaTimeError = (double)UnityEngine.Time.deltaTime - ((double)UnityEngine.Time.time - LastTime);
        if (System.DateTime.Now - LastPrintTime  > System.TimeSpan.FromMilliseconds(1000))
        {
            GetComponent<UnityEngine.UI.Text>().text = "StartTime: " + StartTime.ToLongTimeString()
                + "\nCurrentTime: " + System.DateTime.Now.ToLongTimeString()
                + "\n\nTime.deltaTime: " + UnityEngine.Time.deltaTime.ToString("0.000000")
                + "\nTime.time - LastTime: " + ((float)(UnityEngine.Time.time - LastTime)).ToString("0.000000")
                + "\nAbsolute Error: " +  System.Math.Abs(deltaTimeError).ToString("0.000E0");
            LastPrintTime = System.DateTime.Now;
        }
        LastTime = UnityEngine.Time.time;       
    }
}

inserisci qui la descrizione dell'immagineinserisci qui la descrizione dell'immagineinserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

Se ti stai chiedendo quale sia il 0.033203valore, sono abbastanza sicuro che in realtà 0.033203125abbia una rappresentazione binaria in virgola mobile 00111101000010000000000000000000. Notare le molte 0s nella mantissa.

soluzioni alternative

La maggior parte dei generi non ha bisogno di una soluzione alternativa. La maggior parte dei giocatori non giocherà nemmeno una partita per 100 ore totali, quindi investire denaro per risolvere un problema che le persone noteranno solo dopo un ipotetico paio di giorni di gioco continuo non è economicamente praticabile. Purtroppo non riesco a trovare una semplice soluzione universale che risolva l'intero problema senza portare con sé alcuni svantaggi significativi.

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.