# Bild berührt Bild



## Krypthor (29. Nov 2011)

Hi,
ich bin dabei mir ein kleines Spiel zum Test zu machen,
allerdings bin ich gerade auf ein Problem gestoßen;
Ich hab 2 Spieler (der eine lenkt mit Pfeiltasten der andere mit wasd),
und wenn die sich berühren soll der Berührte bzw. der auf den es festgelegt ist, unsichtbar werden.
Ich hab keine Ahnung wie man das macht, dass wenn ein Pixel eines Bildes den Pixel eines anderen Bildes berührt, eine Aktion stattfindet, deshalb hab ich das etwas umständlich geplant (ich bin Anfänger ).
Und ich weis nicht wenn man ein Bild hat, wo die "Haupt" Koordinate dieses Bildes ist,
wird es ab unten links, oben links oder ab der Mitte an gezeichnet?

Wenn es ab unten links an wäre hätte ich es so geplant:

```
if((p.x * p.y >= (p2.x * p2.y)) && (p.x * p.y <= ((p2.x + 10) * (p2.y + 10)))){
//Code
}
```
10 * 10 ist das Bild groß, p ist Player1 und p2 ist Player2.
Wenns ab der Mitte gezeichnet würde hätte ich das Bild in 4 Teile zerlegt.

Also;
- von wo wird das Bild gezeichnet (unten links,mitte....)?
- gibt es eine einfachere Methode bzw. eine die ich auch als Anfänger verstehe?

Vielen Dank schonmal für Hilfe,
Krypthor


----------



## Marco13 (29. Nov 2011)

Es wird von oben links aus gezeichnet. Für den Überschneidungstest... den kann man per Hand (ziemlich trivial) selbst implementieren, aber ... wenn du für deine Spiel-Objekte eine eigene Klasse hast (*räusper* ) könnte es eine Methode geben
boolean collidesWith(GameObject other) { ... }
die man implementieren kann, wie man will - im zweifelsfall kann man dann auch zwei Rectangle-Objekte erstellen, und mit 
this.rectangle.intersects(other.rectangle);
feststellen, ob sie sich überschneiden.


----------



## Krypthor (29. Nov 2011)

Cool, ich weis nur noch nicht wie und wo ich das jetzt einfügen muss^^
also ich hab zwei klassen für Player1 und 2 und ein "Interface" wo alles zusammengefügt wird.


----------



## Marco13 (30. Nov 2011)

Krypthor hat gesagt.:


> also ich hab zwei klassen für Player1 und 2 und ein "Interface" wo alles zusammengefügt wird.



Falls es noch Fragen gibt: Beschreib das mal genauer (ggf. mit ein paar Zeilen Code...)


----------



## Ralf.Rutke (30. Nov 2011)

Gleiche Frage, nur

ich hab eine Actor-Klasse, eine Mob-Klasse und eine Level-Klasse

jetzt möchte ich erst mal die kollision zwischen einem Objekt der Mob-Klasse mit der Actor-Klasse prüfen

ist es sinnvoll dies in der Actor-Klasse zu machen oder eher in der Mob-Klasse ?!
da die Actor-Klasse ja dann ganz viele Mob-Objekte überprüfen müsste,
oder jedes Mob-Object einmal mit der Actor-Klasse


----------



## Krypthor (1. Dez 2011)

irgendwie klappt das bei mir nicht,
ich hab in der klasse von spieler 1 jetzt stehen :

```
public Rectangle getBounds(){
		return new Rectangle(x, y, 14, 18);
	}
```
und von spieler 2 das gleiche.

Im Interface also da wo alles zusammengefügt wird hab ich das stehen

```
public void checkCollisions(){
		Rectangle r1 = p.getBounds();
        Rectangle r2 = p2.getBounds();
        if (r1.intersects(r2)){System.out.print("test");}
        if(r2.intersects(r1)){System.out.print("test2");
        }
	}
```
klappt aber nicht?!


----------



## Marco13 (1. Dez 2011)

Wetten, dass es klappt, wenn ich ein KSKB drumstricke?


----------



## Krypthor (1. Dez 2011)

Okay 
Wär gut wennde mir das nochmal erklären könntest


----------



## Michael... (1. Dez 2011)

Fang doch einfach mit Kreisen an. Wenn der Abstand der Mittelpunkte zueinander kleiner als die Summe der Radien ist kollidieren diese.


----------



## Marco13 (1. Dez 2011)

Naja, ob Rechtecke oder Kreise ist da ja fast egal... wenn es um Bilder geht, kann man gleich Rechtecke nehmen. 

