Que se passe-t-il lorsque Time.time devient très important dans Unity?

37

Comme il est dit ici :

Temps temps

L'heure au début de cette image (lecture seule). C'est le temps en secondes depuis le début de la partie.

Et comme je sais, le temps est stocké dans float. Donc, ma question est ce qui se passera quand la valeur du temps devient très grande? Peut-il déborder? Va-t-il perdre de la précision et causer des bugs? Le jeu va-t-il s'effondrer ou quoi?

Yaroslav
la source
6
Juste pour le contexte, voir le record du monde Guinness sur le plus long marathon de jeux vidéo .
Théraot
5
@AlexandreVaillancourt Je pense qu'il y a une question utile ici si nous l'interprétons comme suit: "Y a-t-il des problèmes lorsque Time.time devient très grand?" par opposition à se concentrer littéralement sur le "débordement" seul. J'ai modifié la question en fonction de ces lignes pour essayer de répondre à vos commentaires.
DMGregory
2
@DMGregory Mais la question a déjà été répondue et acceptée ... Je conviens que vos modifications en font une question plus utile, juste un peu en retard.
MichaelHouse
J'ai essayé de reformuler l'édition afin que la réponse acceptée reste une réponse correcte à la version modifiée. (Après tout, cela parle déjà de problèmes de précision) Je ne m'offusquerais pas si c'est annulé.
DMGregory

Réponses:

62

L'utilisation d'un flottant à simple précision présente un réel risque de perte de précision temporelle .

Il conservera toujours la précision à la seconde près pendant 97 jours . Mais dans les jeux, la précision dépend souvent de la durée d'une image.

Une valeur de temps flottant simple précision en secondes commence à perdre la précision de la milliseconde après environ 9 heures .

Ce n'est déjà pas possible si une seule session de jeu est possible ou de laisser le jeu en cours d'exécution "AFK" pendant que vous êtes au travail / à l'école ou en train de dormir. (C’est l’une des raisons pour lesquelles un moyen courant de tester un jeu consiste à le lancer du jour au lendemain et à s’assurer qu’il se joue toujours correctement le matin)

Sur les consoles modernes, où les jeux sont souvent suspendus et repris entre les sessions de jeu plutôt que d'être complètement arrêtés, il n'est pas surprenant qu'une "session unique" vue du jeu dépasse 9 heures de temps d'exécution, même si elle est jouée en rafales brèves.

En supposant que l'Unity deltaTimesoit calculé à partir d'une source de temps d'une plus grande précision, ce que corroborent les expériences de Peter dans une autre réponse; il deltaTimeest relativement sûr de s'en remettre à des minuteries d'échelle (vous devez simplement éviter de cumuler des deltaTimevaleurs très longues en ajoutant un grand flottant est une recette classique pour la perte de précision, même lorsque vous compensez avec des algorithmes avertis ).

Dans la mesure où fixedDeltaTimeconserve la même valeur que vous définissez, plutôt que de changer dynamiquement d'une image à l'autre, vous pouvez également définir un comportement sensible au minutage dans FixedUpdate pour obtenir des garanties de cohérence plus solides. deltaTimerenverra automatiquement le delta fixe approprié dans ces méthodes. Bien qu'il puisse y avoir une fréquence de battement entre les mises à jour fixes et les mises à jour d'image, vous pouvez la lisser par interpolation .

Ce que vous voulez éviter, c'est calculer des durées en soustrayant un horodatage d'un autre . Après des heures de jeu, cela peut entraîner une annulation catastrophique et une précision bien inférieure à celle que vous obtenez au début de la course. Si la comparaison des horodatages est importante pour vos systèmes, vous pouvez générer votre propre valeur de temps de résolution supérieure en utilisant d'autres méthodes, comme System.Diagnostics.Stopwatchau lieu de Time.time.

