# Programmstruktur Problem!



## stulleman (20. Jun 2011)

Hallo Leute,
ich stehe zur Zeit vor mehreren kleinen Problemen beim programmieren von einem TD-Spiel.
Wer es nicht kennt: Tower defense - Wikipedia, the free encyclopedia .

Ich habe eine TDFrame Klasse die JFrame extended, eine TDPanel Klasse die JPanel extended und Runnable implementiert und Aufgaben wie update und draw übernimmt.
Weiterhin habe ich eine Level Klasse, die mit einer Id erstellt wird und so das jeweilige Level läd.
Diese Level Klasse speichert die waypoints des Weges in einer ArrayList, die enemies wieder in einer ArrayList, und die towers wieder in einer ArrayList. Enemy und Tower sind wieder jeweils Klassen.

Um jetzt zu checken, ob ein Tower schießen muss (der einfachheit halber nur ein Laserstrahl) wollte ich die Distanz von Gegner und Tower berechnen und gucken ob beide Radien zusammen addiert kleiner oder größer als RadiusGegner + RadiusTower sind.

Mein Problem: Wo checke ich das ganze? In der Tower Klasse oder in der Enemy Klasse? Wenn ich es in der Tower Klasse mache (lasse mir also eine frische Version der enemie ArrayList übergeben) könnte ich zwar theortisch einen Laserstrahl zeichnen lassen, doch wie würde ich dem Enemy jetzt z.B. health abziehen?

Und nur nebenbei, mein Enemy ist eigentlich quadratisch, deswegen wird es durch den Radius etwas ungenau beim Collision detecting, gibt es eine andere Möglichkeit? Mit Rectangle und intersect würde es nicht klappen, da der Radius vom Tower aufjedefall rund sein muss!