Poste mal ein stück compilier- und ausführbaren Code, in dem man das (unzureichend beschriebene) "klappt nicht" nachvollziehen kann. Sicher, dass du nicht z.B. Component.getBounds überschrieben hast oder so...?


----------



## Krypthor (2. Dez 2011)

Naja am besten wirds wohl sein ich poste einfach mal meinen gesamten Code:

Start:

```
import javax.swing.JFrame;
import java.awt.*;
import java.awt.event.*;

import javax.swing.*;


public class Start {
	
	public Start(){
		JFrame frame1 = new JFrame();
		frame1.add(new Interface());
		frame1.setLocation(100,100);
		frame1.setSize(600,400);
		frame1.setTitle("War");
		frame1.setResizable(false);
		frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame1.setVisible(true);
	}

public static void main(String[] args){
	new Start();
	}
}
```

Player:

```
import java.awt.Image;

import javax.swing.ImageIcon;
import java.awt.*;
import java.awt.event.*;

import javax.swing.*;


import java.awt.Image;
import java.awt.event.KeyEvent;
import java.util.ArrayList;

import javax.swing.ImageIcon;

public class Player{
	int x, y, dx, dy, nx, nx2, left;
	Image still;
	ImageIcon i = new ImageIcon("Bilder/Spielersteht.png");
	ImageIcon ls = new ImageIcon("Bilder/SpielerseiteLsteht.png");
	ImageIcon rs = new ImageIcon("Bilder/SpielerseiteRsteht.png");
	ImageIcon ud = new ImageIcon("Bilder/Spieler.gif");
	ImageIcon l = new ImageIcon("Bilder/SpielerseiteL.gif");
	ImageIcon r = new ImageIcon("Bilder/SpielerseiteR.gif");
	
	public Player(){
		still = i.getImage();
		x = 30;
		y = 180;
	}
	
	public Rectangle getBounds(){
		return new Rectangle(x, y, 14, 18);
	}
	
	public void move(){	
		if (x <= 0){
			x = x + 1;
		}
		if(x >= 580){
			x = x - 1;
		}
		if (y <= 0){
			y = y + 1;
		}
		if(y >= 355){
			y = y - 1;
		}
		if(x == 122 && y >= 49 && y <= 322){
			x = x - 1;
		}
			x = x + dx;
			y = y + dy;
	}
	
	
	public int getX(){
		return x;
	}
	public int getY(){
		return y;
	}
	public Image getImage(){
		return still;
	}

	public void keyPressed(KeyEvent e){
		int key = e.getKeyCode(); 
	
		if (key == KeyEvent.VK_LEFT){
			dx = -1;
			still = l.getImage();
		}
		if (key == KeyEvent.VK_RIGHT){
			dx = +1;
			still = r.getImage();
		}
		if (key == KeyEvent.VK_UP){
			dy = -1;
			still = ud.getImage();
		}
		if (key == KeyEvent.VK_DOWN){
			dy = +1;
			still = ud.getImage();
		}
		if (key == KeyEvent.VK_P){
		}
	}

	public void keyReleased(KeyEvent e){
		int key = e.getKeyCode(); 
	
		if (key == KeyEvent.VK_LEFT){
			dx = 0;
			still = ls.getImage();
		}
		if (key == KeyEvent.VK_RIGHT){
			dx = 0;
			still = rs.getImage();
		}
		if (key == KeyEvent.VK_UP){
			dy = 0;
			still = i.getImage();
		}
		if (key == KeyEvent.VK_DOWN){
			dy = 0;
			still = i.getImage();
		}
		//System.out.print(x + "=x ");
		//System.out.print(y + "=y ");
	}
	
}
```

Player2:

