Was passiert, wenn Time.time in Unity sehr groß wird?

37

Wie hier gesagt wird :

Zeit Zeit

Die Zeit am Anfang dieses Frames (schreibgeschützt). Dies ist die Zeit in Sekunden seit dem Start des Spiels.

Und wie ich weiß, ist die Zeit in float gespeichert. Meine Frage ist also, was passiert, wenn der Wert der Zeit sehr groß wird? Kann es überlaufen? Wird es an Präzision verlieren und Fehler verursachen? Wird das Spiel einfach abstürzen oder was?

Jaroslaw
quelle
5
@AlexandreVaillancourt Ich denke, es gibt hier eine nützliche Frage, wenn wir sie als "Gibt es Probleme, wenn Time.time sehr groß wird?" Interpretieren. im Gegensatz zu wörtlich auf "Überlauf" allein zu konzentrieren. Ich habe die Frage in diesem Sinne bearbeitet, um Ihr Feedback zu berücksichtigen.
DMGregory
2
@DMGregory Aber die Frage wurde bereits beantwortet und akzeptiert ... Ich bin damit einverstanden, dass Ihre Änderungen zu einer nützlicheren Frage führen, nur ein bisschen zu spät.
MichaelHouse
Ich habe versucht, die Bearbeitung so zu formulieren, dass die akzeptierte Antwort immer noch eine korrekte Antwort auf die bearbeitete Version ist. (Immerhin spricht es bereits über Präzisionsprobleme.) Ich würde mich nicht ärgern, wenn es zurückgesetzt würde.
DMGregory

Antworten:

62

Bei Verwendung eines Schwimmers mit einfacher Genauigkeit besteht die Gefahr eines Zeitverlusts .

Die Genauigkeit bleibt 97 Tage lang auf die nächste Sekunde genau . Aber in Spielen ist uns oft die Genauigkeit in der Größenordnung einer Frame-Dauer wichtig.

Ein Gleitzeitwert mit einfacher Genauigkeit in Sekunden beginnt nach etwa 9 Stunden an Millisekundengenauigkeit zu verlieren .

Dies ist bereits jetzt nicht mehr möglich, wenn Sie nur eine einzige Sitzung spielen oder das Spiel "AFK" laufen lassen, während Sie in der Arbeit / Schule sind oder schlafen. (Dies ist einer der Gründe, warum ein Spiel häufig über Nacht getestet wird, indem überprüft wird, ob es morgens noch korrekt abgespielt wird.)

Auf modernen Konsolen, auf denen Spiele häufig zwischen den Spielsitzungen angehalten und fortgesetzt werden, anstatt vollständig herunterzufahren, ist es aus Sicht des Spiels nicht unvorhergesehen, dass eine "einzelne Sitzung" 9 Stunden Laufzeit überschreitet, selbst wenn sie in kurzen Serien gespielt wird.

