# Framerate-Einbrüche durch Synchronisation



## Irreparabel (3. Aug 2010)

Heyho,

nachdem ich mir ein paar Ideen aus dem Tutorial von Quaxli (ein dickes Danke an dieser Stelle ) geholt habe, wollte ich dann auch mal ein Spiel programmieren, ein 2D Sidescroller um genau zu sein.
Bisher hat auch alles prima funktioniert, gestern habe ich dann das Scrollen implementiert was auch funktioniert, nur leider kommt es während des Scrollens oft zu kleinen Spalten zwischen den Objekten.
Der Grund ist mir klar, da die paint Methode (Applet) nicht alle Objekte mit der neuen Position zeichnet, darum habe ich mithilfe der Collection in der sich die ganzen Objekte befinden die Threads synchronisiert, was jedoch zu den krassen Framerate-Einbrüchen führte. Jetzt sieht das ganze zwar schicker aus und die Spalten bleiben aus, jedoch ist die Framerate von 90-100 fps auf 50-60 fps gefallen und das schon bei ~10 Objekten in der Collection, bei mehr Objekten sinkt es natürlich weiterhin. Darum hoffe ich jetzt, dass es hier eventuell Leute gibt, die schon Mal das selbe Problem hatten und eine Lösung gefunden haben, da ich bezüglich des Problems ideenlos bin. 
Falls es hilft, hier mal Auszüge aus der paint Methode und dem Scrollen:

```
synchronized(actors) {
	for (Actor actor : actors) {
		actor.drawActor(buffgraphics);
	}
}
```
Ein Objekt zu zeichnen dauert etwa 1.5-2ms

```
synchronized(actors) {
	for (Actor actor : actors) {
		if (player.x < 200) {
			actor.x++;
		}
		if (player.x + player.width > 500) {
			actor.x--;
		}
		if (player.y < 100) {
			actor.y++;
		}
		if (player.y + player.height > 300) {
			actor.y--;
		}
	}
}
```


----------



## dayaftereh (3. Aug 2010)

Also ich weiß ja nicht wie du das angehst! ich würde das so machen, also meine grund-idee für das spiel! Meinem Spieler Objekt eine Position zuweißen playerX & playerY. So und dan würde ich meine Spiel Feld einfach als Mega Universum sehen, und einen Camera Bereich Festlegen, am besten als Rectangel! in dem immer meine Spieler ist. Wenn jetzt meine Spieler sich nach vorbewegt, ziegt die Kamera einfach mit sodas meine Spieler immer in der Mitte ist! Wenn ich die Objekte jetzt zeichne, dan schaue ich erstmal ob sie in meinem Camera Bereich sind, wenn nicht dann vergessen. Mann kan sich das so vorstellen ich beobachte eine Armeise zur eine Luppe. die Luppe ist meine Kamera Bereich und die Armeise meine Spiel Figur. So ich kann immer nur den bericht von der Luppe sehen, wenn jetzt ein stein kommt, kann ich ihn nur sehen wenn er in der Luppe auf taucht, kommt mir eine armeise engegen und betritt meinen Luppen bereich, dann muss ich sie auf meine Luppe zeichen. mach einfach jedem Objekt eine Update Methode und eine Zeichne methode, bei update gibst du das delay der letzten runde mit und beim der Zeichne Methode das Graphic objekt ( auch beim spieler) dan rufst du für jedes Objekt update und dan repaint auf. in der update methode, berechnest du die neue position von dem Objekt in abhänigkeit vom speed und der richtung. wenn du das gemacht hast berechnest du die Screen Position in relation zum Spieler und seinem Camera bereich. jetzt rufst du repaint auf und zeichnest einfach alle objekt mit ihren screen positionen.Wenn eine objekt noch nicht in einer Camera berich ist, dann ist die ScreenPositon eh nicht auf dem Panel, und es wird ins nevahner gezeichnet!Ich würde ints für x und y nehmen, da musst du dan auch nis synchronized, nur wenn du obejtze aus der list löscht, nutzt da für einfach eine CopyAndWriteList! muss einfach nix synchronized.


