¿Qué sucede cuando Time.time se vuelve muy grande en Unity?

37

Como se dice aquí :

Tiempo tiempo

La hora al comienzo de este marco (solo lectura). Este es el tiempo en segundos desde el inicio del juego.

Y como sé, el tiempo se almacena en flotador. Entonces, mi pregunta es ¿qué sucederá cuando el valor del tiempo se vuelva muy grande? ¿Se puede desbordar? ¿Perderá precisión y causará errores? ¿El juego se bloqueará o qué?

Yaroslav
fuente
55
@AlexandreVaillancourt Creo que hay una pregunta útil aquí si la interpretamos como "¿Hay problemas cuando Time.time se vuelve muy grande?" en lugar de centrarse literalmente en "desbordamiento" solo. He editado la pregunta en este sentido para tratar de responder a sus comentarios.
DMGregory
2
@DMGregory Pero la pregunta ya fue respondida y aceptada ... Estoy de acuerdo en que sus ediciones hacen una pregunta más útil, solo un poco tarde.
MichaelHouse
Traté de formular la edición para que la respuesta aceptada siga siendo una respuesta correcta a la versión editada. (Después de todo, ya habla sobre problemas de precisión) Sin embargo, no me ofendería si se revierte.
DMGregory

Respuestas:

62

Existe un riesgo real de pérdida de precisión de tiempo al usar un flotador de precisión simple .

Aún conservará la precisión al segundo más cercano durante 97 días . Pero en los juegos a menudo nos importa la precisión en el orden de duración de un cuadro.

Un valor de tiempo de flotación de precisión simple en segundos comienza a perder precisión de milisegundos después de aproximadamente 9 horas .

Eso ya no está fuera del alcance de la posibilidad de una sola sesión de juego, o dejar el juego funcionando "AFK" mientras estás en el trabajo / escuela o durmiendo. (Esta es una de las razones por las que una forma común de probar un juego es ejecutarlo durante la noche y comprobar que todavía funciona correctamente en la mañana)

En las consolas modernas, donde los juegos a menudo se suspenden y se reanudan entre sesiones de juego en lugar de cerrarse por completo, no es inesperado que una "sesión única" desde el punto de vista del juego supere las 9 horas de tiempo de ejecución incluso cuando se juega en ráfagas cortas.

Suponiendo que Unity deltaTimese calcule a partir de una fuente de tiempo de mayor precisión, lo que se confirma mediante los experimentos de Peter en otra respuesta, confiar en los deltaTimetiempos de escala de cuadro es relativamente seguro (solo tenga cuidado de acumular duraciones muy largas sumando deltaTimevalores, agregando pequeños incrementos a Un flotador más grande es una receta clásica para la pérdida de precisión, incluso cuando se compensa con algoritmos inteligentes ).

Dado que fixedDeltaTimemantiene el mismo valor que estableció, en lugar de cambiar dinámicamente de cuadro a cuadro, también puede poner un comportamiento sensible al tiempo en FixedUpdate para obtener garantías más sólidas de consistencia. deltaTimedevolverá automáticamente el delta fijo apropiado en estos métodos. Si bien puede haber una frecuencia de ritmo entre actualizaciones fijas y de cuadro, puede suavizar esto a través de la interpolación .

Lo que desea evitar es calcular las duraciones restando una marca de tiempo de otra . Después de horas de juego, esto puede causar una cancelación catastrófica, lo que lleva a una precisión mucho menor que la que se obtiene al principio de la carrera. Si comparar las marcas de tiempo es importante para sus sistemas, puede generar su propio valor de tiempo de mayor resolución utilizando otros métodos, como en System.Diagnostics.Stopwatchlugar de Time.time.

