# Ist meine Jump and Run Engine zu genau?



## TimeIsTheKey (2. Apr 2011)

Hallo

Wollte mal nachfragen, ob ich die Sache richtig angehe oder etwas verbessern könnte, um keine Performance-Probleme zu bekommen. Lest euch bitte meine Vorgehensweise durch und gebt mir Kritik. Bräuchte das dringend.

Ich bin zurzeit an einer Jump and Run-Engine dran und habe gerade mit der Gravitation angefangen.  Ich arbeite mit JPanels (jedes Objekt das angezeigt wird, hat eins) und habe eine Klasse geschrieben die stets als Superklasse verwendet wird. Ich habe mir überlegt, dass jeder neue Akteur eine eigene Unterklasse besitzen soll (z.B. Hintergrund, Spieler, Gegner, Hintergrundeffekte ect.). Der Grund dafür ist, dass später alle Akteure ihre eigene move-Methode und sonstige spezielle Attribute haben und ich, falls nötig, jedem Akteur eine KI verpassen könnte.
Jeder Akteur besitzt eine Geschwindigkeit (speed) und ein Gewicht (mass) welches für die Bewegung und Gravitation sehr wichtig ist. In der Hauptklasse ist eine run-Methode, in der ich im Moment nur ständig die run-Methode aller Akteure aufrufe.

Die Bewegung und Gravitation ist nun die Hauptthematik die mich interessiert. Bevor ich die Akteure bewege lasse ich sie nach der Geschwindigkeit (speed) sortieren, weil ich eine Problematik umgehen möchte. Die möchte euch anhang dieser Grafik zeigen:

http://img690.imageshack.us/img690/1006/prob1j.png

Die einzelnen Felder sollen Pixel oder Steps darstellen. Angenommen 1 und 2 werden bei einer Kollision mit 3 verschwinden und das 1 eine Geschwindigkeit von 6 hat und 2 eine von 3. 3 bewegt sich nicht und verschwindet auch, sobald es getroffen wird. Ohne die Sortierung könnte es passieren, dass fälschlicherweise das Objekt 2 verschwindet, obwohl logisch gesehen das Obekt 1 verschwinden müsste.

Nach der Sortierung bewege ich die Akteure der Reihe nach pixelweise. Es wird eine Schleife durchgegangen die x-mal durchläuft. X ist die höchste vorhandene Geschwindigkeit. Die Akteure schauen selbst, ob sie eine Bewegung machen dürfen oder ob sie sich schon zu Ende bewegt haben. Nachdem sich ein Akteur bewegt, wird überprüft, ob es irgend einen anderen Akteur berührt (Kollision). Nach jedem Schleifendurchgang wird eine Methode aufgerufen, die für die Gravitation zuständig ist. Für die Bewegung spielt es eine Rolle was für ein Speed/Mass-Verhältnis besteht. Ist dieses nicht gleich, wird jeweils berechnet, ob der Akteur überhaupt bewegt werden darf. Hat nämlich ein Akteur mehr Speed als Mass, so darf es die Bewegung immer machen und es wird berechnet, ob die Gravitationseinwirkung schon erfolgen darf. Ein Beispiel um das zu verständlicher zu machen:

Hat ein Akteur bei Speed 8 und bei Mass 5, so wird es 8 Schritte geben, die pixelweise erfolgen. Bei jedem move-Befehl, darf er sich bewegen, weil der Speed grösser ist. Bei Mass wird zuerst noch berechnet, ob er sich neu bewegen darf (er dürfte nur beim 2., 4., 5., 7. und 8. Step eine Gravitationseinwirkung erfolgen lassen).

Ist Mass grösser, läuft das gleiche Spiel mit Speed ab, einfach umgekehrt.

Meine Hauptproblematik ist, dass ich nicht weiss, ob das zu viel Performance schluckt und ob das überhaupt so genau durchgesetzt werden sollte. Das ist ziemlich viel Text, aber ich wollte versuchen, meine Überlegungen mit euch zu teilen und Verbesserungsmöglichkeiten von euch einfliessen lassen. Wer den ganzen Code sehen möchte, muss das nur sagen. Ich mache ihn nur ungerne öffentlich, weswegen ich es den Personen jeweils per PN schicken werde. Nur so als kleine Vorwarnung: Die Gravitation funktioniert noch nicht (Bug), aber sie kann leicht ausgeschaltet werden.

Würde mich sehr freuen, wenn ein erfahrener Programmierer sich die Zeit nehmen würde mir Kritik zu geben.


----------