```
import java.awt.Image;
import java.awt.Rectangle;

import javax.swing.ImageIcon;
import java.awt.*;
import java.awt.event.*;

import javax.swing.*;


import java.awt.Image;
import java.awt.event.KeyEvent;

import javax.swing.ImageIcon;

public class Player2{
	int x, y, dx, dy, nx, nx2, left;
	Image still2;
	ImageIcon i = new ImageIcon("Bilder/Spielersteht.png");
	ImageIcon ls = new ImageIcon("Bilder/SpielerseiteLsteht.png");
	ImageIcon rs = new ImageIcon("Bilder/SpielerseiteRsteht.png");
	ImageIcon ud = new ImageIcon("Bilder/Spieler.gif");
	ImageIcon l = new ImageIcon("Bilder/SpielerseiteL.gif");
	ImageIcon r = new ImageIcon("Bilder/SpielerseiteR.gif");

	public Player2(){
		still2 = i.getImage();
		x = 550;
		y = 180;
	}
	
	public Rectangle getBounds(){
		return new Rectangle(x, y, 14, 18);
	}

	public void move(){	
		if (x <= 0){
			x = x + 1;
		}
		if(x >= 580){
			x = x - 1;
		}
		if (y <= 0){
			y = y + 1;
		}
		if(y >= 355){
			y = y - 1;
		}
			x = x + dx;
			y = y + dy;
	}
	
	
	public int getX(){
		return x;
	}
	public int getY(){
		return y;
	}
	public Image getImage(){
		return still2;
	}

	public void keyPressed(KeyEvent e){
		int key = e.getKeyCode(); 
	
		if (key == KeyEvent.VK_A){
			dx = -1;
			still2 = l.getImage();
		}
		if (key == KeyEvent.VK_D){
			dx = +1;
			still2 = r.getImage();
		}
		if (key == KeyEvent.VK_W){
			dy = -1;
			still2 = ud.getImage();
		}
		if (key == KeyEvent.VK_S){
			dy = +1;
			still2 = ud.getImage();
		}
	}

	public void keyReleased(KeyEvent e){
		int key = e.getKeyCode(); 
	
		if (key == KeyEvent.VK_A){
			dx = 0;
			still2 = ls.getImage();
		}
		if (key == KeyEvent.VK_D){
			dx = 0;
			still2 = rs.getImage();
		}
		if (key == KeyEvent.VK_W){
			dy = 0;
			still2 = i.getImage();
		}
		if (key == KeyEvent.VK_S){
			dy = 0;
			still2 = i.getImage();
		}
	}
}
```

Interface:

```
import java.awt.*;
import java.awt.event.*;

import javax.swing.*;

import java.awt.Image;
import java.util.ArrayList;

public class Interface extends JPanel implements ActionListener{
Player p;
Player2 p2;
Image img2;
Timer time;

	public Interface(){
		p = new Player();
		p2 = new Player2();
		addKeyListener(new AL());
		addKeyListener(new AL2());
		setFocusable(true);
		ImageIcon i = new ImageIcon("Unbenannt.png");
		img2 = i.getImage();
		time = new Timer(5, this); 
		time.start();
	}
	
	public void actionPerformed(ActionEvent e) {
		p.move();
		p2.move();
		repaint();
	}
	
	public void checkCollisions(){
		Rectangle r1 = p.getBounds();
        Rectangle r2 = p2.getBounds();
        if (r1.intersects(r2)){
        	System.out.print("jo");
		}
        if(r2.intersects(r1)){
        	System.out.print("jo");
        }
	}
	
	public void paint(Graphics g) {
		
		super.paint(g);
			Graphics2D g2d = (Graphics2D) g;
			g2d.drawImage(img2, 0, 0, null);
			g2d.drawImage(p.getImage(), p.x, p.y, null);
			g2d.drawImage(p2.getImage(), p2.x, p2.y, null);
	}
	
	private class AL extends KeyAdapter{
		public void keyReleased(KeyEvent e){
			p.keyReleased(e);
		}
		public void keyPressed(KeyEvent e){
			p.keyPressed(e);
		}
	}
	private class AL2 extends KeyAdapter{
		public void keyReleased(KeyEvent e){
			p2.keyReleased(e);
		}
		public void keyPressed(KeyEvent e){
			p2.keyPressed(e);
		}
	}
}
```


----------



## Marco13 (2. Dez 2011)

:autsch: Immer, wenn man denkt, ... *durchatme*... Es sollte nur EINE Spieler-Klasse geben. Die beiden Klassen sind ja komplett gleich - es sind nur zwei unterschiedliche Objekte der gleichen Klasse, mit unterschiedlichen Ausprägungen der Eigenschaften, aber das eine ist ein Spieler und das andere ist ein Spieler... Auch sonst liegt da einiges im Argen, aber zur eigentlichen Frage: Laut Browsersuche (STRG+F) wird "checkCollisions" nirgendwo aufgerufen.........


----------



## Krypthor (2. Dez 2011)

ah das mit dem berühren hab ich jetzt hinbekommen, aber was ist daran falsch mit zwei spielerklassen zu arbeiten?
Ist doch eigentlich nur umständlicher?!
Wie könnte ich mir denn eine sparen?


----------



## Marco13 (2. Dez 2011)

Indem man die zweite löscht  Im Ernst: Der Sinn von Klassen ist ja, dass man beschreibt, wie eine bestimmte _Klasse_ von Objekten aufgebaut ist. Wenn man ein MMOPRG mit 100000 Spielern hat, will man ja nicht 100000 Klassen erstellen, sondern EINE, von der es dann 100000 instanzen gibt. In diesem Fall sollte irgendwo sowas stehen wie