----------



## SlaterB (3. Aug 2010)

mathematischer Hinweis nebenbei:
> Ein Objekt zu zeichnen dauert etwa 1.5-2ms

macht maximal 666 Zeichnungen pro Sekunde, bei 10 Zeichnungen pro Frame wären optimal nicht mehr 67 fps möglich


----------



## Irreparabel (3. Aug 2010)

@dayaftereh
Nur das zu zeichnen was auch in dem Bereich ist habe ich bereits versucht, jedoch kommt es ja auch vor dass sich mal mehr und mal weniger Objekte im Sichtbereich befinden, da schwanken die fps dann sehr und es wirkt unflüssig.

@SlaterB
Naja, eben doch:
Da bisher die paint Methode unabhängig von der Spielschleife gearbeitet hat, bis darauf, dass sie am Ende aufgerufen wird und dann nicht mehr auf dem gleichen Thread arbeitet, waren 90-100 fps in der Tat möglich.
Nachdem aber an der Stelle synchronisiert wird muss in der paint Methode gewartet werden, da tritt deine Rechnung dann wohl in Kraft (sofern ich dich jetzt richtig verstanden habe).


----------



## dayaftereh (3. Aug 2010)

Wie viel threads Hast du den am Laufen, normal doch nur zwei, den GameLoop Therade und den EventDispatcher-Therad.sagen wir mal wir haben 100 objete und um die Position zu berechnen mache ich 10 operationen (addition, multi, etc) das dürchte geradem 1 - 5 ms dauern! Ich weiß nicht warum du synchronized machen willst! nutze doch einfach CopyOnWriteArrayList


----------



## Irreparabel (3. Aug 2010)

Genau, 2 Threads, den der Spielschleife und der EDT.
Synchronisieren möchte ich, damit nicht auf dem EDT gezeichnet wird während die Spielschleife noch die Positionen errechnet, denn genau das führt momentan zu den Spalten zwischen den gezeichneten Objekten...
Die CopyOnWriteArrayList ist übrigens schon drinne, aber ändert überhaupt nichts an meinem Problem...


----------



## dayaftereh (3. Aug 2010)

also bei 100 Frps, ist es doch egahl on die Positionen berechnung hinter her ist,ich meine einfach 5 ms! ich glaube du berechnet einfach zu viel, schau mal wie lange deine berechnung von den Positionen dauer.wie lange der repaint dauer! kannst du deine Code mal als sip mir schicken oder so?


----------



## Irreparabel (3. Aug 2010)

Geht klar, hier der Link:
RapidShare: 1-CLICK Web hosting - Easy Filehosting
Dass das ganze an einigen Stellen noch unausgereift ist, ist mir klar, mir geht es jetzt primär um das Scrollen.


----------



## dayaftereh (3. Aug 2010)

Also ich habe mal die Applet Größe auf 800x800 gemacht und habe es 10 mal gurchlaufen lassen mit scpringen von rechts nach links und dem ganzen scrollen, mein niedrigster frame ist 87 und mein höchst 100, das liegt daran das du halt immer feste 10 schläfst. du müsstest einfach schauen wie lange dein durchlauf gedauert hat und das von deine 10 am ziehen! so das du konstant 100 fps hast. wenn sie dan weg berchen, dann ist was faul! ich kann dir nacher mal ne Klasse bereistellen wo so was eingebaut ist!


----------



## Irreparabel (3. Aug 2010)

Ehm, das ist jetzt aber auch der Code ohne Synchronisation, der sollte stabil(er) laufen. 
Aber beweg dich mal ein bisschen dann sollte dir auffallen, dass zwischen den Blöcken ab und zu die Risse/Spalten entstehen.


----------



## dayaftereh (3. Aug 2010)