## schlingel (2. Apr 2011)

> Ich arbeite mit JPanels (jedes Objekt das angezeigt wird, hat eins) und habe eine Klasse geschrieben die stets als Superklasse verwendet wird.



Nein! Mach das bloß nicht so. Wenn du schon mit Swing für ein Spiel hernehmen möchtest, dann zeichne deine Objekte auf ein einziges Canvas. Die Lösung mit den JPanels frisst auf jeden Fall unnötig Ressourcen.



> Ich habe mir überlegt, dass jeder neue Akteur eine eigene Unterklasse besitzen soll (z.B. Hintergrund, Spieler, Gegner, Hintergrundeffekte ect.). Der Grund dafür ist, dass später alle Akteure ihre eigene move-Methode und sonstige spezielle Attribute haben und ich, falls nötig, jedem Akteur eine KI verpassen könnte.



Du hast offensichtlich für alle diese Objekte eine Basisklasse und das wäre Akteur. Warum hier künstlich weitere Kindklassen einführen? Es würde sich z.B. anbieten mit dynamischen Binden die Kontrolle eines Objektes zu regeln und nicht über Vererbung.



> Die einzelnen Felder sollen Pixel oder Steps darstellen. Angenommen 1 und 2 werden bei einer Kollision mit 3 verschwinden und das 1 eine Geschwindigkeit von 6 hat und 2 eine von 3. 3 bewegt sich nicht und verschwindet auch, sobald es getroffen wird. Ohne die Sortierung könnte es passieren, dass fälschlicherweise das Objekt 2 verschwindet, obwohl logisch gesehen das Obekt 1 verschwinden müsste.



Ehrlich gesagt sehe ich auch nach mehrmaligen Lesen das Problem nicht. Wenn ein Objekt langsamer ist bewegt es sich einfacher weniger weit pro Tick. (Siehe z.B. XNA als Beispiel wie das gelöst wird.)



> Nach der Sortierung bewege ich die Akteure der Reihe nach pixelweise. Es wird eine Schleife durchgegangen die x-mal durchläuft. X ist die höchste vorhandene Geschwindigkeit. Die Akteure schauen selbst, ob sie eine Bewegung machen dürfen oder ob sie sich schon zu Ende bewegt haben. Nachdem sich ein Akteur bewegt, wird überprüft, ob es irgend einen anderen Akteur berührt (Kollision). Nach jedem Schleifendurchgang wird eine Methode aufgerufen, die für die Gravitation zuständig ist. Für die Bewegung spielt es eine Rolle was für ein Speed/Mass-Verhältnis besteht. Ist dieses nicht gleich, wird jeweils berechnet, ob der Akteur überhaupt bewegt werden darf. Hat nämlich ein Akteur mehr Speed als Mass, so darf es die Bewegung immer machen und es wird berechnet, ob die Gravitationseinwirkung schon erfolgen darf. Ein Beispiel um das zu verständlicher zu machen:
> 
> Hat ein Akteur bei Speed 8 und bei Mass 5, so wird es 8 Schritte geben, die pixelweise erfolgen. Bei jedem move-Befehl, darf er sich bewegen, weil der Speed grösser ist. Bei Mass wird zuerst noch berechnet, ob er sich neu bewegen darf (er dürfte nur beim 2., 4., 5., 7. und 8. Step eine Gravitationseinwirkung erfolgen lassen).



Prinzipiell halte ich es schon für fragwürdig dass du an einer Stelle eine Schleife einbaust an der eine Formel genügen sollte. Noch dazu sehe ich hier auch nicht den Sinn. Was spricht dagegen dein Physikbuch aufzuschlagen dir die Formel dort rauszusuchen und die einzubauen anstatt hier eine Schleife zu verwenden?


----------



## TimeIsTheKey (2. Apr 2011)

schlingel hat gesagt.:


> Nein! Mach das bloß nicht so. Wenn du schon mit Swing für ein Spiel hernehmen möchtest, dann zeichne deine Objekte auf ein einziges Canvas. Die Lösung mit den JPanels frisst auf jeden Fall unnötig Ressourcen.



Okay. Also einfach in der run-Methode eine draw-Methode einbauen, die alles zeichnet.



schlingel hat gesagt.:


> Du hast offensichtlich für alle diese Objekte eine Basisklasse und das wäre Akteur. Warum hier künstlich weitere Kindklassen einführen? Es würde sich z.B. anbieten mit dynamischen Binden die Kontrolle eines Objektes zu regeln und nicht über Vererbung.



