# Asteroids Clone: Kollision zwischen Schuss und Feind



## Marco84 (19. Sep 2006)

Hallo zusammen,
ich hab ein Problem. (ach nee, gell   )

Und zwar will bei meinem Spielchen die Kollisionsabfrage zwischen Feind und Schuss nicht so recht klappen. Dazu berechne ich den Abstand zwischen Feind und Schuss und wenn der klein genug ist solls bumm machen. 
Manchmal funktioniert das auch und zuerst dachte ich, mein Problem liegt daran, dass ich ja sowohl den neuen Punkt des Feinds als auch den Punkt des Schuss berechnen und deshalb der Abstand fast nie klein genug wird, da sich die beiden Partikel ja gegeneinander bewegen. Deshalb wird sowohl wenn jeder Feind sich bewegt als auch wenn jeder Schuss sich bewegt die Kollision überprüft (trefferPruefen(). Hat aber das Problem nicht komplett gelöst.
Nun hab ich zufällig bemerkt, dass keinesfalls eine Kollision zustande kommt wenn gerade ein neuer Schuss ausgelöst wird. Ich denk mal es liegt daran, dass ich ja in dem Moment in dem ein Schuss erzeugt wird dem Schuss die x- und y-Koordinate des Schiffs zuweise und er diese Punkte zur Berechnung verwendet. Ich hab nun den Treffer vor jedem Schuss-Erzeugen testen lassen, aber das hat nichts gebracht. Auch das Zwischenspeichern der alten Schusskoordinaten in Variablen und dass ich diese dann zur Abstandsberechnung verwendet habe hat leider nichts gebracht.

Inzwischen bin ich allmählich komplett betriebsblind und weiß allmählich nicht mehr weiter. Vieleicht kann mir ja einer von euch helfen oder nen Tip geben.

Im Anhang findet ihr das Applet, arbeiten tu ich mit Eclipse. Das Raumschiff wird mit HOCH/RUNTER-Pfeil hoch und runter gesteuert, gefeuert wird mit der Leertaste, Boni werden mit B abgefeuert.

Anhang hier downloaden


----------



## Marco84 (19. Sep 2006)

Uups, die URL sollte natürlich http://www.marco-huber.de/uni/oop heißen. dort könnt ihr mein Applet downloaden.

Marco84


----------



## LoN_Nemesis (20. Sep 2006)

Ich habe die Methode mal geändert, so funktionieren alle Kollisionen. Du darfst sie jetzt allerdings nur noch einmal aufrufen, nachdem alle Bewegungen gemacht wurden, also z.B. nach

```
// Bonus abfeuern
if (bonusGefangen == true && bonusFired == true) {
...
}

trefferPruefen();
```

Hier die geänderte Methode:



```
public void trefferPruefen() {
		for (int i = 0; i<feinde.size(); i++)
			for (int j = 0; j<schuesse.size(); j++) {
				Partikel shot = (Partikel)schuesse.get(j);
				Partikel enemy = (Partikel)feinde.get(i);
				xAbstandschuss = enemy.px - shot.px;
				yAbstandschuss = enemy.py - shot.py;
				abstandschuss = Math.sqrt((xAbstandschuss * xAbstandschuss)
						+ (yAbstandschuss * yAbstandschuss));
				System.out.println("SchussAbstand: " + abstandschuss);
				if (abstandschuss < 15) {
					treffer++;
					kleineExplosion.play();
					explosion(enemy);
					enemy.px = 700;
					shot.px = -100;
					if (treffer == (bonusZaehler * 5)) {
						bonusTest = true;
					}
				}
			}
	}
```


Zudem habe ich noch die explosion() Methode leicht abgeändert, da manchmal Explosionspartikel an falscher Stelle dargestellt wurden (evtl. durch meine Änderung). Du übergibst der Methode also jetzt ein Partikel, welches sagt wo die Explosion stattfinden soll. Vorher musste man sich darauf verlassen, dass feind der letzte abgeschossene Feind war.


```
public void explosion(Partikel p) {
		for (int j = 0; j < 5; j++) {
			System.out.println("run(): Explosion");
			explosionspartikel = new Partikel();
			explosionspartikel.px = p.px;
			explosionspartikel.py = p.py;
			explosionspartikel.vx = zufall(-30, 30);
			explosionspartikel.vy = zufall(-30, 30);
			explosionspartikel.ax = 0;
			explosionspartikel.ay = (float) 9.81;
			explodiert.add(explosionspartikel);
		}
	}
```

In public void kollisionPruefen() musst du dann auch den Aufruf von explosion(); zu explosion(feind); ändern.

Ein letzter Hinweis noch:


```
if (((Partikel) feinde.get(feinde.size() - 1)).px > 400)
```

Die Zeile solltest du abändern, denn wenn keine Feinde da sind, wirft das eine Nullpointer Exception. Jetzt wo alle Schüsse treffen schafft man das locker mit dauerschiessen 




P.S.: Das Spiel macht irgendwie Spass, obwohl es so einfach ist. Ich mag die Bonusschüsse 

P.S.2: Ich habe noch nie soviele Debuggausgaben gesehen, denk daran die alle zu löschen bevor du das Applet hochlädest, damit steigerst du die Performance sicher um 2000%


----------



## Marco84 (20. Sep 2006)

Vielen Dank für die schnelle Antwort. Jetzt funktionierts wirklich ausgezeichnet!
Kannst du mir vieleicht noch erklären, was an meiner Version falsch war damit ichs beim nächsten mal selber weis?


----------



## LoN_Nemesis (20. Sep 2006)

Dein Fehler war die zu intensive Benutzung von globalen Variablen. Du hast jedesmal wenn du die "schuesse" Liste durchlaufen bist immer die globale Variable "schuss" für das aktuelle Element benutzt. Und dann wenn du die trefferPrüfen() Methode aufrufst, übergibst du "schuss" nicht, sondern verlässt dich beim Aufruf, dass "schuss" auf das richtige Objekt zeigt. Das geht eben dann schief, wenn du einen neuen Schuss erstellst, weil dann "schuss" auf einen anderen Partikel zeigte oder wenn "feind" plötzlich auf etwas anderes zeigt.

Zudem hast du die Methode jedesmal aufgerufen wenn du einen Schuss bewegt hast und jedesmal wenn du einen Feind bewegt hast. Ich habe die Abfrage so umgebaut, dass jeder Schuss mit jedem Feind gecheckt wird, deswegen musst du die Methode nur noch einmal aufrufen wenn alle Bewegungen gemacht wurden. So wie du es gemacht hast, wäre es auch gegangen, aber nur wenn "feind" und "schuss" jeweils auf das richtige Element zeigen, und das konnte man eben nicht sicherstellen. Wenn du am Ende einfach "jeden mit jedem" überprüfst, kannst du sicher sein, dass eine Kollision auch erkannt wird.

Allgemein könntest du den OOP Gedanken von Java mehr nutzen, du hast nur eine 10 Zeilen Miniklasse und der Rest vom Code ist in der Appletklasse. Falls du das Spiel noch ausbauen willst, kann es sehr schnell unübersichtlich werden. Zum Glück ist der Code vorbildlich kommentiert, weshalb man sich relativ schnell zurechtfindet.


----------



## Marco84 (28. Sep 2006)

So, bin nun nach längerer Zeit mal wieder dazu gekommen, ein bisschen weiter zu Programmieren.
Ich hab jetzt mal versucht, ein bisschen mehr OOP in die Sache zu bringen und für jede Partikelart ne eigene Klasse angelegt. Die paint-Methoden hab ich auch schon ausgelagert. Allerdings musste ich feststellen, dass der Code in der Applet-Klasse deshalb nicht wirklich übersichtlicher oder kürzer wird. 
Was für Methoden sollte ich am besten noch auslagern? Oder geh ich völlig falsch an die Sache ran?
Source gibts wie immer unter http://www.marco-huber.de/uni/oop/source/ die neuste Version zum ausprobieren gibts ein Verzeichnis drüber...


----------



## LoN_Nemesis (28. Sep 2006)

Nein du gehst richtig an die Sache ran, aber du musst das konsequenter machen 

Zum Beispiel könntest du eine Klasse SpielLogik erstellen, die alle deine Listen von Schüssen, Feinden und Boni verwaltet. Dann musst du in deiner Applet Klasse in der Hauptschleife quasi nur noch ein paar Methoden aufrufen, etwa in der Art


```
while(true) {
     SpielLogik.bewegeFeinde();
     SpielLogik.bewegeSchüsse();
     SpielLogik.bewegeBoni();

     SpielLogik.checkForCollision();

}
```

Falls dein Spiel dann noch weiter wächst, kannst du die Klasse (falls es Sinn macht) vielleicht auch noch aufsplitten.


----------



## Marco84 (5. Okt 2006)

Vielen Dank für Deine Tips, hab mich mal drangesetzt und ne Logik-Klasse angelegt. Is eigentlich mehr ne Methodenklasse geworden. 

Nun hab ich aber mal wider ein neues Problem. Wollt ein Highscore-System einbaun dass die Higscores in ner txt-datei speichert die im selben Verzeichnis wie das Applet liegt.
Hier mal der code:

```
// gibts nen neuen Highscore?
	File datei = new File("highscore.txt");
	public void alterHighscore() throws IOException {
		FileReader fr = new FileReader(datei);
		BufferedReader br = new BufferedReader(fr);
		while (br.ready()) {
			altesLevel = Integer.parseInt(br.readLine());
			alteTreffer = Integer.parseInt(br.readLine());
		}
		br.close();
	}

	public void neuerHighscore() throws IOException {
		if (level == altesLevel) {
			if (treffer > alteTreffer) {
				newHighscore = true;
			}
		}
		if (level > altesLevel) {
			newHighscore = true;
		}
		if (newHighscore == true) {
			FileWriter fw = new FileWriter(datei);
			BufferedWriter bw = new BufferedWriter(fw);
			bw.write((new Integer(level)).toString());
			bw.newLine();
			bw.write((new Integer(treffer)).toString());
			bw.flush();
			bw.close();
		}

	}
```
Mein Problem ist der Zugriff auf die Datei! 
Mach ich das so wie oben im Code bekomm ich, wenn ich das Applet lokal ausführ keinen Fehler, wenn ich das Applet aber aufn Server hochlad und dann ausführ bekomm ich ne NullPointerException weil er die Datei nicht findet.
Ich hab mich schon ein paar mal umgeschaut und dabei eigentlich die richtige Methode gefunden, aber da schreibt mir Eclipse immer den Fehler an, dass der Konstructor File(URL) nicht definiert wäre, was ja laut API auch stimmt :

```
File datei = new File (new URL(getDocumentBase(), "highscore.txt"))
```
Nun, weiß ich nicht mehr weiter  aber Ihr könnt mir ja sicher helfen


----------



## Marco84 (7. Okt 2006)

Falls es wen interessiert, bzw für andere Programmieranfänger die vor dem gleichen Problem stehen: ich hab mein Problem gelöst! Hab das ganze jetzt mit Hilfe eines kleinen PHP-Skripts gelöst.

Hab im Internet (http://forum.developers-guide.net/showthread.php?t=3291) diese Klasse gefunden:

```
import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.net.URL;

import java.net.URLConnection;

/**
 * 
 * PhpPostConnect.java
 * 
 * This class can represant a connection to an PHP-Site
 * 
 * to send the side data via "POST" and get Data from
 * 
 * the side which will be written by side with "echo"
 * 
 * @author sparrow
 * 
 */

public class PhpPostConnect {

	/** Contains the URL to the PHP-Script */

	private URL sitepath;

	/** The Connection to the URL */

	private URLConnection con;

	/**
	 * 
	 * Empty construct, you must set the URL of the
	 * 
	 * target before you start to send and read data
	 * 
	 */

	public PhpPostConnect() {

	}

	/**
	 * 
	 * Construct which also define the targed URL
	 * 
	 * @param sitepath
	 *            The URL to the target PHP-Script
	 * 
	 */

	public PhpPostConnect(URL sitepath) {

		this.sitepath = sitepath;

	}

	/**
	 * 
	 * Set the URL to the target PHP-Script
	 * 
	 * @param sitepath
	 *            The URL to the target PHP-Script
	 * 
	 */

	public void setSitePath(URL sitepath) {

		this.sitepath = sitepath;

	}

	/**
	 * 
	 * To get the target-URL
	 * 
	 * @return The URL to the target PHP-Script
	 * 
	 */

	public URL getSitePath() {

		return this.sitepath;

	}

	/**
	 * 
	 * Sending data to the target-URL
	 * 
	 * @param data
	 *            The data which should be send
	 * 
	 * @throws IOException
	 * 
	 */

	public void send(String data) throws IOException {

		if (con == null) {

			con = sitepath.openConnection();

		}

		if (con.getDoOutput() == false) {

			con.setDoOutput(true);

		}

		OutputStream out = con.getOutputStream();

		out.write(data.getBytes());

		out.flush();

	}

	/**
	 * 
	 * Reading incoming data from the target-URL
	 * 
	 * @return The incoming data
	 * 
	 * @throws IOException
	 * 
	 */

	public String read() throws IOException {

		if (con == null) {

			con = sitepath.openConnection();

		}

		InputStream in = con.getInputStream();

		int c = 0;

		StringBuffer incoming = new StringBuffer();

		while (c >= 0) {

			c = in.read();

			incoming.append((char) c);

		}

		return incoming.toString();

	}

}
```
dazu dann noch diese Methoden:

```
int altesLevel;

	int alteTreffer;

	String alterString;

	String[] alterString2;

	boolean newHighscore = false;
	public void alterHighscore() throws IOException {
		try {
			URL url = new URL("http://www.XXX.de/XXX/highscorelesen.php");
			PhpPostConnect con2 = new PhpPostConnect(url);
			try {
				con2.send("los=gehts");
				alterString = con2.read();
				// System.out.println(alterString+"!");
			} catch (IOException e) {
				e.printStackTrace();
			}
		} catch (MalformedURLException e) {
			e.printStackTrace();
		}
		alterString2 = alterString.split("a\r\n");
		// System.out.println(alterString2[0]+": 0! "+alterString2[1]+": 1! ");
		altesLevel = Integer.parseInt(alterString2[0]);
		alteTreffer = Integer.parseInt(alterString2[1]);
	}

	public void neuerHighscore() throws IOException {
		if (level == altesLevel) {
			if (treffer > alteTreffer) {
				newHighscore = true;
			}
		}
		if (level > altesLevel) {
			newHighscore = true;
		}
		if (newHighscore == true) {
			// System.out.println("neuer Highscore: true");
			try {
				URL url = new URL("http://www.XXX.de/XXX/highscoreschreiben.php");
				PhpPostConnect con = new PhpPostConnect(url);
				// System.out.println("neuer Highscore: verbindung geöffnet");
				try {
					con.send("level=" + level + "&treffer=" + treffer);
					// System.out.println("neuer Highscore: level"+level+" und treffer"+treffer+" gesendet");
					con.read();
				} catch (IOException e) {
					e.printStackTrace();
				}
			} catch (MalformedURLException e) {
				e.printStackTrace();
			}
                        newHighscore = false;
		}

	}
```
dieses PHP-Skript:
highscorelesen.php

```
<?php
    if(strtolower($_SERVER['REQUEST_METHOD']) == 'post') {
		$dateilesen = fopen("/home/httpd/htdocs/marco-huber.de/uni/oop/highscore.txt", "r");
		$zeile = fgets($dateilesen,100);
		echo $zeile;
		$zeile2 = fgets($dateilesen,100);
		echo $zeile2;
        fclose($dateilesen);
    }
?>
```
highscoreschreiben.php

```
<?php
    if(strtolower($_SERVER['REQUEST_METHOD']) == 'post') {
    	$dateischreiben = fopen("/home/httpd/htdocs/marco-huber.de/uni/oop/highscore.txt", "r+");
        foreach($_POST as $key => $value) {
			$schreiben = $value."a\r\n";
			fwrite($dateischreiben, $schreiben);
        }
        fclose($dateischreiben);
    }
?>
```
und einer highscore.txt die so aussieht:

```
1a
0a
```
wenn jemand fragen dazu hat, kann er mich gerne fragen, die genaue erklärung wie und warum das ganze funktioniert erspar ich mir jetzt mal.

die funktionierende Version gibts wie immer auf www.marco-huber.de/uni/oop, da gibts auch nen link zum quelltext.[/code]


----------



## Illuvatar (7. Okt 2006)

Ich kriege grade einige NoClassDefFoundErrors.



> -- Opera Java Console --
> 
> Java vendor: Sun Microsystems Inc.
> Java version: 1.5.0_06
> ...



Das Spiel hängt sich bei dem ersten Treffer auf.


----------



## Marco84 (8. Okt 2006)

Hmm, also die erste, die NullPointerException bekomm ich auch, auch wenn ich noch keine Ursache oder gar Lösung dafür weiß.

Aber die NoClassDefFoundError konnt ich leider nicht reproduzieren. Hast Du vieleicht ne Idee woher das kommt?

Was ich allerdings sagen, ist dass der Fehler erst auftritt, seit ich das Highscore-System eingebaut hat. Und wenn ich die Highscore-Methoden nicht aufrufe, dann tritt auch der Fehler nicht mehr auf. Allerdings machen/ändern/beeinflussen die Highscore-Funktionen doch gar nichts an den anderen Funktionen, oder?


----------



## Illuvatar (8. Okt 2006)

Jetzt kommt der NoClassDefFoundError bei mir auch nicht mehr...


----------



## Marco84 (8. Okt 2006)

und bei mir kam grad gar kein fehler mehr.... ???? 

Schön wenn sich solche Probleme von allein lösen


----------



## Marco84 (9. Okt 2006)

So, hab den Fehler gefunde, hatte einfach ein repaint zu viel. Falls ihr noch Fehler findet, dann schreibt bitte!


----------