DMGregory
la source
Unreal choisit également d'exposer le temps de jeu sous forme de float à simple précision, dans le code et les plans . Vous pouvez le contourner avec des horodatages en temps réel et en faisant votre propre dilatation, comme vous le feriez dans Unity. (Veillez simplement à ce que le type d'horodatage d'Unreal soit basé sur l' époque 32 bits ). Des extensions existent pour C # dans Unreal si vous le souhaitez.
DMGregory
Pour [ms], vous commencez à perdre de la précision autour de la bande 16 484 - 32 768. C'est-à-dire que vous pouvez perdre la précision après 4h30 , après 9h, vous ne pouvez certainement pas vous fier à cela.
xmedeko
Techniquement, entre 4,5 et 9 heures, la précision est de 0,98 millisecondes. J'ai choisi d'indiquer le point auquel l'erreur dépasse 1,00 millisecondes. Mais le point est bien pris - la précision se dégrade tout au long du processus, de sorte que le code qui a besoin de plus de précision peut commencer à se comporter mal, même plus tôt.
DMGregory
16

La valeur maximale de float est 3,40282347 * 10 ^ 38, ce qui correspond à 10 ^ 31 ans en secondes (ou à 10 ^ 28 ans en millisecondes). Croyez-moi, ça ne va pas déborder.

La seule chose qui peut apparaître est l'inexactitude. Mais un nombre à virgule flottante simple précision a une précision de 7 à 8 chiffres décimaux. Si vous l'utilisez pour mesurer les secondes, il est précis pendant environ 194 jours. Si vous mesurez des millisecondes, elle n’est précise que pour 4,5 heures. Cela dépend donc complètement de la précision dont vous avez besoin et vous devrez peut-être trouver d'autres moyens si vous devez être précis à la milliseconde près (ce que vous n'avez probablement pas).

LukeG
la source
7
Wolfram Alpha donne une idée précise du nombre d'années qu'il convient de mentionner: c'est environ 20 magnitudes de plus que l'âge actuel de l'univers. Pas vingt fois plus haut, mais l'âge actuel plus vingt autres zéros.
doppelgreener
1
@ Draco18s Dépend de la façon dont l'unité mesure deltaTime, mais si elles prennent un point de temps pour chaque image et soustraient ces deux, je suppose que la réponse est un oui catégorique.
LukeG
7
Cette réponse n'est pas juste du tout. Vous pouvez lancer une expérience. Vous pouvez incrémenter les flotteurs un par un dans la boucle. Après avoir atteint 10 000 000, il cessera d’augmenter. La raison en est que vous ne pouvez pas ajouter correctement de petits nombres à de grands nombres lorsque vous traitez avec float. La bonne réponse a été donnée par un @DMGregory
Seagull
2
@ Seagull Nulle part je n'écris sur l'incrémentation. C’est un fait que le nombre maximum pouvant être représenté par un float est d’environ 3,4 * 10 ^ 38, de sorte que les règles débordent au sens strict (comme l’entend le PO). Je ne vois pas pourquoi la réponse serait inexacte en l'état.
LukeG
2
@Seagull Je pense que la réponse de LukeG est correcte (même avant les améliorations apportées aux modifications) - sa réponse et la mienne font simplement référence à différentes classes de besoins. J'aime m'intéresser à la précision du flottant et aux cas limites, tandis que LukeG aborde la question un peu plus largement, en mettant moins l'accent sur les besoins précis en précision.
DMGregory
12

De combien de précision votre jeu a-t-il besoin?

Un float de 32 bits a une précision de 24 bits. En d'autres termes, à l'instant t, la précision est de ± 2 -23 × t. (Ce n'est pas tout à fait correct, mais c'est proche, et les détails exacts ne sont pas très intéressants.)

  • Si vous avez besoin d'une précision de 1 ms, après 1 ms ÷ 2 -23 = 8400 s = 2h 20m, un float de 32 bits n'est plus acceptable. Une précision de 1 ms n'est pas nécessaire pour la plupart des jeux, mais elle est considérée comme nécessaire pour les applications musicales.

  • Si votre jeu fonctionne sur un moniteur de 144 Hz et que vous souhaitez des graphiques précis, alors après 1 1 144 Hz 2 -23 = 58000 s = 16h, un float 32 bits n'est plus acceptable. 16h, c'est long de faire tourner une partie, mais ce n'est pas inconcevable. Je parie qu'un pourcentage important d'entre nous avons couru un match pendant 16 heures en un seul passage - même si nous avons laissé le match en cours d'exécution et avons obtenu un peu de sommeil. À ce stade, les animations et les mises à jour physiques seraient réduites à moins de 144Hz.

