# Kleines Game



## Kenan89 (26. Mrz 2012)

Hallo, ich würde gerne mit Java ein Spiel pogrammieren und habe ein paar Fragen dazu.

Ich habe vor auf einem Frame Objecte zu platzieren und zu bewegen. Das ist die Grundidee.

Grafiken kann ich schon mit paintComponent ladn und anzeigen. Problem ist, dass ich über dem JFrame ein JPanel habe mit einem Bild drauf, welches die Fläche darstellen soll. Darauf soll dann ein Strichmännchen geladen werden. seitlich daneben zu laden ist kein Problem aber wie kriege ich das Bild auf die Fläche? Denn die Fläche liegt momentan über dem Bild.

Danke im voraus.


----------



## maestr0 (26. Mrz 2012)

Kannst du bitte einen kleinen Codeauszug posten?
Komm nicht ganz dahinter was du nun genau meinst 
Denke aber,dass das die Geschichte mit getContentPane() ist.
Mit getContentPane() platzierst du direkt Komponenten auf deinem Frame.

Wenn das dein erstes Java-Spiel ist,kannst du auch gerne auf meiner Homepage unter Tutorials->Text-Tutorials->Spieleprogrammierung in Java nachschauen.Vielleicht findest du dort etwas interessantes.


----------



## Robokopp (26. Mrz 2012)

mit g.drawImage(...) kannst du Bilder zeichnen.
Wenn du es mit den Parametern (0,0,panelbreite,panelhöhe) befüllst, sollte es den gesamten Hintergrund überdecken


----------



## Fu3L (26. Mrz 2012)

> Ich habe vor auf einem Frame Objecte zu platzieren und zu bewegen. Das ist die Grundidee.



Das ist nicht ganz die beste Idee. Erbe lieber von einem JPanel und füge deises auf der ganzen Größe des Frames hinzu (einfach per frame.add()).
Auf diesem malst du dann in paintComponent() erst den Hintergrund und dann deine Objekte. So wird der Hintergrund nicht deine Objekte überdecken.

Ich kann nichts zu "maestr0's" Tutorial sagen, aber das hier im Forum von Quaxlie ist wirklich empfehlenswert für Anfänger  http://www.java-forum.org/spiele-multimedia-programmierung/54795-quaxli-2d-spiele-tutorial.html



> mit paintComponent ladn



Sollte das kein flüchtiges Schreiben gewesen sein: Lade die Bilder lieber vorher und nicht erst in der paintComponent() Methode


----------



## irgendjemand (26. Mrz 2012)

für ebenen eignet sich auch noch [japi]JLayeredPane[/japi]


----------



## Kenan89 (26. Mrz 2012)

Habe jetzt auf den JFrame ein JPanel platziert und dadrauf mein  Strichmännchen mit JLayeredPane platziert. Auch die Bewegung klappt mit den Pfeiltasten(KeyListener).
Ich verwende zum Bewegen die Funktion move()... Diesen streicht Eclipse durch, da sie wohl "veraltet" ist. Was ist denn die "neue" Art, Objekte zu bewegen?
Code:

```
if(richtung.equals("links")){
			character.move(((int)x-3), (int)y);//move() wird in Eclipse durchgestrichen
		}
```


----------



## vanny (26. Mrz 2012)

Es genügt ein einfaches JPanel.
Erstell dir eine Methode, die dir erst dein Hintergrund und dann das Strichmännchen(entw. mit Pfaden/Lines oder mit einem Shape/Alphakram) an die richtige Stelle in ein BufferedImage zeichnet.

in der painComponent(); zeichnest du dann nur das BufferedImage in dein JPanel.

Gruß Vanny

PS: Beispiele dazu findest du hier im Forum eigentlich im wöchentlichen Abstand zu genüge.


----------



## Kenan89 (26. Mrz 2012)

