# Niedrige Auslastung oder ruckelfrei?



## stulleman (16. Okt 2012)

Hallo zusammen!

Ich probiere gerade viele gameloops aus und habe endlich eine halbwegs gute gefunden!
Falls es jemanden interessiert habe ich es von hier.

EDIT: Ich habe die fixed timestep loop genommen 

Das Problem sind die letzten Zeilen der gameloop. Entlaste ich die CPU und rufe sleep auf, läuft es 5 Sekunden flüssig, ruckelt 2 Sekunden und läuft dann wieder flüssig. Das wiederholt sich unendlich so weiter. Die CPU Auslastung liegt bei ca. 4-7 %.
Nehme ich den sleep raus, läuft es wesentlich besser, trotzdem nicht 100% perfekt. Die CPU Auslastung liegt bei 25-27%. Was soll ich machen? Sleep weglassen? Oder hat jemand eine andere Lösung?

Danke schonmal!


----------



## Paddelpirat (16. Okt 2012)

Hast du mal die alternativen Lösungen aus den comments ausprobiert? Sind ja doch mehrere, die sich da was haben einfallen lassen.


----------



## Network (16. Okt 2012)

Um ehrlich zu sein wirkt der Artikel nicht gerade von einer Person geschrieben mit... sagen wir mal wirklicher Erfahrung:


			
				http://www.java-gaming.org/index.php?topic=24220.0 hat gesagt.:
			
		

> OMG so bad. Don't ever do that.



[EDIT]
Um nur ein Beispiel zu nennen... Und es war auch nicht gemeint, dass diese Person keinerlei Erfahrung hat, aber meist spiegeln die Kommentare ein weit breiter gefächertes Wissen ab, als sowas.

Btw. Normalerweise habe ich nichts gegen schlechte Rechtschreibung oder dergleichen, aber in einer Veröffentlichung sollte man nunmal auf soetwas achten.
[/EDIT]


----------



## stulleman (16. Okt 2012)

Hm okay!
Aber wenn ihr das nicht gut findet, müsst ihr doch sicherlich ein besseres Tutorial gefunden haben oder?
Ich habe ja auch nicht gesagt das es super gut ist, nur das mir bis jetzt am besten geholfen hat 
Ich wäre dankbar über ein "besseres" Tutorial!

Und zu den Kommentaren, vielleicht habe ich es nicht richtig gelesen, aber auf eine Wirkliche Lösung sind die ja auch nicht gekommen oder?


----------



## Paddelpirat (16. Okt 2012)

Habs zwar noch nicht selber gelesen, aber soweit ich weiß gibt es hier ein recht gutes Tutorial von dem Benutzer Quaxli. Such einfach mal danach.


----------



## stulleman (17. Okt 2012)

Das habe ich mir schon durchgelesen. Aber mir gefallen daran mehrere dinge nicht.
Keine Interpolation, und kein active rendering. Das Problem ist ja nicht das ich nicht weiß wie man sich ein Spiel zusammen bastelt, sondern wie man es gut macht! Und ein sehr wichtiger Bestandteil ist halt die gameloop. Dazu gibt es leider nur sehr wenige Tutorials.


----------



## Spacerat (17. Okt 2012)

Also mit Code und Tutorials kommt man da nicht wirklich zu etwas richtig Guten, man muss sich auch schon mal selbst Gedanken machen. Z.B. darüber, welche "Arbeiten" in einem Programm parallel ausgeführt werden können, dann hätte man halt nicht nur einen Gameloop, sondern obendrein noch einen Renderloop, einen Soundloop uvm. Der Gameloop lässt sich evtl. auch noch in mehrere Threads aufteilen, Logik und Datenverwaltung z.B.


----------



## Guest2 (17. Okt 2012)

Moin,