Danke schonmal (;


----------



## Landei (20. Jun 2011)

Na der Turm berechnet, ob und auf wen er schießen muss, schießt und teilt dann im Falle eines Treffers dem getroffenen Feind mit, dass selbiger gerade einen Laserstrahl der und der Art abbekommen hat. Der Feind ändert seine Einstellungen selbsttätig entsprechend seiner Panzerung u.s.w. (was den Turm ja nicht zu interessieren hat).

Beim Radius-Test kannst du ja berechnen, ob eine der vier Ecken des Feindrechtecks nahe genug dran ist, das wird schon genauer.


----------



## stulleman (20. Jun 2011)

Ja, aber eben das ist ja mein Problem. Wie teile ich dem Enemy denn mit das er getroffen ist? Ich habe ja nur eine Kopie der ArrayList mit den Feinden drinn. Irgendeinen Denkfehler muss ich doch haben!


----------



## stulleman (20. Jun 2011)

Achsoo? Ich mache eine Methode in der Enemy Klasse um mit einem Laserstrahl getroffen zu werden, veränder so dann die Kopie der ArrayList und mache eine neue Methode in die Level Klasse um eine neue Version der ArrayList zu übergeben, richtig?


----------



## Fu3L (21. Jun 2011)

Wenn dein Programmaufbau nicht unwies komplex ist und mit vielen Threads arbeitet, würde ich empfehlen, keine Kopie der ArrayList zu erzeugen (ich habe sogar die Vermutung, dass du gar keine Kopie anlegst^^). So kannst du einfach die Referenz auf die ArrayList Enemies aus dem Hauptprogramm an den Turm weiterreichen und wenn der dann prüft, kann er auch gleich eine Methode des Enemies aufrufen:


```
for(Enemy e : enemies) {
 if(Collision) { //Implementierung wie oben von Landei beschrieben
    e.hitByLaser(laserStrength); //Vllt. gibts ja upgrades? Daher mit Parameter.
    break; //Nur einmal schießen
 }
}
```

Dafür muss der Aufruf in der Hauptlogik so aussehen:


```
for(Tower t : towers) {
  t.attackIfPossible(enemies); //Hier ohne .copy()!
}
```

So hat der Methodenaufruf in Tower Auswirkungen auch über Tower hinaus^^


----------



## Landei (21. Jun 2011)

Du kannst "flache" und "tiefe" Kopien machen:

```
////////// Flache Kopie ///////////

List<Enemy> original = ...
List<Enemy> copy = new ArrayList<Enemy>(original);

//Struktur
original +-+-+-+-+
         | | | | | 
         v v v v v 
Enemies  E E E E E 
         ^ ^ ^ ^ ^ 
         | | | | | 
copy     +-+-+-+-+


////////// Tiefe Kopie /////////

List<Enemy> original = ...
List<Enemy> copy = new ArrayList<Enemy>();
for(Enemy e : original) {
   copy.add((Enemy)(e.clone())); //auch von den Feinden wird eine Kopie gemacht
}

//Struktur
original +-+-+-+-+
         | | | | | 
         v v v v v 
Enemies  E E E E E 

copy     +-+-+-+-+
         | | | | | 
         v v v v v 
Enemies  E E E E E  (Kopien der originalen Enemies)
```

Bei einer "flachen" Kopie hast du also eine neue Liste, aber mit den gleichen Referenzen wie in der Original-Liste. Du kannst die *Struktur* der Original-Liste nicht über die Kopie-Liste ändern (also z.B. ein neues Element anfügen), aber die gemeinsam referenzierten Elemente lassen sich sehr wohl ändern.


----------



## stulleman (21. Jun 2011)

Ich mache das so:

Frame -> Panel (ist der einzige Thread, der das Level Objekt updatet) -> Level -> Towers,Enemies,Waypoints usw...

Das Level hat ja eine ArrayList mit enemies und towers, also update ich das Level so :

```
public void update() {
		for(int i = 0; i < enemies.size(); i++) {
			enemies.get(i).update();
		}
		
		for(int i = 0; i < towers.size(); i++) {
			towers.get(i).update(enemies);
		}
	}
```
Da der Tower und die enemies wieder eigene update methoden haben übergebe ich ihnen eine Kopie der ArrayList

```
towers.get(i).update(enemies);
```

oder? Ist das keine Kopie und ich muss mir das zurückschreiben sparen?
Danke nochmal und sorry falls das einer schon meinte, aber das hatte ich dann nicht verstanden (;


----------



## Fu3L (21. Jun 2011)

Wenn du nirgendwo bewusst .copy() oder .clone() aufrufst ist es keine Kopie, die du übergibst. Das ist einfach ein Verweis auf die Liste, die bei allen, denen du diesen Verweis übergibst *die selbe* ist.

Um das mal an einem Beispiel zu illustrieren (das Beispiel ist nicht für eine Liste mit Objekten geeignet, aber vllt kommt das Verständnis dadurch):
Du hast einen Fernseher, der jetzt dein Objekt darstellt (zB deine Liste mit den Werten drin). Du kannst den Fernseher nicht direkt bedienen, weil er weit weg steht (so wie das Objekt sich weit weg im Heap befindet). Aber du hast eine Fernbedienung, die für deine Referenzvariable (ArrayList<Enemy> enemies) steht. Wenn du jezz folgendes tust:


```
ArrayList<Enemy> enemies = new ArrayList<Enemy>();
ArrayList<Enemy> enemies2 = enemies;
//Oder
methodenAufruf(enemies);
```

Dann sind in der Methode, die aufgerufen wurde bzw. in enemies2 nur Kopien deiner Fernbedienung vorhanden, aber jeder schaut auf den gleichen Fernseher. Wird jetzt bei einer dieser Fernbedienungen der Sender gewechselt, ändert sich der Sender für alle, die zusehen, weil sie ja nur auf einen Fernseher gucken.

Erst wenn du eine Methode wie .copy() oder .clone() aufrufst, wird auch ein neuer, baugleicher Fernseher hingestellt. 
Und dann gibt es noch den von Landei beschriebenen Fall bei Sammlungen von Objekten, dass man einmal eben nur die SAmmlung kopiert, aber die Werte in beiden Sammlungen sind *die selben* und einmal, dass man die Sammlungen inklusive ihrer Objekte kopiert, sodass jede Liste ihre eigenen Objekte hat und nicht das Umschaltproblem aus der obigen Metapher auftreten kann


----------



## stulleman (21. Jun 2011)

Super danke habe es verstanden! Sehr komisch das mir das vorher noch nie aufgefallen ist (;


----------