Si celle-ci Time.deltaTimeest calculée à partir d'une horloge de précision supérieure, vous pouvez l'utiliser et obtenir une précision optimale. Je ne sais pas si c'est vrai ou pas.

Note latérale

Les jeux et autres programmes sur Windows utilisent souvent GetTickCount()pour déterminer l'heure. Puisqu'il utilise un entier 32 bits pour compter les millisecondes, il tourne autour de tous les 50 jours environ, si vous laissez votre ordinateur trop longtemps. Je soupçonne que beaucoup de jeux se bloqueraient, se bloqueraient ou se conduiraient de manière bizarre si vous les jouiez à la marque des 50 jours.

Les entiers, comme Windows GetTickCount(), risquent de déborder, alors que les flottants, comme ceux de Unity Time.time, risquent de perdre de la précision.

Dietrich Epp
la source
10

Les autres réponses ne font que spéculer, alors j’ai écrit un simple projet Unity et l’a laissé fonctionner pendant un moment. Après quelques heures, voici le résultat:

  • UnityEngine.Time.timeperd de la précision assez rapidement. Comme prévu, après 4 heures de jeu, les valeurs sautent de 1 milliseconde et l’imprécision augmente par la suite.
  • UnityEngine.Time.deltaTimeconserve sa précision initiale inférieure à la milliseconde même après que Unity a fonctionné pendant des heures. Ainsi, il est pratiquement garanti que le résultat deltaTimeprovient d'un compteur haute résolution dépendant de la plate-forme (qui déborde généralement après 10 à 1000 ans). Il reste un petit risque qui deltaTimeperdra encore de la précision sur certaines plates-formes.

L’inexactitude de Time est un problème pour tout ce qui en dépend UnityEngine.Time.time. Un exemple spécifique est la rotation (par exemple d’un moulin à vent), qui est généralement basée sur UnityEngine.Mathf.Sin(UnityEngine.Time.time*rotationSpeed). Un problème encore plus important est celui _Timequi est explicitement utilisé pour les animations par les shaders. Quelle doit être l'inexactitude avant de commencer à être perceptible? Une précision de 20-30 ms (2 jours) devrait être le point où les personnes qui sont au courant du problème et qui portent une attention particulière le remarqueront. À 50-100 ms (5-10 jours), les personnes sensibles qui ne connaissent pas le problème commenceront à le comprendre. A partir de 200-300 ms (20-30 jours), le problème sera évident.

Jusqu'à présent , je ne l' ai pas testé l'exactitude des _SinTime, _CosTimeet Unity_DeltaTime, donc je ne peux pas commenter ces.

C'est le script que j'ai utilisé pour tester. Il est associé à un UI.Textélément, les paramètres de lecteur du projet permettent au projet de s'exécuter en arrière-plan et VSync dans les paramètres de qualité est défini sur Toutes les secondes vide.

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

enter image description hereenter image description hereenter image description here enter image description here

Si vous vous interrogez sur la 0.033203valeur, je suis à peu près certaine que c'est en 0.033203125fait une représentation binaire à virgule flottante 00111101000010000000000000000000. Notez les nombreux 0s dans la mantisse.

Solutions de contournement

La plupart des genres n'ont pas besoin d'une solution de contournement. La plupart des joueurs ne joueront même pas un jeu pendant 100 heures au total. Investir de l'argent pour résoudre un problème que les gens ne remarqueront donc qu'après quelques jours hypothétiques de temps de jeu continu est économiquement non viable. Malheureusement, je ne parviens pas à trouver une solution de contournement universelle simple qui résout le problème dans son ensemble sans entraîner des inconvénients importants.

Peter
la source