# Spieleprogrammierung



## Paladin (4. Jan 2007)

Hi,

ich bin gerade dabei mich in die Spieleprogrammierung in Java einzuarbeiten. Als Übung will ich ein kleines 2D Spiel á la TeacherBusters (C64) schreiben.  Bei dem genannten Spiel fährt der Spieler mit einem kleinen Panzer durch 
eine horizontal/vertikal scrollende Welt. 

Meine Frage ist jetzt welche API sich am besten für die Umsetzung eines solchen Projekts eignet. Ich habe schon ein wenig mit swing und swt rumprobiert aber hatte das Gefühl, dass dies nicht die optimale Lösung ist. JOGL oder LWJGL scheint für diese Art von Spiel passend zu sein. Gibt es noch andere die für ein solches Projekt noch besser geeignet sind? Was ist mit Java2D? Wäre Java2D für mein Projekt noch besser geeignet als JOGL/LWJGL?
Hat jemand Erfahrungen wie schwer es ist sich in diese APIs einzuarbeiten? Zumindestens LWJGL (JOGL und Java2D kenne ich noch nicht) ist für einen Anfänger der Spieleentwicklung nicht gerade leicht zu handhaben.

Vielen Dank im voraus.

Gruß

Paladin


----------



## LoN_Nemesis (4. Jan 2007)

Ich weiss nicht, aber wenn du es nicht super komplex machen willst, dann halte ich die Benutzung von OpenGL für Overkill. Für solch ein einfaches 2D Spiel reicht locker auch Java2D. Dabei ist Einarbeitungszeit auch recht gering, wenn man schon allgemeine Java bzw Programmierkenntnisse hat.


----------



## Wildcard (4. Jan 2007)

Eine 3D Engine bringt mal eben gar nichts (ausser Problemen) für ein 2D Spiel.  :shock:


----------



## Campino (4. Jan 2007)

Java2D. Das baut im Übrigen auf Swing auf  Also eigentlich Java2D mit Swing. Was hattest du den für Probleme?


----------



## Guest (4. Jan 2007)

Mein Problem mit Swing war, dass mein Testszenario schon mit relativ wenigen Sprites arg geruckelt hat. Wie schon gesagt bin ich was Spieleentwicklung angeht noch ein Anfänger also nehme ich mal an, dass mein code nicht optimal 
war. Aber ein so kleines Programm sollte meiner Meinung nach eben auch mit schlechtem Code gut laufen.


----------



## Wildcard (4. Jan 2007)

Anonymous hat gesagt.:
			
		

> Aber ein so kleines Programm sollte meiner Meinung nach eben auch mit schlechtem Code gut laufen.


Das stimmt so einfach nicht. Swing ist ein extrem leistungsfähiges Toolkit wird von weniger erfahrenen Entwicklern aber fast immer falsch benutzt.


----------



## LoN_Nemesis (4. Jan 2007)

Wenn du mal den "schlechten Code" postest können wir dir höchstwahrscheinlich sagen wo das Problem liegt. An Java2D/Swing jedenfalls nicht. Wie gesagt sollte es für dein Problem sehr angemessen und locker ausreichend sein.


----------



## Paladin (5. Jan 2007)

Also in meinem Beispielprogramm gibt es nur ein Gebäude und ein Fahrzeug. Das Fahrzeug sollte sich mit den Cursortasten links und rechts drehen lassen und mit der Cursortaste Hoch in die entsprechende Richtung fahren.
Das Problem ist jetzt, dass der Bildschirm bei der Darstellung extrem flimmert was höchstwahrscheinlich an der
paint() Methode liegt. Ich nehme an, dass ich das Problem mit dem flimmern durch Double Buffering in den Griff
bekommen könnte aber der Versuch das in meine Paint Methode einzubauen war wenig erfolgreich - daher arbeite
ich zur Zeit auch wieder mit der alten paint Methode.

Und die sieht so aus:


```
public void paint(Graphics g) {
    Graphics2D g2 = (Graphics2D) g;
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
				
    AffineTransform trans_org = new AffineTransform();
    AffineTransform trans = new AffineTransform();
    
    //amucity_lower = BufferedImage    Die Plattform auf der das Gebäude steht
    //acd = Die Klasse AmuCityData welche die Gebäudedaten enthält		
    g2.drawImage(amucity_lower, (int)acd.getXpos(), (int)acd.getYpos(),this);

    //td = Die Klasse TankData welche alle Daten zu dem Fahrzeug enthält				
    trans.translate((int)td.getXAxis(), (int)td.getYAxis());
    trans.rotate((int)td.getRotation());
    trans.translate((int)-td.getXAxis(), (int)-td.getYAxis());
    g2.setTransform(trans);
		
    //tank_solo = BufferedImage    Das Fahrzeug
    g2.drawImage(tank_solo, (int)td.getXpos(), (int)td.getYpos(),this);

    g2.setTransform(trans_org);
		
    //amucity_upper = BufferedImage    Das Gebäude
    g2.drawImage(amucity_upper, (int)acd.getXpos()+acd.getCeilingCorrection_x(), (int)acd.getYpos()
        +acd.getCeilingCorrection_y(),this);
    
}
```

Neben der Klasse paint gibt es noch einen Thread welcher den Screen dauernd neuzeichnet.
Dieser sieht so aus.