Jitter/Mikro-Ruckler Phänomene sind ein Problem der Synchronisation und des Timings. Im Ideal müsste jedes Frame das auf dem Bildschirm erscheinen soll perfekt mit dem VSync abgeglichen sein (zu spät = verlorenes Frame; zu früh = zu viel gerechnet) und zudem die dargestellte Bewegung zeitlich perfekt ausgerichtet sein (Problem: Timer / Zeitabfragen / Sleep haben nur eine relativ geringe Auflösung und es passiert viel in einer Blackbox (Java + Betriebssystem), das sich zeitlich nicht vernünftig messen lässt). Ein gewisser Kompromiss zwischen Auslastung und Jitter / Tearing / Ruckeln muss imho immer getroffen werden.

Was mich allerdings wundert, sind die Zeitangaben von 5 Sekunden flüssig / 2 Sekunden ruckeln. Meinem Gefühl nach sind die Zeiten zu lang, als dass es sich auf die Konstruktion der Gameloop zurückführen lässt. Hast Du vielleicht mal ein KSKB versucht, in dem sich das Phänomen reproduzieren lässt? (Z.B. ein einfacher vertikaler Strich, der mit einer konstanten (aber wählbaren) Geschwindigkeit von links nach rechts durchs Fenster wandert? (klingt einfacher als es ist )

Viele Grüße,
Fancy


----------



## stulleman (17. Okt 2012)

@Spacerat: An so etwas habe ich schon gedacht, updates in einem Thread, und render in einem anderen. Nur habe ich das so nirgendwo gesehen, und dachte mir das dass einen Grund haben wird 

@Guest2: Ja, ich habe nicht einfach den Code aus dem Beispiel kopiert, sondern habe nur die gameloop übernommen. Hier ist das komplette Programm, indem man die anzuzeigenden Kreise wählen kann, und die Richtung und Geschwindigkeit.
Vielleicht kann es jemand mal bei sich auf dem Pc versuchen?

Ich merke gerade das hier jetzt fast überhaupt nicht mehr läuft, obwohl ich nichts verändert habe!
Kann jemand die starken Ruckler bestätigen?


```
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.util.Random;
import java.util.Vector;

import javax.swing.JFrame;

public class EasyRenderFrame extends JFrame implements Runnable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 2079014996055778535L;

	private static final int NUMBER_OF_OBJECTS = 1;	// Anzahl der Kreise
	
	
	private static final double UPDATE_HERTZ = 30;
	private static final double TIME_BETWEEN_UPDATES = 1000000000 / UPDATE_HERTZ;
	private static final int MAX_UPDATES_BEFORE_RENDER = 100;
	private static final double TARGET_FPS = 60;
	private static final double TARGET_TIME_BETWEEN_RENDERS = 1000000000 / TARGET_FPS;

	private int width, height;
	private Canvas canvas;
	private BufferStrategy bufferStrategy;
	private BufferedImage offScreenImage;
	private Graphics g;
	private Graphics2D g2d;

	private int fps = 0;
	private int frameCount = 0;

	private boolean running;
	private boolean pause;

	private Color[] colors = new Color[] {Color.black, Color.blue, Color.green, Color.magenta, Color.orange, Color.pink, Color.red};
	private Vector<Sprite> sprites;

	public EasyRenderFrame(int width, int height) {
		this.width = width;
		this.height = height;

		initialize();
		setUp();

		Thread animator = new Thread(this);
		animator.start();
	}

	private void initialize() {
		setIgnoreRepaint(true);
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		setTitle("Easy Render");

		canvas = new Canvas();
		canvas.setSize(width, height);
		canvas.setIgnoreRepaint(true);

		add(canvas);
		pack();
		setLocationRelativeTo(null);
		setVisible(true);

		canvas.createBufferStrategy(2);
		bufferStrategy = canvas.getBufferStrategy();

		GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
		GraphicsDevice gd = ge.getDefaultScreenDevice();
		GraphicsConfiguration gc = gd.getDefaultConfiguration();

		offScreenImage = gc.createCompatibleImage(width, height);

		g = null;
		g2d = null;

		running = true;
		pause = false;
	}

	private void setUp() {
		sprites = new Vector<>();
		Random rand = new Random();

		for(int i = 0; i < NUMBER_OF_OBJECTS; i++) {
			int x = rand.nextInt(539) + 31;
			int y = rand.nextInt(339) + 31;
			int width = rand.nextInt(20) + 10;
			int height = rand.nextInt(20) + 10;
//			double xSpeed = rand.nextDouble() * 6;		// Random Geschwindigkeiten, oder selber aussuchen!
//			double ySpeed = rand.nextDouble() * 6;		
			double xSpeed = 5;
			double ySpeed = 0;
			
			Color color = colors[rand.nextInt(colors.length)];

			sprites.add(new Sprite(x, y, width, height, xSpeed, ySpeed, color));
		}
	}

	private void update() {
		for(int i = 0; i < sprites.size(); i++) {
			Sprite a = sprites.get(i);
			a.updateLogic();
			a.updatePosition();
		}
	}

	private void render(float interpolation) {
		try {
			g2d = offScreenImage.createGraphics();
			g2d.setColor(Color.white);
			g2d.fillRect(0, 0, width, height);

			// User Objects

			for(int i = 0; i < sprites.size(); i++) {
				sprites.get(i).drawSprite(g2d, interpolation);
			}

			// User Objects end

			g = bufferStrategy.getDrawGraphics();
			g.drawImage(offScreenImage, 0, 0, null);

			if(!bufferStrategy.contentsLost()) {
				bufferStrategy.show();
			}
		} finally {
			if( g != null ) {
				g.dispose();
			}
			if( g2d != null ) {
				g2d.dispose();
			}
		}
	}

	@Override
	public void run() {
		double lastUpdateTime = System.nanoTime();
		double lastRenderTime = System.nanoTime();

		int lastSecondTime = (int) (lastUpdateTime / 1000000000);

		while(running) {
			double now = System.nanoTime();
			int updateCount = 0;

			if(!pause) {
				while(now - lastUpdateTime > TIME_BETWEEN_UPDATES && updateCount < MAX_UPDATES_BEFORE_RENDER) {
					update();
					lastUpdateTime += TIME_BETWEEN_UPDATES;
					updateCount++;
				}

				if (now - lastUpdateTime > TIME_BETWEEN_UPDATES) {
					lastUpdateTime = now - TIME_BETWEEN_UPDATES;
				}

				float interpolation = Math.min(1.0f, (float) ((now - lastUpdateTime) / TIME_BETWEEN_UPDATES) );
				render(interpolation);
				lastRenderTime = now;

				int thisSecond = (int) (lastUpdateTime / 1000000000);
				if (thisSecond > lastSecondTime) {
					fps = frameCount;
					frameCount = 0;
					lastSecondTime = thisSecond;
				}

//				while (now - lastRenderTime < TARGET_TIME_BETWEEN_RENDERS && now - lastUpdateTime < TIME_BETWEEN_UPDATES) {
//					Thread.yield();
//
//					//This stops the app from consuming all your CPU. It makes this slightly less accurate, but is worth it.
//					//You can remove this line and it will still work (better), your CPU just climbs on certain OSes.
//					//FYI on some OS's this can cause pretty bad stuttering. Scroll down and have a look at different peoples' solutions to this.
//					try {
//						Thread.sleep(10);
//					} catch(Exception e) {} 
//
//					now = System.nanoTime();
//				}

			} else {

			}
		}
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		new EasyRenderFrame(600, 400);
	}
}
```


```
package de.pk.maximilian.easyrender;

import java.awt.Color;
import java.awt.Graphics2D;

public class Sprite implements Moveable, Drawable {

	private double xPos, yPos;
	private double lastXPos, lastYPos;
	private double width, height;
	private double xSpeed, ySpeed;
	private Color color;
	
	public Sprite(double xPos, double yPos, double width, double height, double xSpeed, double ySpeed, Color color) {
		this.xPos = xPos;
		this.yPos = yPos;
		this.lastXPos = xPos;
		this.lastYPos = yPos;
		this.width = width;
		this.height = height;
		this.xSpeed = xSpeed;
		this.ySpeed = ySpeed;
		this.color = color;
	}
	
	@Override
	public void updateLogic() {
		if(xPos <= 30) {
			xSpeed = -xSpeed;
		}
		if(xPos >= 570) {
			xSpeed = -xSpeed;
		}
		
		if(yPos <= 30) {
			ySpeed = -ySpeed;
		}
		if(yPos >= 370) {
			ySpeed = -ySpeed;
		}
	}
	
	@Override
	public void updatePosition() {
		lastXPos = xPos;
		lastYPos = yPos;
		
		xPos += xSpeed;
		yPos += ySpeed;
	}
	
	@Override
	public void drawSprite(Graphics2D g2d, float interpolation) {
		g2d.setColor(color);
		int drawX = (int) ((xPos - lastXPos) * interpolation + lastXPos - width/2);
		int drawY = (int) ((yPos - lastYPos) * interpolation + lastYPos - height/2);
		g2d.fillOval(drawX, drawY, (int) width, (int) height);
	}
}
```


```
package de.pk.maximilian.easyrender;

public interface Moveable {

	public void updateLogic();
	
	public void updatePosition();
}
```


```
package de.pk.maximilian.easyrender;

import java.awt.Graphics2D;


public interface Drawable {

	public void drawSprite(Graphics2D g2d, float interpolation);
}
```


----------



## Network (17. Okt 2012)

Nein es läuft tadellos. Keine Ruckler kein nichts. Es läuft so flüssig wie es nur flüssig laufen kann.

Dabei fällt mir zu dem Code noch ein, dass bspw. JPanel automatisch doublebuffering unterstützt und damit eine eigene Implementation eigentlich in den meisten Fällen unnütz ist.
Du verwendest auch etwas älteren Java-Code der heute nicht mehr unbedingt Gang und Gebe ist, aber das heißt nicht, dass er nicht mehr funktioniert.

Ich würde dir ja dringend Raten Eclipse zu verwenden (oder Netbeans)!
Man merkt sofort, dass du diese nicht verwendest.

Dort ist ein Fehler in Zeile 89, es müsste eigentlich Vector<Sprite>(); heißen.

Mehr kann ich auch nicht sagen.


----------



## Spacerat (17. Okt 2012)

stulleman hat gesagt.:


> @Spacerat: An so etwas habe ich schon gedacht, updates in einem Thread, und render in einem anderen. Nur habe ich das so nirgendwo gesehen, und dachte mir das dass einen Grund haben wird


Nun, es hat einen Grund, nämlich den, dass die Unterteilung in mehrere Threads den Rahmen eines jeden Tutorials sprengen würde. Was sich in Threads parallelisieren lässt, hängt vom Programm ab, deswegen kann einem diese Entscheidung niemand und schon gar nicht von einem Tutorial abgenommen werden.


----------



## stulleman (17. Okt 2012)

Network hat gesagt.:


> Nein es läuft tadellos. Keine Ruckler kein nichts. Es läuft so flüssig wie es nur flüssig laufen kann.



Komisch, kann das denn vielleicht noch jemand bestätigen? Welches OS hast du denn?



Network hat gesagt.:


> Ich würde dir ja dringend Raten Eclipse zu verwenden (oder Netbeans)!
> Man merkt sofort, dass du diese nicht verwendest.



Ich frage mich welches Eclipse du benutzt, aber nicht das selbe wie ich. Bei mir kommt nämlich kein Fehler. Denn wenn man einmal den Typ angibt, muss man ihn bei der Initialisierung nicht erneut angeben.

Und ich bin ja dankbar für Kritik, aber bringt mir diese überhaupt nichts wenn man mir nicht sagt was falsch oder "alt" ist 

@Spacerat: Das heißt ich sollte mir was zu Thread Parallelisierung durchlesen? Und das wird dann der beste Weg sein?


----------



## Network (17. Okt 2012)

Ich verwende Indigo. Und dass man es bei der Initialisierung nicht mehr angeben muss gilt soweit ich weiss nur für 1.7.
Die ganzen nicht verwendeten Variablen werden bei dir nicht angezeigt? (Deshalb dachte ich an das fehlen einer gescheiten IDE )

Wie gesagt, es ist nicht schlimm an ein paar Stellen gibt es halt neuere Vorgehensweisen... wollte damit nur andeuten, dass mir sonst nichts an dem Code schlimmes auffällt, was ja nichtmal wirklich "schlimm" ist 
Deshalb bin ich nicht näher darauf eingegangen.
-> Bis auf das erwähnte "Canvas" statt JPanel, das habe ich erwähnt. Wenn man ein Spiel macht das mehrere Threads brauchen würde für Multicoreunterstützung wegen fehlender Leistung, dann würde man auch eine OpenGL Schnittstelle verwenden. Ein Canvas ist also normalerweise... lasse mich aber gerne eines besseren belehren ;D

Ich verwende Windows 7 Home Premium.

Gruß
Net


----------



## Spacerat (17. Okt 2012)

stulleman hat gesagt.:


> @Spacerat: Das heißt ich sollte mir was zu Thread Parallelisierung durchlesen? Und das wird dann der beste Weg sein?


Jep. Erste Lektion: Multithreading hat immer was mit Parallelisierung zu tun oder man macht etwas falsch bzw. hat etwas nicht verstanden. "Thread Parallelisierung" wäre also schon mal doppelt gemoppelt. 

@Network: Ein Canvas geht schon in Ordnung sofern man weiss, was man tut. Swing nutzt, wenn man so will, auch nichts anderes als einen Canvas oder besser gesagt ein natives Fenster ebenso wie Canvas auch. Wenn man also die Gui in senem Spiel komplett selber machen will...
Kann mich dran erinnern, dass sich Java3D und die ganzen OpenGL-Bindings sich auch einst schwer taten Swing zu unterstützen. Weil man annahm, dass mit diesen Bindings eh' nur Spiele im Fullscreen-Modus entwickelt werden würden, die keine vordefinierten GUI-Elemente benötigen, nutzte man halt "java.awt.Canvas" als grundlegende Schnittstelle.


----------



## stulleman (17. Okt 2012)

Network hat gesagt.:


> Und dass man es bei der Initialisierung nicht mehr angeben muss gilt soweit ich weiss nur für 1.7.



Das weiß ich nicht  Aber ich weiß das es bei mir nicht als Fehler angezeigt wird und kompilierbar ist.



Network hat gesagt.:


> Die ganzen nicht verwendeten Variablen werden bei dir nicht angezeigt? (Deshalb dachte ich an das fehlen einer gescheiten IDE )



Doch doch, nur sind diese nicht benutzt, weil ich einen Teil des Codes auskommentiert habe und später vielleicht noch brauche 

Also deiner Meinung nach einfach statt dem Canvas ein JPanel benutzen?



Spacerat hat gesagt.:


> Jep. Erste Lektion: Multithreading hat immer was mit Parallelisierung zu tun oder man macht etwas falsch bzw. hat etwas nicht verstanden. "Thread Parallelisierung" wäre also schon mal doppelt gemoppelt.



Okay, aber das Problem ist das ich kleine Spiele die ich schon geschrieben habe, z.B. Pong flüssig ablaufen lassen will. Und die einzige Lösung dazu wird wohl kaum Multithreading sein, oder?

Wie würdet ihr denn ein kleines Spiel wie Pong schreiben?


----------



## Spacerat (17. Okt 2012)

stulleman hat gesagt.:


> Okay, aber das Problem ist das ich kleine Spiele die ich schon geschrieben habe, z.B. Pong flüssig ablaufen lassen will. Und die einzige Lösung dazu wird wohl kaum Multithreading sein, oder?


Haha... Nee. Für Pong benötigt man nur den Animations-Thread und "Thread.sleep(20)" für ca. 50FPS. Die Bewegungen der "Schläger" steuert man über Events und klar, in diesem Fall natürlich Swing, weil dann spart man sich das ganze DoubleBuffering-Gedöns, wie Network es schon andeutete. Für Pong findet man aber auch genug Tutorials die funktionieren solten. Quaxlis Tutorial ist da ja sogar noch ein wenig heftiger, was die Animation angeht.


----------



## stulleman (17. Okt 2012)

Ich glaube das ganz kommt irgendwie falsch rüber 
Ich weiß wie man Pong programmiert, habe ich auch schon. Der einzige Punkt dabei sind immer die flüssigen Animationen. Bei mir gibt es immer kleine Ruckler! Und zur Zeit suche ich halt nach einer Methode diese los zu werden, ohne mit der Implementation übers Ziel hinaus zu schießen!
Denn ich bin mir sicher wenn ich das Tutorial von Quaxlis kopieren würde, hätte ich noch diese kleine Ruckler.


----------



## Spacerat (17. Okt 2012)

Tja, wer ahnt denn schon, dass es heutzutage noch Rechner gibt, auf denen auch nur eine einzige Pong-Implementation ruckelt. Evtl. liegt's ja an deiner doch recht ungewöhnlichen Art, "ignoreRepaint()" und BufferStrategy zu verwenden, vllt. auch an der unsynchronisierten Verwendung gemeinsamer Ressourcen in unterschiedlichen Threads. Die Sprites zum Beispiel sollten evtl. ihre Position, Bild und Grösse nicht verändern, während sie gezeichnet werden. Demnach würde ich dir raten, dich mal eingehender in Multithreading einzulesen.
Bei mir läuft's aber auch ruckelfrei, was bei 4 Kernen mit 3,2GHz auch zu erwarten ist. OS: Win7 Ultimate.
Also wenn irgendwann mal eine aktuelle Implementation von Pong ruckeln sollte, hat man entweder eines, mehrere oder gar alle Themen von MultiThreading, Doublebuffering, Eventhandling und Grafik in Java nicht verstanden und macht deswegen etwas falsch oder man hat unzureichende oder defekte Hardware (veraltete Treiber und falsche Konfiguration zähle ich mal zu "defekter" Hardware).


----------



## Network (17. Okt 2012)

Vieleicht solltest du echt mal Quaxlis Tutorial Beispiel ausprobieren. Ist ja bereits als fertiges Programm im Download erhalten, auf wunsch sende ich dir mal ein Programm(code) von Grund auf neu sowie ich es geschrieben hätte (vereinfacht aber mit der selben Funktion wie es dein Programm auch jetzt hat).
Und dann testest du das mal auf deinem Computer.

Ich würde erstere Methode bevorzugen 

Vieleicht liegts ja wie du bereits andachtest an deinem Betriebssystem oder wie von Spacerat erwähnt an deiner Hardware oder wie von mir gesagt (jetzt ) an Java selbst.
Eventuell hast du ja was verhunzt bei der installation. Der Programmpfad ändert sich bei jeder neuen Version (nicht mehr ab 1.7), deshalb bleibt bei einem Update oder Installation einer neueren Java-Version oder SDKs das alte JavaZeug übrig.


----------



## stulleman (17. Okt 2012)

@Network: Machen wir es lieber so das ich nochmal etwas programmiere und du guckst ob es bei dir ruckelt, ja?

Mein Rechner ist nicht alt, sodass es nicht an der Hardware liegen kann 
Woran es wirklich liegen kann ist an der Java Installation. Weil woran sonst soll es liegen das ein und das selbe Programm am ersten Tag kaum ruckelt und am zweiten total stottert?

Danke erstmal


----------