Ich glaube ich habe es schon so implementiert, wie du es meinst. Da gibt es eine Basisklasse (heisst bei mir AnimatedSprite) und diese ist abstract (move mit und ohne Parameter sind abstrakt). Dann habe ich weitere Klassen die von AnimatedSprite erben. In diesen werden die Methode ausprogrammiert. In einer for-Schleife gehe ich dann ein ArrayList durch die mit AnimatedSprites gefüllt ist (eigentlich Refferenzen der Akteure) und greife auf die abstrakte Methode zu. Ist doch so programmiert, wie du es meinst, oder?



schlingel hat gesagt.:


> Ehrlich gesagt sehe ich auch nach mehrmaligen Lesen das Problem nicht. Wenn ein Objekt langsamer ist bewegt es sich einfacher weniger weit pro Tick. (Siehe z.B. XNA als Beispiel wie das gelöst wird.)



Naja. Die Problematik ist nicht so gross. Falls 1 links von 3 und 2 rechts von 3 ist und beide nur noch 1 Feld brauchen um 3 zu berühren, taucht diese "Problematik" auf. Logisch gesehen muss 1 zuerst 3 berühren, weil die Geschwindigkeit höher ist.



schlingel hat gesagt.:


> Prinzipiell halte ich es schon für fragwürdig dass du an einer Stelle eine Schleife einbaust an der eine Formel genügen sollte. Noch dazu sehe ich hier auch nicht den Sinn. Was spricht dagegen dein Physikbuch aufzuschlagen dir die Formel dort rauszusuchen und die einzubauen anstatt hier eine Schleife zu verwenden?



Der Sinn ist irgendwie schwer in Worte festzuhalten. Es wäre einfacher wenn ich es dir an einem Blatt zeigen könnte, aber ich versuche es mit Sätzen. Das Problem ist nicht die Formel (berechne ich schon). Damit meine Akteure sich nicht teleportieren (z.B. wenn der Speed 10 betragen würde) und auch keine Kollision verpassen (z.B. falls in diesen 10 Pixeln ein Akteur wäre), gehen sie jedes Pixel während ihrer Bewegung durch. Dabei wird auch die Geschwindigkeit der anderen Akteure in Betracht gezogen. Das erste was beachtet wird, ist wieviele Schritte gemacht werden. Falls der höchste Speed 6 ist, darf ein Akteur mit einem Speed von 3 nur bei jedem 2. Step eine Bewegung machen. Bis hier sollte es noch verständlich sein. Mir fällt auf, dass ich es ein bisschen falsch implementiert habe, aber das lässt sich leicht beheben. 
Nun kommt aber noch die Gravitation dazu. Angenommen ein Akteur hat 8 Speed und 5 Mass (in diesem Beispiel ist die Gravitation auf 1 gesetzt). Würde sich der Akteur in der Luft nun nach rechts bewegen, dürfte er nicht nach jedem Step 1 nach runter gehen, sondern berechnen, wann er 1 nach unten gezogen werden kann. Hier ein Beispiel wie sich der Akteur bewegen würde:

http://img88.imageshack.us/img88/1702/prob2d.png

Hoffe man sieht was ich meine.


----------



## schlingel (2. Apr 2011)

> Okay. Also einfach in der run-Methode eine draw-Methode einbauen, die alles zeichnet.


Das ist auch der falsche Weg. Das Objekt sollte sich nicht selbst zeichnen sondern es sollte alles von einer zentralen Stelle ausgehen. Ein gutes Beispiel ist da XNA da hier das Framework das ganze so vorgibt.

Es gibt eine Methode die wird jeden Tick aufgerufen und dient dir dazu die Kalkulationen in deinen Objekten durchzuführen. Diese Methode gibt dir die Info mit wieviel Zeit seit dem letzten Methodenaufruf vergangen ist damit du weißt wie weit sich deine Objekte bewegen sollen.

In einer zweiten Methode, die ebenfalls periodisch aufgerufen wird, zeichnest du. Aber tatsächlich nur zeichnen. Du rufst von jedem Objekt seine Position auf und das aktuelle Bild welches du aus dem SpriteBatch zeichnen musst.



> Ich glaube ich habe es schon so implementiert, wie du es meinst


Ich glaube nicht. Es reicht wenn du eine Klasse hast die, die Infrastruktur für das Sprite-Zeichnen und das Bewegen zur Verfügung stellt. Diese Klasse wird dann intern über irgend eine Logik gesteuert. Genau diese Logik sollte austauschbar sein. Dadurch verändert sich dann das Verhalten deines Akteurs. 