```
class painter implements Runnable {

    public void run() {
			
        while(true) {
		
	if(td.getSpeed()!=0) {
	    tank_x_delta = (Math.cos(td.getRotation()/58)*td.getSpeed());
	    tank_y_delta = (Math.sin(td.getRotation()/58)*td.getSpeed());
				
	    if(checkCollision(tank_x_delta,tank_y_delta)==false) { 
	        if(((td.getXpos()+tank_x_delta) > 500)||((td.getXpos()+tank_x_delta) < 200)||((td.getYpos()
                                +tank_y_delta) > 500)||((td.getYpos()+tank_y_delta) < 200)) {
	            acd.setXpos(-tank_x_delta);
	            acd.setYpos(-tank_y_delta);
	        }
	    else {
	        td.setXpos(tank_x_delta);
	        td.setYpos(tank_y_delta);
	    }
                }
            }
            if(td.isBrake()) {
	if(td.getSpeed()>0) td.setSpeed(-0.01);
	else td.setBrake(false);
            }
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
	e.printStackTrace();
            }
            
            repaint();
				
        }
    }
}
```

Ich habe jetzt bewusst darauf verzichtet die Methoden für Fensterbau, Keylistener und laden der Bilder 
darzustellen weil ich denke, dass der Fehler schon bei den beiden oberen Methoden liegt.


----------



## Campino (5. Jan 2007)

Versuch mal alles erst in ein BufferedImage zu zeichnen und das dann auf den Bildschirm, manchmal hilft das. Dann kommt mir der sleep relativ klein vor.


----------



## Paladin (5. Jan 2007)

Ich habe die paint() Funktion jetzt wie folgt abgeändert aber es macht keinen Unterschied:


```
public void paint(Graphics g) {
    Graphics2D g2 = (Graphics2D) g;
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

    //buffer = Graphics2D
    //offscreen = BufferedImage		
    buffer = (Graphics2D) offscreen.getGraphics();
				
    buffer.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
			
    AffineTransform trans_org = new AffineTransform();
    AffineTransform trans = new AffineTransform();
		
    buffer.drawImage(amucity_lower, (int)acd.getXpos(), (int)acd.getYpos(),this);
					
    trans.translate((int)td.getXAxis(), (int)td.getYAxis());
    trans.rotate((int)td.getRotation());
    trans.translate((int)-td.getXAxis(), (int)-td.getYAxis());
    buffer.setTransform(trans);
		
    buffer.drawImage(tank_solo, (int)td.getXpos(), (int)td.getYpos(),this);

    buffer.setTransform(trans_org);
				
    buffer.drawImage(amucity_upper, (int)acd.getXpos()+acd.getCeilingCorrection_x(), (int)acd.getYpos()
            +acd.getCeilingCorrection_y(),this);
    buffer.drawString("Rotation=" + td.getRotation(),10,10);
		
    g2.drawImage(offscreen, 0, 0, this);
}
```


----------



## EgonOlsen (5. Jan 2007)

Was du da machen willst, ist doch so ähnlich wie das hier, oder?: www.java-forum.org/de/viewtopic.php?t=41482
Vielleicht hilft das was...


----------



## Paladin (5. Jan 2007)

Danke für den Hinweis. Ich werde mir den Code mal genau ansehen...


----------



## Paladin (5. Jan 2007)

Hi EgonOlsen,

vielen Dank für deine Hilfe! 
Die Lösung meines Problems heisst BufferStrategy.   

Um das Problem zu lösen habe ich das folgende getan:

1.) Methode paint() und update() wie folgt überschreiben:


```
public void paint(Graphics g) {}

    public void update(Graphics g) {}
```

2.) Eigene paint Methode schreiben in welcher BufferStrategy genutzt wird.
     Die eigene paint Methode wird vom Timer dann immer wieder aufgerufen.


```
public void myUpdate() {
				
    g2 = (Graphics2D) bufstrat.getDrawGraphics();
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
					
    AffineTransform trans_org = new AffineTransform();
    AffineTransform trans = new AffineTransform();
		
    g2.drawImage(amucity_lower, (int)acd.getXpos(), (int)acd.getYpos(),this);
					
    trans.translate((int)td.getXAxis(), (int)td.getYAxis());
    trans.rotate((int)td.getRotation());
    trans.translate((int)-td.getXAxis(), (int)-td.getYAxis());
    g2.setTransform(trans);
		
    g2.drawImage(tank_solo, (int)td.getXpos(), (int)td.getYpos(),this);

    g2.setTransform(trans_org);
				
    g2.drawImage(amucity_upper, (int)acd.getXpos()+acd.getCeilingCorrection_x(), (int)acd.getYpos()
            +acd.getCeilingCorrection_y(),this);
    		
    bufstrat.show();		
		
}
```

3.) BufferStrategy nach der Erstellung der GUI und start des Timers initialisieren:


```
public Main_Swing() {
    loadImages();                                             //Grafiken für das Spiel laden
    timer = new Thread(new painter());              //Timer initialisieren
    timer.setPriority(Thread.MIN_PRIORITY);
    buildGUI();                                                  //GUI bauen
    timer.start();                                               //Timer starten
    super.createBufferStrategy(2);                     
    bufstrat = super.getBufferStrategy();            //BufferStrategy initialisieren
}
```

Gruß

Paladin


----------