Das Erstellen klappt ja. Nur das Bewegen mit move()(siehe mein Post oben) wird von Eclipse durchgestrichen weil es wohl veraltet ist.
Momentan sitze ich an der Kollisionserkennung mit anderen Objekten. 
Ich habe das so geplant, aber vielleicht gibt es eine effizientere Lösung:
Immer wenn eine Taste gedrückt wird, wird verglichen, ob sich die XY Koordinaten vom Strichmännchen mit denen der anderen Objekte schneidet. Wenn ja, wird die Bewegung nicht durchgeführt. Leider kommt es mir uneffizient vor, da ja auf einem JPanel quasi hunderte Objekte platziert werden können und ich kann ja nicht bei jedem Druck auf die Pfeiltasten prüfen, ob sich die Koordinaten überschneiden.
Was gibt es für Lösungsmöglichkeiten?


----------



## vanny (26. Mrz 2012)

Da gibt es verschiedene Möglichkeiten.
zBsp. kannst du die Kollisionsbereiche der fixen Objekte (Mauern, Bäume, etc.) in eine Liste speichern, über die du itterierst und schaust, ob ja oder nein.
move(); solltest du dir selbst schreiben und dementsprechend das in meinem vorherigen post erwähnte BufferedImage zeichnen.

Zieh dir Quaxlis Tutorial rein, denn mit LayeredPanes rumzuwurschteln, hat mit effizient nicht viel zu tun.

Gruß Vanny


----------



## Fu3L (27. Mrz 2012)

Ja, es scheint mir, als nutztest du zB JLabels oder der gleichen zum Zeichnen der Männchen... GUI Komponenten dafür zu verwenden, stellt jede Kollisionserkennung in den Schatten, wenn du wirklich "mehrere hundert Objekte" haben solltest )

Edit: Nochmal ganz deutlich: Vergiss JLayeredPane


----------



## Kenan89 (27. Mrz 2012)

Jedes meiner Objekte, z.b. charactere oder häuser befindet sich in einem JPanel. Schlechte Idee oder=?


----------



## Fu3L (27. Mrz 2012)

Kurz und knapp: Ja^^
Für sowas sind GUI Komponenten nicht ausgelegt, schleppen viel zu viel Ballast mit sich rum und das Zeichnen ist auch wesentlich aufwendiger, als wenn du einfach sagst: "Bild auf Panel"^^


----------



## Kenan89 (27. Mrz 2012)

Welche Komponenten sind dafür ausgelegt. Das Tutorial ist nicht schlecht, aber viel zu langatmig und mit vollen Informationen, die ich gar nicht brauche etc...


----------



## vanny (27. Mrz 2012)

Liest du auch die Antworten hier?...

JPanel und fertig!

PS: Wenn dir das Tutorial zu viel Information enthält, dann filter die für dich wichtigen Sachen raus, oder lass es sein mit dem Spieleprogrammieren.
Einzelunterricht wirste hier nich bekommen.:autsch:

Gruß Vanny


----------



## Kenan89 (27. Mrz 2012)

Na gut auf einem JFrame bewegen sich jetzt per Zufall ca 20 Objecte, welche jeweils
JPanels sind.
Jetzt ist die Kollisionserkennung dran. Die leichteste Variante ist wohl, um die Sprites ein Rectangle zu zeichnen. Aber wie funktioniert die Überprüfung der Kollisionen?
Liege ich richtig in der Annahme, dass ich jedes Object mit jedem Object auf Kollision prüfen muss?

Bei 20 bewegenden Objecten wären das über 100 ifs


----------



## Marco13 (27. Mrz 2012)

Ja: 

```
if (anzahlObjekte == 2)
{
    teste(objekt0, objekt1);
}
if (anzahlObjekte == 3)
{
    teste(objekt0, objekt1);
    teste(objekt1, objekt2);
    teste(objekt0, objekt2);
}
if (anzahlObjekte == 4)
{
    teste(objekt0, objekt1);
    teste(objekt1, objekt2);
    teste(objekt2, objekt3);
    teste(objekt0, objekt2);
    teste(objekt0, objekt3);
    teste(objekt1, objekt3);
}
...
```

:joke:

Im ernst: Sowas wie

