Wie kann man alle N Sekunden im Spiel etwas bewirken?

8

Ich möchte, dass alle N Sekunden etwas passiert. Wie würde ich das tun? Vielleicht einen Timer benutzen, aber wie?

user2957632
quelle
Ich habe Ihre Frage allgemeiner gestaltet, da ich denke, dass sie für mehr Menschen auf diese Weise nützlicher ist.
MichaelHouse
Aufgrund der Kommentare zu einer der Antworten glaube ich nicht, dass das Poster nach dem Timer-Bit sucht, sondern wie man den Sprite-Frame ändert. Das hängt mehr davon ab, über welche Plattform wir sprechen. Unabhängig davon, dass die Frage jetzt bearbeitet wurde, repräsentiert sie nicht das, was er wirklich stellt. Java 2D war wichtig für die Frage, die er zu stellen versucht.
Prototyp
Das Ändern des Sprites und das Ausführen alle 5 Sekunden haben nichts miteinander zu tun und sollten als unterschiedliche Fragen gestellt werden. Bitte stellen Sie eine neue Frage zum Ändern des Sprites. Stellen Sie sicher, dass Sie angeben, was Sie versucht haben und was nicht funktioniert.
MichaelHouse

Antworten:

14

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 deltaTimeVariable 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 .

MichaelHouse
quelle
Ist dies in technischer Hinsicht dem Starten eines expliziten Timers wie in @ Joshs Antwort vorzuziehen?
Jack M
@ JackM Das hängt von der Implementierung des Timers ab. Der Grund, warum ich so geantwortet habe, ist, dass es sprachunabhängig und sehr einfach zu implementieren ist.
MichaelHouse
1
@ JackM: Es ist auch vorzuziehen, wenn Sie jemals unter einem Debugger laufen. Die akkumulierte Spielzeit kann anhalten, wenn Sie einen Haltepunkt erreichen, anstatt dass jeder Timer abläuft und sofort ausgelöst wird, wenn Sie fortfahren.
Ben Jackson
Man muss vorsichtig sein, da das Spiel zum Beispiel einfrieren und Zecken verloren gehen können. Daher muss man die Aktion eines Timers für jeden Tick mit dem angegebenen Delta aufrufen, der vergangen ist und nicht nur einmal usw.
Christian Ivicevic
Diese Methode basiert nicht auf dem Zählen von Ticks, sondern auf der Deltazeit. Wenn es einen extra langen Frame gibt, ist auch die Deltazeit extra lang. In vielen Fällen ist es jedoch vorzuziehen, die Deltazeit zu begrenzen. Extra lange Frames können Probleme mit der Physik und dergleichen verursachen. Diese Methode ist dann besser als die Verwendung der aktuellen Zeit abzüglich der Startzeit, da sie besser mit den anderen Berechnungen im Spiel synchronisiert werden kann, wenn die Zeitspanne der Deltazeit begrenzt wird.
MichaelHouse
3

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.

Josh
quelle
1
Gute Antwort. Dies kann in einigen Sprachen (z. B. .NET) zu Threading-Problemen führen, bei denen ein Timer in einem von Ihrer Spieleschleife getrennten Thread implementiert ist.
Asche999
1
Hierbei timeist auch zu beachten, dass für die Speicherung der verstrichenen Spielzeit ein Gleitkommaformat mit doppelter Genauigkeit verwendet werden sollte.
Kevintodisco
Das sind gute Punkte, ich werde sie der Antwort hinzufügen.
Josh
2

Wie bereits in anderen Antworten erwähnt, kann die Implementierung eines Zeitgebers erfolgen, indem ein gespeicherter Wert um jeden Frame erhöht deltaTimeund 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 TimerKlasse, 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 doubleTyp 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, floatwenn 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 timeGetTimeoder 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.

Kevintodisco
quelle
ye danke, aber ich jetzt, wie man das bisschen macht, aber wie würde ich das Sprite hin und her ändern
user2957632
@ user2957632 Das ist nicht das, was deine Frage gerade stellt. Bitte machen Sie es klarer.
Kevintodisco