```
Player player0 = new Player("red");
Player player1 = new Player("green");
```
oder so.

Und es geht noch weit darüber hinaus: Ein wichtiger Aspekt bei der Objektorientierten Programmierung ist es, Gemeinsamkeiten zwischen Klassen zu finden, und diese Abstrakt zu beschreiben. Z.B. könnte es ja sein, dass du ein Spiel schreiben willst, wo es mehrere Spieler geben kann (d.h. mehr als ZWEI ) und es sowohl Computer- als auch menschliche Spieler geben kann. Dann würde man sowas machen wie

```
interface Player
{
    void paintThisPlayerInto(Graphics g);
    Point getPosition();
    void moveOneStep();
}
```
oder was eben ALLE Spieler gemeinsam haben, und dann z.B. eine abstrakte Basisklasse erstellen

```
abstract class AbstractPlayer implements Player
{
    void paintThisPlayerInto(Graphics g) { ... implementieren ... }
    Point getPosition() { ... implementieren ... }

    abstract void moveOneStep(); // Die bleibt noch abstrakt
}
```
und als konkrete Implementierungen dann

```
class HumanPlayer extends AbstractPlayer implements Player
{
    void moveOneStep() {
        // mache Bewegung mit einem KeyListener
    }
}

class ComputerPlayer extends AbstractPlayer implements Player
{
    void moveOneStep() {
        // mache Bewegung mit einer KI
    }
}
```

Das entscheidende ist, dass man dann z.B. 10 Spieler gegeneinander spielen lassen kann

```
List<Player> players = new ArrayList<Player>();
for (int i=0; i<9; i++)
{
    players.add(new ComputerPlayer()); // 9 Computergegner
}
players.add(new HumanPlayer()); // und ein menschlicher Spieler
```

Ohne dass man 10 Klassen erstellen muss

```
class ComputerPlayer0 {
    // 1000 Zeilen code
}

class ComputerPlayer1 {
    // 1000 Zeilen code - die gleichen wie oben
}

class ComputerPlayer2 {
    // 1000 Zeilen code - die gleichen wie oben
}

...
```

Das "schwierigste" in diesem Fall ist es, die KeyListener richtig mehreren menschlichen Spielern zuzuordnen, aber das ist auch kein Hexenwerk....


----------



## Krypthor (2. Dez 2011)

Hört sich logisch an, ich werds aber erst nochmal so lassen wie ich es habe, da ich einfach noch nicht genug Erfahrungen mit der Programmierung habe um sowas umzusetzen.
Aber vielen Dank für deine Hilfe!

EDIT:
Noch mal ne kurze Frage,
ich habe versucht ein Menü zu erstellen aber es hat irgenwie nicht ganz geklappt.
Das Menü verschwindet zwar und die Spielfläche wird sichtbar, aber man kann den Spieler nicht bewegen?!


```
import javax.swing.JFrame;
import java.awt.*;
import java.awt.event.*;

import javax.swing.*;


public class Start {
	
	ImageIcon img11 = new ImageIcon("Bilder/Start.png");
	
	public Start(){
		
		final JLabel labelstart = new JLabel(img11);
		labelstart.setBounds(10, 10, 100, 30);
		labelstart.setVisible(true);
		
		final JFrame frame1 = new JFrame();
		frame1.setLocation(100,100);
		frame1.setSize(600,400);
		frame1.setTitle("War");
		frame1.setResizable(false);
		frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame1.add(labelstart);
		frame1.setVisible(true);
		
		
		
		labelstart.addMouseListener(new MouseListener() {

			@Override
			public void mouseClicked(MouseEvent e) {

				frame1.add(new Interface());
				labelstart.setVisible(false);
			}

			@Override
			public void mouseEntered(MouseEvent e) {
				
			}

			@Override
			public void mouseExited(MouseEvent e) {
				
			}

			@Override
			public void mousePressed(MouseEvent e) {
				
			}

			@Override
			public void mouseReleased(MouseEvent e) {
				
			}
			
		});
	}

public static void main(String[] args){
	new Start();
	}
}
```


----------



## Marco13 (2. Dez 2011)

Klick' ggf mal kurz in die Compoenent, wo der KeyListener dranhängt. Wenn der KeyListener danach geht, ist's ein Focusproblem, da kann man ggf. mit component.requestFocus() was machen.


----------



## Krypthor (2. Dez 2011)

Ne geht trozdem nicht....ich wüsste auch nicht woran es liegen könnte?!
Ich kenn mich da leider noch nicht genug aus.


----------