```
for (int i=0; i<objekte.size(); i++)
{
    for (int j=i+1; j<objekte.size(); j++)
    {
        teste(objekte.get(i), objekte.get(j));
    }
}
```
Sollte es tun. Ggf. muss man noch nach Objektarten unterscheiden, aber grundsätzlich läuft's wohl auf zwei verschachtelte for-Schleifen raus.


----------



## Kenan89 (27. Mrz 2012)

Ja schon, mein Denkfehler  Danke


----------



## Fu3L (27. Mrz 2012)

> Bei 20 bewegenden Objecten wären das über 100 ifs





> Na gut auf einem JFrame bewegen sich jetzt per Zufall ca 20 Objecte, welche jeweils
> JPanels sind.



Du "hörst" nicht richtig zu und bist evtl. etwas voreilig das Tutorial als "langatmig und mit vollen Informationen, die [du] gar nicht brauche[st]" abzutun... Denke schon, dass du das Tutorial brauchst, von Anfang bis Ende.


----------



## Kenan89 (28. Mrz 2012)

Ich habe das Problem, dass sich meine Figur nur in einem bestimmten Bereich des Panels, worauf sie geaddet wurde bewegt...
Es ist so aufgebaut:

JFrame
-->Panel mit Hintergrundbild
---->Panel mit Characterbild( diese Panel ist so groß, wie das Bild pixelhat)

Layout vom Panel mit Hintergrundbild ist null.


----------



## Kenan89 (28. Mrz 2012)

Hier ist der Code dazu...
1. Es soll ein Bild mit 500x500px (grass.png) geladen und dem Frame hinzugefügt werden
2. Danach soll die Grafik chara geladen und ebenfalls Frame hinzugefügt werden

Jetzt ist es so, dass nur der angezeigt wird, der zuletzt hinzugefügt wurde. Ich habe mich bereits schlau darüber im Netz gemacht.
Die Variante mit JLayeredPane wird wohl nicht gern benutzt.
Dann gibt es ncoh die Variante mit setComponentZOrder(), aber konnte ihn nich einsetzen, da ich keine Ahnung hatte, wie das geht und auch kein Beispiel gefunden habe

Jedenfalls der Code bisher: 


```
public class Character extends JPanel{

	BufferedImage chara = null;
	int width, height = 0;
	
	public void getImg(String path){
		try{
			chara = ImageIO.read(new File(path));
			width = chara.getWidth();
			height = chara.getHeight();
		}
		catch(IOException e){
			e.printStackTrace();
		}
	}
	
	public void paintComponent(Graphics g){
		Graphics2D g2d = (Graphics2D) g;
		g2d.drawImage(chara, 0, 0, this);	
	}
	
	public Character(){
		getImg("graphics/chars/char1.png");
		this.setPreferredSize(new Dimension(width, height));
	}
	
}
```


```
public class GamePanel extends JPanel{
	
	public static void main(String[] args){
		new GamePanel();
	}
	
	JFrame frame = new JFrame("Game");
	int frameHeight = 500;
	int frameWidth = 500;
	
	int width, height = 0;
	
	BufferedImage picture;
	
	public void getImg(String path){
		try{
			picture = ImageIO.read(new File(path));
			width = picture.getWidth();
			height = picture.getHeight();
		}
		catch(IOException e){
			e.printStackTrace();
		}
	}
	
	@Override
	public void paintComponent(Graphics g){
		Graphics2D g2d = (Graphics2D) g;
		g2d.drawImage(picture, 0, 0, this);	
	}
	
	public GamePanel(){
			
		getImg("graphics/bg/grass.png");
		this.setPreferredSize(new Dimension(width, height));
		
		Character chara = new Character();
		
		frame.add(this);
		frame.add(chara);
		
		frame.setSize(frameWidth, frameHeight);
		frame.setLocationRelativeTo(null);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setVisible(true);
	}
}
```


----------



## Fu3L (28. Mrz 2012)

Entweder es liegt am BorderLayout des Frames und es wird irgendwo an den Rand gequetscht und ist kaum zu sehen oder die letzte Komponente ersetzt die vorherige.