Also bei mir leif es einband frei, mir ist nicht aufgefallen das die Blöcke mal wo anderst sind, bei mir ist der Block immer einband frei nach unten gefallen! und die anderen sind auf die Position gefahren!

Ich habe das mit meine FPS so gemacht, damit sie einfach immer konnstand sind, hier mal meine Hilfs Klassen:

Klasse FPSCounter.ava 
	
	
	
	





```
public class FPSCounter {

	private long lastCount;
	private int currentFPS, frameCount;

	public FPSCounter() {
	}

	public void refresh() {
		this.frameCount = 0;
		this.lastCount = System.currentTimeMillis();
	}

	public void calculateFPS() {
		this.frameCount++;
		if (System.currentTimeMillis() - this.lastCount > 1000) {
			this.lastCount = System.currentTimeMillis();
			this.currentFPS = this.frameCount;
			this.frameCount = 0;
		}
	}

	public int getCurrentFPS() {
		return this.currentFPS;
	}

}
```

Die Klasse SystemTimer.java 
	
	
	
	





```
public class SystemTimer {

	private boolean running;

	private int fps = 100;
	private long msDelay;
	private long start, end, timeDiff, sleepTime;
	private long overSleepTime;

	private FPSCounter fpsCounter;

	public SystemTimer() {
		this.fpsCounter = new FPSCounter();
	}

	public int getCurrentFPS() {
		return this.fpsCounter.getCurrentFPS();
	}

	public int getFPS() {
		return this.fps;
	}

	public long getTime() {
		return System.currentTimeMillis();
	}

	public boolean isRunning() {
		return this.running;
	}

	public void refresh() {
		this.start = System.currentTimeMillis();
		this.overSleepTime = 0;
	}

	public void setFPS(int fps) {
		if (this.fps == fps) {
			return;
		}
		this.fps = fps;

		if (this.running) {
			this.startTimer();
		}
	}

	public long sleep() {
		this.end = System.currentTimeMillis();

		this.timeDiff = this.end - this.start;
		this.sleepTime = (this.msDelay - this.timeDiff) - this.overSleepTime;

		if (this.sleepTime > 0) {
			try {
				Thread.sleep(this.sleepTime);
			} catch (InterruptedException e) {
			}

			this.overSleepTime = (System.currentTimeMillis() - this.end) - this.sleepTime;

		} else {
			try {
				Thread.sleep(1);
			} catch (InterruptedException e) {
			}
			this.overSleepTime = 0;
		}

		this.fpsCounter.calculateFPS();

		long end = System.currentTimeMillis();
		long elapsedTime = end - this.start;
		this.start = end;

		return elapsedTime;
	}

	public void startTimer() {
		if (this.running) {
			this.stopTimer();
		}
		this.running = true;

		this.msDelay = 1000 / this.fps;
		this.refresh();

		this.fpsCounter.refresh();
	}

	public void stopTimer() {
		this.running = false;
	}

}
```

Und nutzen Habe ich sie genutz um immer zu schlafen und den Delay zurüuck zu bekommen führ die bewegung der Objekte:

```
// Erzugen des Timers
SystemTimer timer = new SystemTimer();
// Anzahl der FPS
timer.setFPS(100);
timer.startTimer();
timer.refresh();
// ....
// im GameLoop
// Schläft immer so das man 100 fps bekommt und gibt das Delay zurück
long delay= timer.sleep();
```


----------



## Irreparabel (3. Aug 2010)

Danke, die Klassen werde ich mir gleich mal anschauen.^^
Damit das mit den Spalten mal stärker auffällt, in der init() Methode in Main, änder map = "start" mal in map = "zweite", und fall nach rechts unten/links unten, dann sollte es auch dir auffallen.


----------



## Irreparabel (4. Aug 2010)

Das Problem hat sich jetzt irgendwie von allein gelöst, noch weiß ich nicht wie.


----------