Unter der Annahme, dass deltaTimeUnitys aus einer Zeitquelle mit höherer Genauigkeit berechnet wird, was in Peters Experimenten in einer anderen Antwort bestätigt wird, deltaTimeist es relativ sicher , sich auf Frame-Scale-Timings zu stützen (seien Sie nur vorsichtig, wenn Sie sehr lange Zeiträume durch Summieren von deltaTimeWerten akkumulieren - indem Sie kleine Inkremente hinzufügen Ein größerer Schwimmer ist ein klassisches Rezept für Präzisionsverluste, auch wenn Sie dies mit ausgeklügelten Algorithmen kompensieren .

Da fixedDeltaTimeder von Ihnen festgelegte Wert beibehalten wird, anstatt sich dynamisch von Frame zu Frame zu ändern, können Sie auch zeitabhängiges Verhalten in FixedUpdate festlegen, um stärkere Garantien für die Konsistenz zu erhalten. deltaTimegibt bei diesen Methoden automatisch das entsprechende feste Delta zurück. Zwar kann es eine Taktfrequenz zwischen festen und Frame-Aktualisierungen geben, Sie können dies jedoch durch Interpolation ausgleichen .

Was Sie vermeiden möchten, ist die Berechnung der Dauer durch Subtrahieren eines Zeitstempels von einem anderen . Nach stundenlangem Spielen kann dies zu einem katastrophalen Abbruch führen, was zu einer weitaus geringeren Präzision führt, als Sie es zu Beginn des Laufs erreichen. Wenn der Vergleich von Zeitstempeln für Ihre Systeme wichtig ist, können Sie Ihren eigenen Zeitwert mit höherer Auflösung mithilfe anderer Methoden wie System.Diagnostics.Stopwatchanstelle von generieren Time.time.

DMGregory
quelle
Unreal legt die Spielzeit auch als Gleitkomma mit einfacher Genauigkeit in Code und Blaupausen offen . Sie können es mit Zeitstempeln in Echtzeit umgehen und Ihre eigene Zeitdilatation durchführen, genau wie Sie es in Unity tun würden. (Beachten Sie, dass der Zeitstempeltyp von Unreal auf der 32-Bit-Epoche basiert .) Es gibt Erweiterungen für C # in Unreal, wenn Sie dies wünschen.
DMGregory
Bei [ms] verlieren Sie die Genauigkeit im Bereich von 16 484 - 32 768. Das heißt, Sie können die Präzision nach 4h30min verlieren , nach 9h können Sie dem sicherlich nicht vertrauen.
Xmedeko
Technisch gesehen haben Sie zwischen 4,5 und 9 Stunden eine Genauigkeit von 0,98 Millisekunden. Ich habe mich dafür entschieden, den Punkt zu markieren, an dem der Fehler 1,00 Millisekunden überschreitet. Aber der Punkt ist gut verstanden - die Präzision nimmt auf dem ganzen Weg ab, sodass Code, der mehr Präzision benötigt, sich möglicherweise noch früher schlecht verhält.
DMGregory
16

Der maximale Wert von float ist 3.40282347 * 10 ^ 38, was 10 ^ 31 Jahren entspricht, gemessen in Sekunden (oder 10 ^ 28 Jahren, gemessen in Millisekunden). Vertrauen Sie mir, es wird nicht überlaufen.

Das einzige, was auftauchen kann, ist Ungenauigkeit. Eine Gleitkommazahl mit einfacher Genauigkeit hat jedoch eine Genauigkeit von 7-8 Dezimalstellen. Wenn Sie es zum Messen von Sekunden verwenden, ist es ungefähr 194 Tage lang genau. Wenn Millisekunden gemessen werden, ist die Genauigkeit nur 4,5 Stunden lang. Es hängt also ganz von der Genauigkeit ab, die Sie benötigen, und Sie müssen möglicherweise alternative Wege finden, um auf die Millisekunde genau zu sein (was Sie wahrscheinlich nicht tun).

LukeG
quelle
7
Wolfram Alpha liefert eine tonangebende Vorstellung davon, wie groß eine Anzahl von Jahren ist, die erwähnenswert sein könnte: Das sind ungefähr 20 Größenordnungen höher als das aktuelle Alter des Universums. Nicht zwanzig mal höher, aber das aktuelle Alter und zwanzig Nullen.
doppelgreener
1
@Draco18s Hängt davon ab, wie Unity DeltaTime misst, aber wenn sie einen Zeitpunkt für jeden Frame nehmen und diese beiden subtrahieren, ist die Antwort vermutlich ein definitives Ja.
LukeG
7
Diese Antwort ist überhaupt nicht richtig. Sie können ein Experiment durchführen. Sie können float nacheinander in der Schleife erhöhen. Nach Erreichen von 10 000 000 wird das Inkrementieren beendet. Der Grund ist, dass Sie kleine Zahlen nicht korrekt zu großen Zahlen hinzufügen können, wenn Sie mit Float arbeiten. Die richtige Antwort gab @DMGregory
Seagull
2
@Seagull Nirgendwo schreibe ich über das Inkrementieren. Es ist eine Tatsache, dass die maximale Zahl, die ein Float darstellen kann, (ungefähr) 3,4 * 10 ^ 38 ist, so dass ein Überlaufen im engeren Sinne (wie vom OP gemeint) ausgeschlossen ist. Ich verstehe nicht, warum die Antwort so falsch ist, wie sie steht?
LukeG
2
@Seagull Ich denke, LukeGs Antwort ist richtig (sogar vor den Verbesserungen in den Bearbeitungen) - seine Antwort und meine beziehen sich nur auf verschiedene Klassen von Bedürfnissen. Ich beschäftige mich gerne mit Float-Präzision und Grenzfällen, während LukeG das Thema etwas ausführlicher betrachtet, wobei der Schwerpunkt weniger auf genauen Präzisionsanforderungen liegt.
DMGregory
12

Wie viel Präzision braucht dein Spiel?

Ein 32-Bit-Float hat eine Genauigkeit von 24 Bit. Mit anderen Worten, zum Zeitpunkt t beträgt die Genauigkeit ± 2 - 23 × t. (Dies ist nicht genau richtig, aber es ist nah, und die genauen Details sind nicht besonders interessant.)

  • Wenn Sie eine Genauigkeit von 1 ms benötigen , ist ein 32-Bit-Float nach 1 ms s 2 -23 = 8400 s = 2h 20m nicht mehr akzeptabel. Eine Genauigkeit von 1 ms ist für die meisten Spiele nicht erforderlich, wird jedoch für Musikanwendungen als erforderlich angesehen.

  • Wenn Ihr Spiel läuft auf einem 144 Hz - Monitor, und Sie framegenaue Grafiken wollen, dann nach 1 ÷ 144 Hz ÷ 2 -23 = 58000 s = 16h, ein 32-Bit - Float nicht mehr akzeptabel ist . 16h ist eine lange Zeit, um ein Spiel zu starten, aber es ist nicht unvorstellbar. Ich wette, ein signifikanter Prozentsatz von uns hat 16 Stunden lang ein Spiel in einem Durchgang gespielt - auch wenn wir nur das Spiel verlassen haben und etwas geschlafen haben. Zu diesem Zeitpunkt würden Animationen und Aktualisierungen der Physik auf unter 144 Hz reduziert.

Wenn Unity Time.deltaTimeaus einer Uhr mit höherer Genauigkeit berechnet wird, können Sie diese verwenden und trotzdem die volle Genauigkeit erhalten. Ich weiß nicht, ob das stimmt oder nicht.

Randnotiz

Spiele und andere Programme unter Windows ermitteln häufig GetTickCount()die Uhrzeit. Da für die Zählung von Millisekunden eine 32-Bit-Ganzzahl verwendet wird, wird etwa alle 50 Tage ein Zeilenumbruch durchgeführt, wenn Sie Ihren Computer so lange eingeschaltet lassen. Ich vermute, dass sich viele Spiele auf bizarre Weise aufhängen, abstürzen oder schlecht benehmen würden, wenn Sie sie an der 50-Tage-Marke spielen würden.

Ganzzahlen wie bei Windows GetTickCount()können überlaufen, während bei Floats wie bei Unity die Time.timeGefahr besteht, dass sie an Genauigkeit verlieren.

Dietrich Epp
quelle
10

Die anderen Antworten spekulieren nur, deshalb habe ich ein einfaches Unity-Projekt geschrieben und es eine Weile laufen lassen. Nach ein paar Stunden ist dies das Ergebnis:

  • UnityEngine.Time.timeverliert ziemlich schnell an Genauigkeit. Wie erwartet, springen die Werte nach 4-stündigem Ausführen des Spiels um 1 Millisekunde, und die Ungenauigkeit nimmt danach zu.
  • UnityEngine.Time.deltaTimebehält seine anfängliche Genauigkeit von unter einer Millisekunde, selbst nachdem Unity stundenlang ausgeführt wurde. Somit ist praktisch garantiert, dass deltaTimevon einem plattformabhängigen hochauflösenden Zähler (der in der Regel nach 10-1000 Jahren überläuft) abgeleitet wird. Es bleibt ein winziges Risiko, deltaTimedas auf einigen Plattformen immer noch an Präzision verliert.

Die Ungenauigkeit der Zeit ist ein Problem für alles, worauf es ankommt UnityEngine.Time.time. Ein konkretes Beispiel ist die Rotation (z. B. einer Windmühle), auf der in der Regel basiert UnityEngine.Mathf.Sin(UnityEngine.Time.time*rotationSpeed). Ein noch größeres Problem wird _Timeexplizit für Animationen von Shadern verwendet. Wie hoch muss die Ungenauigkeit sein, bevor sie spürbar wird? Die Genauigkeit von 20-30 ms (2 Tage) sollte der Punkt sein, an dem Personen, die sich des Problems bewusst sind und genau darauf achten, es bemerken werden. Bei 50-100 ms (5-10 Tage) werden sensible Leute, die nicht über das Problem Bescheid wissen, anfangen, es herauszufinden. Ab 200-300 ms (20-30 Tage) ist das Problem offensichtlich.

Bisher habe ich die Genauigkeit von _SinTime, _CosTimeund noch nicht getestet Unity_DeltaTime, daher kann ich dazu noch keinen Kommentar abgeben.

Dies ist das Skript, das ich zum Testen verwendet habe. Es ist an ein UI.TextElement angehängt . In den Player-Einstellungen des Projekts kann das Projekt im Hintergrund ausgeführt werden. In den Qualitätseinstellungen ist VSync auf Every Second V Blank festgelegt:

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

Bildbeschreibung hier eingebenBildbeschreibung hier eingebenBildbeschreibung hier eingeben Bildbeschreibung hier eingeben

Wenn Sie sich über den 0.033203Wert wundern , bin ich mir ziemlich sicher, dass dies tatsächlich der Fall ist 0.033203125, für den eine binäre Gleitkommadarstellung vorliegt 00111101000010000000000000000000. Beachten Sie die vielen 0s in der Mantisse.

Problemumgehungen

Die meisten Genres benötigen keine Problemumgehung. Die meisten Spieler werden nicht einmal 100 Stunden lang ein Spiel spielen, weshalb es wirtschaftlich nicht rentabel ist, Geld zu investieren, um ein Problem zu beheben. Leider kann ich keine einfache, universelle Problemumgehung finden, die das gesamte Problem behebt, ohne einige wesentliche Nachteile mit sich zu bringen.

Peter
quelle