Mein Rat bleibt immer noch bestehen: Nutze keine Komponenten außer ein einziges JPanel auf dem du alles - auch den Hintergrund - zeichnest. Du wirst sonst früher oder später noch mehr Probleme damit erleben. Und ob du nun schreibst: "panel.setBounds(x, y, width, height)" oder was für ne Methode es sonst so gibt, zum Bewegen von Panels oder ob du schreibst: "spielfigur.moveBy(x, y)" und dann in paintComponent() über die Spielfiguren iterierst und dort "drawImage(figur.getImage(), figur.x, figur.y, null)" aufrufst, dürfte doch keinen großen Unterschied machen.


----------



## Kenan89 (29. Mrz 2012)

Ach ja und denkt nicht ich hba das tutorial übersprungen. Ich habe es schon gemacht, zumindest bis Seite 30 aber ich will es nicht mit Vectoren sondern, dass bei mir jedes Object auf dem Bildschirm ein JPanel darstellt, der sich auf der untersten Ebene, in diesem Fall dem Rasen, frei bewegen kann.


----------



## Kenan89 (29. Mrz 2012)

Okay, es kommt mir logischer vor, nur ein JPanel zu verwenden und darauf alle Objekte mit paintComponent zu zeichnen:

Jetzt habe ich nur eine Klasse: GamePanel.class


```
public class GamePanel extends JPanel{
	
	public static void main(String[] args){
		new GamePanel();
	}
	
	JFrame frame = new JFrame("Game");
	int frameHeight = 500;
	int frameWidth = 500;
	
	int width, height = 0;
	
	BufferedImage img;
	
	public void getImg(String path){
		try{
			this.img = ImageIO.read(new File(path));
			width = this.img.getWidth();
			height = this.img.getHeight();
			repaint();	
		}
		catch(IOException e){
			e.printStackTrace();
		}
	}
	
	@Override
	public void paintComponent(Graphics g){
		Graphics2D g2d = (Graphics2D) g;
		g2d.drawImage(img, 0, 0, this);	
	}
	
	public GamePanel(){
			
		getImg("graphics/bg/grass.png");		
		getImg("graphics/chars/char1.png");	
		
		this.setPreferredSize(new Dimension(width, height));

		frame.add(this);		
		frame.setSize(frameWidth, frameHeight);
		frame.setLocationRelativeTo(null);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setVisible(true);
	}
}
```

Folgendes Problem ergibt sich dadurch:
Die Bilder liegen nicht übereinander, d.h. das zu letzt gezeichnete ersetzt den davor gezeichneten.


----------



## Kenan89 (29. Mrz 2012)

Das Problem an meinem Code im vorherigen Post ist einfach, dass in der Klasse GamePanel() 2x paintComponent gerufen wird, wobei jedesmal img gezeichnet wird. Logischerweise überschreibt der letzte Aufruf von paintComponent() das img, welches davor gezeichnet wurde, da ja jetzt ein neues gezeichnet wird. 

Jetzt kommt die Frage: Brauche ich für jeden Objekt ein JPanel? Sprich für den Spieler 1 JPanel mit einer paintComponent Methode, und für einen Non-Player-Character einen weiteren? Das wären auf einem GamePanel mit 20 Spielfiguren 20 Klassen?


----------



## Fu3L (29. Mrz 2012)

> dass in der Klasse GamePanel() 2x paintComponent gerufen wird, wobei jedesmal img gezeichnet wird. Logischerweise überschreibt der letzte Aufruf von paintComponent() das img, welches davor gezeichnet wurde, da ja jetzt ein neues gezeichnet wird.



Theoretisch kann paintComponent beliebig oft aufgerufen werden, das kannst du selbst mit repaint() zwar etwas steuern, aber generell macht Swing das wie Swing das will.
Wenn du nun ein JPanel hast und eine painComponent(Graphics g) Methode und dort schreibst du:

