[Spieleprogrammierung] - Verständnissfragen zur Game-Loop

Jack159

Lieutenant
Registriert
Dez. 2011
Beiträge
766
Hallo,

Ich habe mir mal eine übliche Game-Loop aus einem Tutorial (Setting up the JFrame - YouTube) herausgepickt und habe dazu aber noch ein paar Verständnissfragen.
Die Fragen habe ich direkt in den Code reinkommentiert:

Code:
public class Game {

	public static void main(String[] args) {
		
		/*
		 * Wir wollen hier 60 Ticks pro Sekunde erreichen
		 */
		
		long lastTime = System.nanoTime();

		double nsPerTick = 1000000000D / 60D;    // speichert die Nanosekunden, die wir pro Tick im Schnitt brauchen müssen, um auf 60 Ticks pro Sekunde zu kommen
		
                int ticks = 0;
		int frames = 0;
		
		long lastTimer = System.currentTimeMillis();
		
                double delta = 0;								// soll wohl die Dauer des letzten Schleifendurchlaufes festhalten?!

		while(true) {
			long now = System.nanoTime();
			
			/*
			 *  "now - lastTime" = Dauer des letzten Schleifendurchlaufes, das ist klar.
			 *  Aber wieso wird hier nun noch durch nsPerTick geteilt? Was will man damit erreichen?
			 */
			delta += (now - lastTime) / nsPerTick;  
			
			lastTime = now;	
			
                        boolean shouldRender = true;		// Kann man später auf false setzen um auf 60FPS zu beschränken
			
			
			/* Wann genau soll diese Schleife hier durchlaufen werden? Also wozu die Prüfung "delta >= 1" ?
			 * Ziel ist doch offenbar, dass diese Schleife 60x pro Sekunde durchlaufen wird oder? 
			 */
			while(delta >= 1) {		
				
                                ticks++;
				tick();
				
                               delta -= 1;			// Wieso reduziert man delta um 1? Kann man delta nicht direkt gleich 0 setzen?
				
                               shouldRender=true;
			}
			
			try {
				Thread.sleep(2);					// Damit das System nicht voll ausgelastet wird
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
			if(shouldRender) {
				frames++;
				render();
			}
				
			if(System.currentTimeMillis() - lastTimer >= 100) {
				lastTimer +=1000;
				System.out.println(ticks + " ticks, " + frames + "frames");
				frames = 0;
				ticks = 0;
			}
			
			
		}
		
		
	}

	private static void render() {
	}

	private static void tick() {
	}

}
 
Ums kurz zu machen: Ohne Delta wäre die Spielgeschwindigkeit von den Frames abhängig.
D.h. auf langsamen Kisten würde das Spiel langsamer, auf schnellen schneller laufen, wenn man den Wert nicht bei Bewegung oder Animation mit einrechnet.

Das -1 ist dafür da, dass das Delta immer zwischen 0 und <1 bleibt.

Und noch kleines Beispiel: player.rotate(0, 2*delta, 0); würde z.B. den Spieler entlang der Y-Achse unabhängig von der Framerate immer gleich schnell drehen.
 
>delta += (now - lastTime) / nsPerTick;
delta ist die Anzahl der Ticks, die der letzte Durchlauf verbraucht hat: Anzahl verbrauchte Nanosekunden/Nanosekunden pro Tick

>Wann genau soll diese Schleife hier durchlaufen werden? Also wozu die Prüfung "delta >= 1" ?
>Ziel ist doch offenbar, dass diese Schleife 60x pro Sekunde durchlaufen wird oder?

Er führt für jeden verbrauchten Tick des letzten Durchlaufs diese Schleife einmal aus, ich kann mir vorstellen dass in tick() quasi die Logik liegt (Positionsbestimmung und sowas), die muss ja fortlaufend sein. Das eigentliche rendern passiert dann nur einmal pro Durchlauf, auch wenn 5 Ticks vergangen sind seit dem letzten Durchlauf. Setzt du dann boolean shouldRender = false; Würde er nur renderen, wenn der letzte Durchlauf wenigstens einen Tick gedauert hätte, also bei 60 Ticks 60 FPS.

Auf gut Deutsch: du bist in einer Endlosschleife, schaust wie viele Ticks seit dem letzten Durchlauf vergangen sind, bringst das Spiel (Positionen der Spieler usw) nach Anzahl der Ticks auf den aktuellen Stand(falls mehrere Ticks vergangen sind halt mehrmals, die Postion baut ja immer auf der vorherigen Position auf) und renderst dann den aktuellen Stand (optional nur falls wirklich ein Tick vergangen ist).
Ergänzung ()

JiJiB! schrieb:
Das -1 ist dafür da, dass das Delta immer zwischen 0 und <1 bleibt.

Theoretisch ja, weil von Delta in jedem Durchlauf 1 abgezogen wird, sprich am Ende der Schleife ist Delta immer 0 ;) Der Wert von Delta ist aber völlig irrelevant, das dient hier nur als Schleifenzähler um pro vergangenem Tick einmal tick() aufzurufen bzw shouldRender auf true zu setzen, falls ein Tick vergangen ist :D

Das player.rotate würde außerhalb der while(delta >= 1) Schleife gar nichts bringen (player.rotate(0, 2*0, 0)) und innerhalb der Schleife sehr verwegene Ergebnisse produzieren, damit rotierst du den Spieler nämlich um die Fakultät der vergangenen Ticks ;)
 
Zurück
Oben