DMGregory
fuente
Unreal también elige exponer el tiempo de juego como un flotador de precisión simple, tanto en código como en planos . Puede solucionarlo con marcas de tiempo en tiempo real y hacer su propia dilatación de tiempo, lo mismo que haría en Unity. (Solo observe que el tipo de marca de tiempo de Unreal se basa en la época de 32 bits ). Existen extensiones para C # en Unreal si así lo elige.
DMGregory
Para [ms], comienza a perder precisión alrededor de 16 484 - 32 768 bandas. Es decir, puede perder precisión después de 4h30min , después de 9h seguramente no puede confiar en eso.
Xmedeko
Técnicamente, entre 4.5 y 9 horas, tiene una precisión de 0,98 milisegundos. Elegí marcar el punto en el que el error supera los 1,00 milisegundos. Pero el punto está bien entendido: la precisión se degrada a lo largo del camino, por lo que el código que necesita más precisión puede comenzar a comportarse mal incluso antes.
DMGregory
16

El valor máximo de flotación es 3.40282347 * 10 ^ 38, lo que equivale a 10 ^ 31 años cuando se mide en segundos (o 10 ^ 28 años cuando se mide en milisegundos). Confía en mí, no se desbordará.

Lo único que puede aparecer es la inexactitud. Pero un solo número de coma flotante de precisión tiene una precisión de 7-8 dígitos decimales. Si lo usa para medir segundos, es preciso durante aproximadamente 194 días. Si mide milisegundos, es preciso por solo 4,5 horas. Por lo tanto, depende completamente de la precisión que necesita y es posible que necesite encontrar formas alternativas si necesita ser preciso al milisegundo (que probablemente no lo haga).

LukeG
fuente
77
Wolfram Alpha proporciona una idea para establecer el tono de la cantidad de años que vale la pena mencionar: eso es aproximadamente 20 magnitudes más altas que la edad actual del universo. No veinte veces más alto, pero la edad actual más veinte ceros más.
doppelgreener
1
@ Draco18s Depende de la forma en que la unidad mide deltaTime, pero si toman un punto de tiempo para cada cuadro y restan esos dos, supongo que la respuesta es un sí definitivo.
LukeG
77
Esta respuesta no es correcta en absoluto. Puedes hacer un experimento. Puede incrementar el flotador uno por uno en el bucle. Después de llegar a 10 000 000, dejará de aumentar. La razón es que no puede agregar números pequeños a números grandes correctamente cuando se trata de flotante. La respuesta correcta fue dada por un @DMGregory
Gaviota
2
@ Gaviota En ninguna parte escribo sobre el incremento. Es un hecho que el número máximo representable por un flotador es (aproximadamente) 3,4 * 10 ^ 38, por lo que descarta desbordarse en sentido estricto (como se entiende por el OP). No veo por qué la respuesta sería incorrecta como está.
LukeG
2
@ Gaviota Creo que la respuesta de LukeG es correcta (incluso antes de las mejoras en las ediciones): su respuesta y la mía solo se refieren a diferentes clases de necesidades. Me gusta echar un vistazo a la precisión de flotación y limitar los casos, mientras que LukeG analiza el tema de manera más amplia, con menos énfasis en las necesidades de precisión exactas.
DMGregory
12

¿Cuánta precisión necesita tu juego?

Un flotante de 32 bits tiene 24 bits de precisión. En otras palabras, en el tiempo t, la precisión es ± 2 -23 × t. (Esto no es exactamente correcto, pero está cerca, y los detalles exactos no son terriblemente interesantes).

  • Si necesita una precisión de 1 ms, luego de 1 ms ÷ 2 -23 = 8400 s = 2h 20m, un flotador de 32 bits ya no es aceptable. Una precisión de 1 ms no es necesaria para la mayoría de los juegos, pero se considera necesaria para aplicaciones musicales.

  • Si su juego se ejecuta en un monitor de 144 Hz, y desea gráficos con precisión de cuadro, entonces después de 1 ÷ 144 Hz ÷ 2 -23 = 58000 s = 16h, un flotador de 32 bits ya no es aceptable. 16h es mucho tiempo para ejecutar un juego, pero no es inconcebible. Apuesto a que un porcentaje significativo de nosotros hemos jugado un juego durante 16 horas en un solo tramo, incluso solo porque dejamos el juego corriendo y dormimos un poco. En ese punto, las animaciones y las actualizaciones físicas se reducirían a menos de 144Hz.