Generell sehe ich aber keinen Grund den Code der zeichnet und sonstige infrastrukturelle Angelegenheiten kalkuliert und den Code der das Objekt im Spiel beschreibt zu vermischen. Dafür bietet sich das Strategy Pattern bzw. Dynamisches Binden an. 

Zu deiner Physikberechnung: Ich glaube mittlerweile verstanden zu haben wie du das meinst und ich glaube dass die Idee richtig ist. Doch sollte die zeit- bzw. wegabhängige Kalkulation nicht relativ vom jeweiligen Objekt sondern zentral vom Spiel bestimmt werden. Das heißt jeden Tick sollte für alle Objekte kalkuliert werden wo sie sich aktuell befinden und wie die Masse/Geschwindigkeit sich verändert hat.


----------



## TimeIsTheKey (3. Apr 2011)

schlingel hat gesagt.:


> Das ist auch der falsche Weg. Das Objekt sollte sich nicht selbst zeichnen sondern es sollte alles von einer zentralen Stelle ausgehen. Ein gutes Beispiel ist da XNA da hier das Framework das ganze so vorgibt.
> 
> Es gibt eine Methode die wird jeden Tick aufgerufen und dient dir dazu die Kalkulationen in deinen Objekten durchzuführen. Diese Methode gibt dir die Info mit wieviel Zeit seit dem letzten Methodenaufruf vergangen ist damit du weißt wie weit sich deine Objekte bewegen sollen.
> 
> In einer zweiten Methode, die ebenfalls periodisch aufgerufen wird, zeichnest du. Aber tatsächlich nur zeichnen. Du rufst von jedem Objekt seine Position auf und das aktuelle Bild welches du aus dem SpriteBatch zeichnen musst.



Ich habe mir das XNA noch nicht angeschaut, aber ich werde es nachholen. Eigentlich hatten wir aber beide genau das Gleiche gemeint. Meine Mainklasse hat die Schnittstelle Runnable und somit die run-Methode implementiert. Die würde dann so aussehen:


```
public void run(){
		while(game_running){
			if(!break){
				moveObjects();
                                drawObjects();
				repaint();
			}
			try {
				Thread.sleep(speed);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
	}
}
```

Das mit der Zeit ist schon eingebaut. Die Akteure haben individuelle Wartezeiten und die Akteure werden sich nur bewegen, wenn sie ihre Wartezeit abgesessen haben.



schlingel hat gesagt.:


> Ich glaube nicht. Es reicht wenn du eine Klasse hast die, die Infrastruktur für das Sprite-Zeichnen und das Bewegen zur Verfügung stellt. Diese Klasse wird dann intern über irgend eine Logik gesteuert. Genau diese Logik sollte austauschbar sein. Dadurch verändert sich dann das Verhalten deines Akteurs.
> 
> Generell sehe ich aber keinen Grund den Code der zeichnet und sonstige infrastrukturelle Angelegenheiten kalkuliert und den Code der das Objekt im Spiel beschreibt zu vermischen. Dafür bietet sich das Strategy Pattern bzw. Dynamisches Binden an.



Hmmm... Bin mir nicht sicher, ob das "dynamisch gebunden" ist, aber ich schätze (zwar ohne überschreiben, aber macht keinen grossen Unterschied):



```
public abstract class Sprite extends JPanel implements Comparable<Object>{

	public abstract void moveSprite(int x, int y);
	public abstract void moveSprite();

}
```


```
public class Player extends Sprite {

public void moveSprite(int x, int y) { ... }
public void moveSprite() { ... }

}
```


```
public void moveObjects(){
...
for(Sprite sprites: spritelist){
	sprites.moveSprite();
}
...
}
```



schlingel hat gesagt.:


> Zu deiner Physikberechnung: Ich glaube mittlerweile verstanden zu haben wie du das meinst und ich glaube dass die Idee richtig ist. Doch sollte die zeit- bzw. wegabhängige Kalkulation nicht relativ vom jeweiligen Objekt sondern zentral vom Spiel bestimmt werden. Das heißt jeden Tick sollte für alle Objekte kalkuliert werden wo sie sich aktuell befinden und wie die Masse/Geschwindigkeit sich verändert hat.



Hast recht. Im Moment gebe ich den Befehl sich zu bewegen und überprüfe erst dann, ob überhaupt zum aktuellen Tick eine Bewegung gemacht werden muss. Das Gleiche werde ich mit der Gravitation machen. Sollte den Ablauf verschnellern und mir die Programmierung erleichtern. Danke!


----------