## Marco13 (2. Dez 2011)

Hab's jetzt nicht getestet ... geht es denn, bevor das Menü gewählt wurde


----------



## Krypthor (2. Dez 2011)

Wie bevor das Menü gewählt wurde?
Also vorher gings dann hab ich das Menü hinzugefügt und wenn ich jetzt das Programm starte öffnet sich das Menü und wenn man auf das Bild Start klickt wird es unsichtbar und das Interface öffnet sich
aber man kann die Spieler nicht bewegen.


----------



## Marco13 (2. Dez 2011)

Ja, ist halt nicht so systematisch, vermutlich müßte man es sich mal zusammenkopieren und testen, aber du kannst mal schauen, ob statt des
labelstart.setVisible(false);
ein 
frame.remove(labelstart);
nicht angebrachter wäre (besser wäre ein CardLayout, oder irgendein anderes _Konzept_ (man erreicht erstaunlich viel, indem man einfach Quellcode hinschreibt, aber irgendwann nicht mehr))

EDIT: Wobei das auf den KeyListener nicht viel Einfluß haben sollte.... ich werd's bei Gelegenheit mal testen, wenn es sich nicht von selbst erledigt...


----------



## Krypthor (2. Dez 2011)

das mit dem remove hab ich schon probiert aber wenn man dann auf das Bild klickt passiert garnichts.


----------



## Marco13 (2. Dez 2011)

Ja, sowas wie

```
@Override
            public void mouseClicked(MouseEvent e) {
                frame1.remove(labelstart);
                Interface i = new Interface();
                frame1.add(i);
                i.requestFocus();
                frame1.validate();
            }
```
einzufügen löst zwar vermeintlich das Problem aber... ich hab' das Gefühl, dass du mit diesem "Code hinschreiben der Sachen macht"-Ansatz nicht sooo weit kommen wirst...


----------



## Krypthor (2. Dez 2011)

ahh danke dir es klappt!


> ich hab' das Gefühl, dass du mit diesem "Code hinschreiben der Sachen macht"-Ansatz nicht sooo weit kommen wirst...


Das ist mir klar ich hab auch erst vor 2 Wochen oder so angefangen mit der Programmierung ;D


----------



## Marco13 (2. Dez 2011)

Ja, um so ein erstes Gefühl zu bekommen, mag das ja OK sein. Aber setz' dir nicht das Ziel, _genau dieses_ Spiel "perfekt bis zum Ende" durchzuprogrammieren....


----------



## Krypthor (2. Dez 2011)

Jo, ich mach immer soweit ich komme und fang dann ein neues Projekt an und später bekomm ich die alten Sachen dann automatisch hin.


----------



## Marco13 (3. Dez 2011)

Und sich Monate oder gar Jahre später alten Code durchzulesen kann sehr erheiternd sein


----------



## Krypthor (3. Dez 2011)

Noch ne kleine Frage, lohnt sich jetzt nicht extra deswegen ein Thread aufzumachen^^

Wenn ich eine Taste drücke soll der Gegenspieler bei Kontakt K.O. gehen. Das klappt auch ganz gut,
nur man könnte diese Taste durchgehend drücken und dadurch wäre sie wiederum sinnlos.
Wie kann ich die für 1 oder 2 sek. "deaktivieren"?


----------



## Marco13 (3. Dez 2011)

Hm... da gibts mehrere mehr oder weniger ausgefeilte möglichkeiten. Eine sehr einfache wäre, sich beim Tastendruck eine Zeit zu speichern und beim nächsten zu überprüfen

```
private long lastPressMS = -1;


.... 
void keyPressed(...)
{
    long current = System.currentTimeMillis();
    if (lastPressMS == -1  || current > lastPressMS + 3000) {
        lastPressMS = current;
        killOpponent();
   }
}
```


----------



## Krypthor (4. Dez 2011)

Das klappt zwar wenn man die Taste danach loslässt aber wenn man sie gedrückt hält bekommt der Gegner ohne Pause Leben gezogen. Das liegt daran das ich den boolean der für den Schaden verantwortlich ist erst wieder auf false setzte, wenn die Tast losgelassen wird.
Wie kann man vielleicht wenn man die Taste drückt den boolean auf true setzen, und nach ungefähr 1s automatisch wieder auf false setzen?


----------



## Marco13 (5. Dez 2011)

Das kommt davon wenn man den zusammenhang nicht richtig beschreibt. Was machst du wo wann mit welchen booleans in keyPressed, keyTyped und keyReleased? 
Man könnte einen Timer starten, aber das klingt krampfig, und als wollte man ein Problem lösen, das man selbst verursacht hat...


----------