```
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(img, 0, 0, this);
g2d.drawImage(img2, 2, 2, this);
```
Dann werden von img noch immer die obersten zwei Zeilen und die 2 Spalten links zu sehen sein. 
D.h.: Alle deine Zeichnungen müssen in eine painComponent()-Methode. Und seis dadurch, dass du dort für jedes Objekt eine Methode aufrufst und der das Graphics Objekt übergibst. Wie zB in Quaxlies Tutorial. Oder du schreibst einen "Renderer", dem du Graphics aus der paintComponent() übergibst.


----------



## Kenan89 (31. Mrz 2012)

Ich habe jetzt diesen Ansatz genommen:
In ein Object[][] obj Array speichere ich folgende Daten:

```
BufferedImage img;
Object[][] obj = new Object[2][6];

public void initialPictures(){
 img = ImageIO.read(new File("xy.png"));
 obj[0][0] = "xy.png";
 obj[0][1] = img;
 obj[0][2] = 20;  //posX
 obj[0][3] = 40;  //posY
 obj[0][4] = img.getWidth(); //bildbreite
 obj[0][5] = img.getHeight(); //bildhöhe

 img = ImageIO.read(new File("xy2.png"));
 obj[1][0] = "xy2.png";
 obj[1][1] = img;
 obj[1][2] = 20;  //posX
 obj[1][3] = 40;  //posY
 obj[1][4] = img.getWidth(); //bildbreite
 obj[1][5] = img.getHeight(); //bildhöhe
 repaint();
}

public void paintComponent(Graphics g){
 Graphics2D g2d = (Graphics2D) g;
 for(int i=0; i<obj.length; i++){
  g2d.drawImage(obj[i][1], obj[i][2], obj[i][3], this)
; }
}
```

Problem an dieser Methode ist, wie ich in drawImage obj_[1] in ein Image umwandelt...
EDIT: gut das problem hat sich erledigt, indem ich einfach geschrieben habe (BufferedImage)obj[1]
Aber ein weiteres Problem ist, wie ich die posX und posY in ein int umwandle für g2d.drawImage
Ich habe folgendes versucht:
-das Object in ein String umgewandelt, dadurch müsste im obigen Beispiel aus 20 ein String mit dem Inhalt "20" werden. Diesen dann in ein int umgewandelt. Leider erscheint in meinem String(herausgefunden mit println) "null". SPrich im Object steht zwar 20 aber nach dem casten in ein String erhalte ich null. Woran liegt das und wie kann man das lösen?
EDIT: Oh ok, ich habe mit String.valueOf(obj) umgewandelt. korrekt wäre es wohl mit obj.toString()

EDIT:
Habe die ursache des Problems herausgefunden, bzw. was ich falsch mache. die werte für den Object[][] werden in einer anderen klasse gesetzt und mit get in mein klasse mit paint component übernommen. also sollte theoretisch so sein.

Ich habe 2 Klassen:


		Java:In die Zwischenablage kopieren


1. GP
Object objects[][];
Obj obj = new Obj();

public GP(){
 objects = Obj.getObjects(); 
}

paintComponent(){...}

2. Obj

Object objects = new Object[1][6];

public void getObjects(){return objects;}


Problem ist wohl die zuweisung der return Methode an das Object-2d-array in meiner klasse GP._


----------



## Fu3L (31. Mrz 2012)

Erstens, wird nach der Zuweisung an ein Object Array aus einem int ein Integer, daher musst du ((Integer) obj[a]*).inValue().

Zum anderen: Weißt du wofür es Klassen gibt in Java? Damit man keine Object Arrays erzeugen muss, die so unleserlich sind und so unperformant und so bug-garantierend und....^^

Ein Object in deinem Spiel hat durch eine Klasse repräsentiert zu sein:



		Java:In die Zwischenablage kopieren


public class CoolesMonster {
private int x;
private int y;
private BufferedImage img;

//Hier GETTER UND SETTER und Konstruktor


Und die Bildbreite und Höhe brauchst du nicht extra speichern, die sind durch img.getWidth() erreichbar.

Wie willst du mit Object[][] den Überblick behalten? Bzw. variable Anzahlen an Spielfiguren ermöglichen?*


----------

