Ich möchte, dass alle N Sekunden etwas passiert. Wie würde ich das tun? Vielleicht einen Timer benutzen, aber wie?
Ich möchte, dass alle N Sekunden etwas passiert. Wie würde ich das tun? Vielleicht einen Timer benutzen, aber wie?
Antworten:
Ein üblicher Ansatz hierfür ist die Verwendung der Aktualisierungsschleife und der Deltazeit.
float timerCurrent = 0f;
float timerTotal = 5f;
Update() {
timerCurrent += deltaTime;
if(timerCurrent >= timerTotal) {
//do timer action
timerCurrent -= timerTotal;
}
}
Dies setzt voraus, dass Sie Zugriff auf eine deltaTime
Variable haben. Die Deltazeit ist einfach die Zeit, die seit dem letzten Frame vergangen ist. Viele Motoren müssen diese zur Verfügung, oder Sie können Ihre eigenen erfahren Sie mehr über das Einrichten von hier .
In den meisten Sprachen müssen Sie die Uhr mit einer Art Funktionsaufruf nach dem Vorbild clock.startTimer()
der Hauptschleife starten . Dann müssen Sie den Uhrzeitwert speichern, bevor Sie die Hauptschleife in eine Variable mit einer Funktion wie eingeben time = clock.getTime()
. Speichern Sie Ihre Schrittzeit in Variable n
.
In Ihrer Hauptschleife ist die Prüfung, die Sie durchführen möchten, etwas in der Art von
if(time + n <= clock.getTime())
//do stuff
time = clock.getTime() //important to assign new time value here
Hinweis: Ich bin mir nicht sicher, welche Sprache / Bibliothek Sie sich ansehen. Dies ist also nur ein generischer Algorithmus, an den ich auf Anhieb gedacht habe. Es entspricht möglicherweise nicht genau Ihren Anforderungen. In einigen Sprachen / Bibliotheken müssen Sie ein Uhrobjekt erstellen, in anderen können Sie einfach direkt einen Funktionsaufruf durchführen.
Gemäß den Kommentaren unten müssen Sie bei Threading-Problemen vorsichtig sein, je nachdem, welche Sprache Sie verwenden. Sie sollten zum Speichern auch einen doppelten Schwimmer verwenden time
.
time
ist auch zu beachten, dass für die Speicherung der verstrichenen Spielzeit ein Gleitkommaformat mit doppelter Genauigkeit verwendet werden sollte.
Wie bereits in anderen Antworten erwähnt, kann die Implementierung eines Zeitgebers erfolgen, indem ein gespeicherter Wert um jeden Frame erhöht deltaTime
und mit der erwarteten Dauer verglichen wird. Der Vollständigkeit halber werde ich Code einfügen, der den anderen Antworten sehr ähnlich ist:
float elapsed = 0.0f;
float duration = 4.0f;
void update(float deltaTime) {
elapsed += deltaTime;
if (elapsed <= duration) {
// Run code.
elapsed = 0.0f;
// Maybe remove from update loop?
}
}
Wie Sie mit der // Run code.
Portion umgehen möchten, liegt bei Ihnen. Vielleicht haben Sie eine Timer
Klasse, von der eine Instanz ein delegate
(C #) oder ein callback
(C ++) nimmt und sie aufruft, wenn der Timer abläuft. Wenn Sie den Timer anhalten, müssen Sie ihn auch aus der Aktualisierungsschleife entfernen.
Ein alternativer Ansatz besteht darin, die Startzeit des Timers zu markieren und stattdessen eine On-Demand-Berechnung der verstrichenen Zeit durchzuführen:
double startTime = 0.0; // This is a double for a reason, I'll explain below.
bool running = false;
void start() {
startTime = getTime(); // This is whatever system time call you want to use.
running = true;
}
double getElapsed() {
double elapsed = getTime() - startTime;
return elapsed;
}
Dieser Stil gibt dem Benutzer des Timers etwas mehr Kontrolle. Die Struktur selbst befasst sich nicht damit, was zu tun ist, wenn die verstrichene Zeit einen bestimmten Punkt erreicht. es wird nicht einmal aktualisiert. Der Benutzer kann einfach die verstrichene Zeit abfragen, wenn sie benötigt wird. Dieses Muster hat den unglücklichen Nebeneffekt, dass es nicht debuggerfreundlich ist. Die Systemzeit läuft weiter, da Ihr Programm in einem Debugger angehalten wird, sodass sich Timer wie dieser beim Durchlaufen von Programmen anders verhalten. Eine mögliche Lösung für das ist , einen programmspezifischen verstrichenen Zeitwert zu erhalten , die für jeden Rahmen unter Verwendung von Systemzeit - Delta aktualisiert wird.
Ich denke jedoch, dass zwei Macken des Timings auf einem Computer am wichtigsten sind, insbesondere wenn es um die Spieleentwicklung geht. Die erste ist die Gleitkommapräzision und die zweite ist die native Timerauflösung.
Sie werden feststellen, dass ich im zweiten Beispiel einen double
Typ anstelle von a verwendet habe float
, wie im ersten Beispiel (der Typname hängt natürlich von der Sprache ab, die Sie verwenden). Der Grund dafür ist, dass das zweite Beispiel das Speichern der gesamten verstrichenen Spielzeit ist . Wenn das Spiel sehr lange läuft, haben Gleitkomma-Formatwerte mit einfacher Genauigkeit keine ausreichende Genauigkeit, um die verstrichene Zeit genau zu messen , was zu Stabilitätsproblemen führt. Das erste Beispiel ist in Ordnung, float
wenn der Typ verwendet wird, solange keine großen Dauern erwartet werden.
Wenn die erwartete Aktualisierungszeit am anderen Ende des Spektrums klein genug ist, können Sie sie möglicherweise zunächst nicht erreichen, je nachdem, wie Sie die Systemzeit erhalten. Timer hängen häufig von der Timerauflösung des Betriebssystems ab, auf dem Sie ausgeführt werden. Die Standard-Timer-Auflösung von Windows 7 und niedriger beträgt beispielsweise 15,6 ms . Dies bedeutet, dass die niedrigste Timer-Genauigkeit, die Sie mit Funktionen wie timeGetTime
oder erreichen können GetTickCount
, 15,6 ms beträgt, sofern Sie nicht die Timer-Auflösung ändern (damit sind auch Gefahren verbunden).
Nun, das hat sich als etwas lang herausgestellt, aber dies sind wichtige Dinge, die Sie bei der Implementierung von Timern beachten sollten.