Si la Unidad Time.deltaTimese calcula a partir de un reloj de mayor precisión, entonces podría usar eso y aún así obtener una precisión completa. No sé si esto es cierto o no.

Nota al margen

Los juegos y otros programas en Windows a menudo usan GetTickCount() para calcular el tiempo. Como usa un número entero de 32 bits para contar milisegundos, se ajusta aproximadamente cada 50 días, si deja su computadora encendida durante tanto tiempo. Sospecho que muchos juegos se colgarían, fallarían o se comportarían de manera extraña si los jugaras en la marca de 50 días.

Los enteros, como los de Windows GetTickCount(), corren el riesgo de desbordarse, mientras que los flotantes, como los de Unity Time.time, corren el riesgo de perder precisión.

Dietrich Epp
fuente
10

Las otras respuestas solo especulan, así que escribí un proyecto simple de Unity y lo dejé correr por un tiempo. Después de un par de horas, este es el resultado:

  • UnityEngine.Time.timepierde precisión con bastante rapidez. Como se esperaba, después de ejecutar el juego durante 4 horas, los valores saltan en 1 milisegundo, y la imprecisión aumenta después de eso.
  • UnityEngine.Time.deltaTimemantiene su precisión inicial de submilisegundos incluso después de que Unity ha estado funcionando durante horas. Por lo tanto, está prácticamente garantizado que deltaTimese deriva de un contador de alta resolución dependiente de la plataforma (que generalmente se desborda después de 10-1000 años). Sigue existiendo un pequeño riesgo que deltaTimeaún perderá precisión en algunas plataformas.

La inexactitud del tiempo es un problema para todo lo que depende UnityEngine.Time.time. Un ejemplo específico es la rotación (por ejemplo, de un molino de viento), que generalmente se basa en UnityEngine.Mathf.Sin(UnityEngine.Time.time*rotationSpeed). Aún más de un problema es_Time que se usa explícitamente para las animaciones de los sombreadores. ¿Qué tan alta debe ser la inexactitud antes de que comience a ser notable? La precisión de 20-30 ms (2 días) debe ser el punto donde las personas que conocen el problema y prestan mucha atención se darán cuenta. A 50-100 ms (5-10 días) las personas sensibles que no conocen el problema comenzarán a resolverlo. De 200 a 300 ms (20 a 30 días) el problema será obvio.

Hasta ahora no he probado la exactitud de _SinTime, _CosTimey Unity_DeltaTime, así que no puedo comentar sobre ellos.

Este es el script que he usado para probar. Se adjunta a un UI.Textelemento, la configuración del reproductor del proyecto permite que el proyecto se ejecute en segundo plano y VSync en la configuración de calidad se establece en cada segundo V en blanco:

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;       
    }
}

ingrese la descripción de la imagen aquíingrese la descripción de la imagen aquíingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí

Si se está preguntando sobre el 0.033203valor, estoy bastante seguro de que es realmente 0.033203125, que tiene una representación binaria de coma flotante 00111101000010000000000000000000. Observe los muchos 0s en la mantisa.

Soluciones alternativas

La mayoría de los géneros no necesitan una solución alternativa. La mayoría de los jugadores ni siquiera juegan un juego durante 100 horas en total, por lo que invertir dinero para solucionar un problema que la gente solo notará después de unos pocos días hipotéticos de tiempo de juego continuo no es económicamente viable. Lamentablemente, no puedo encontrar una solución universal simple que solucione la totalidad del problema sin acarrear algunos inconvenientes significativos.

Peter
fuente