# Space Invaders



## annabanana (29. Dez 2013)

Hallo,
ich habe leider ein Problem mit meinem Spiel Space Invaders. Irgendwie werden manchmal mehrere Aliens auf einmal abgeschossen. Das liegt da dran, dass die Aliens horizontal sich hin und her bewegen. Leider weiß ich nicht, wie man das Problem lösen kann. Hier ist mein Code, ich hoffe ihr könnt mir helfen. Danke im Voraus. 


```
package pp2013.gruppe12.server.engine;

import pp2013.gruppe12.shared.Map;
import pp2013.gruppe12.shared.Player;
import pp2013.gruppe12.shared.Tile;

public class Shoot extends Thread {
	private Map map;
	private ServerEngine serverEngine;
	private boolean shot;
	private Player player;
	
	/**
	 * This method is used by ServerEngine for when a shot has been fired
	 * @param shot if a shot has been fired
	 */
	public void setBoolean(Boolean shot){
		this.shot= shot;
	}
	
	/**
	 * This is the constructor so set the map and serverEngine
	 * @param map the map be to set
	 * @param serverEngine the serverEngine to be set
	 * @param player the player to be set
	 */
	public Shoot(Map map, ServerEngine serverEngine, Player player){
		this.map=map;
		this.serverEngine=serverEngine;
		this.player=player;
	}
	
	
	/**
	 * This method constantly checks if a shot has been fired
	 */
	public void run(){
		while(true){
			try{
				sleep(10);
			}catch (InterruptedException e) {	
				e.printStackTrace();
			}
			if(shot==true){
				shot=false;
				shoot();
			}
		}
	}
	
	/**
	 * This method reacts after a shot has been fired
	 */
	public void shoot(){
		if(map.getTilePosition(Tile.MYPOSITION)!=null){
			
		
		int xMyPosition=(int) map.getTilePosition(Tile.MYPOSITION).getX();//change
		if(map.getTile(xMyPosition, 6).getTileID()!=Tile.HOUSE){
			synchronized(map){
			for(int y=6; y>=0; y--){
				int currentTileID=map.getTile(xMyPosition, y).getTileID();
				if(currentTileID==Tile.EMPTY){
					map.setTile(xMyPosition,y, Tile.SHOT);//set new tile as shot
					if(map.getTile(xMyPosition, y+1).getTileID()==Tile.SHOT){//clean the last tile
						map.setTile(xMyPosition, y+1, Tile.EMPTY);
					}
					serverEngine.sendToClient(map);
				}else if(map.getTile(xMyPosition, y).getTileID()==Tile.INVADERS||map.getTile(xMyPosition, y).getTileID()==Tile.BOMB){
				//TODO bomb wird noch als punkt gezaehlt
					map.setTile(xMyPosition, y, Tile.EXPLOSION);
					map.setTile(xMyPosition, y+1, Tile.EMPTY);
					
					serverEngine.sendToClient(map);
				}try {
					sleep(200);//so one can see the shot moving
				} catch(InterruptedException e) {e.printStackTrace();}	
				if(map.getTile(xMyPosition, y).getTileID()==Tile.EXPLOSION){
					
					map.setTile(xMyPosition, y, Tile.EMPTY);
				
					player.minusInvadersLeft();
					player.addPoint();
					if(player.getInvadersLeft()==0){
						player.increaseLevel();
						player.setInvadersLeft(40);
						map.setInvaders();
					}	
					serverEngine.sendToClient(player);
					serverEngine.sendToClient(map);
					break;// so the  bullet stops moving and shooting invaders
				}
				if(y==0){ //if the bullet is at the top and has not hit anything, then the bullet disappears
					map.setTile(xMyPosition, y, Tile.EMPTY);
					serverEngine.sendToClient(map);	
				}
			}
			}
		}
	}
	}
}
```


```
package pp2013.gruppe12.server.engine;

import pp2013.gruppe12.shared.Map;
import pp2013.gruppe12.shared.Tile;

public class MoveInvaders extends Thread {

	private Map map;
	private ServerEngine serverEngine;
	
	public MoveInvaders(Map map, ServerEngine serverEngine){
		this.map=map;
		this.serverEngine=serverEngine;
	}
	
	public void run(){
		while(true){
			try{
				sleep(10);
			}catch (InterruptedException e) {	
				e.printStackTrace();
			}
			moveInvaders();
	
		}
	}
	
	public void moveInvaders(){
		int max=0;
		int min=13;
		for(int i=0; i<Map.WIDTH;i++){
			for (int j=0; j<Map.HEIGHT; j++){
				if(map.getTile(i, j).getTileID()==Tile.INVADERS){
					if(i<min){
						min=i;
					}
					if(i>max){
						max=i;
					}
				}
				
			}
		}
		int rightSpace=13-max;
		int leftSpace=min;
		if(leftSpace>=rightSpace){
			while(leftSpace>0){
			for(int i=0; i<Map.WIDTH;i++){
				for (int j=0; j<Map.HEIGHT; j++){
					if(map.getTile(i, j).getTileID()==Tile.INVADERS){
						map.setTile(i-1,j,Tile.INVADERS);
						map.setTile(i,j,Tile.EMPTY);
						//serverEngine.setMap(map);
						serverEngine.sendToClient(map);
					}
					
				}
			}try {
				sleep(1500);//so one can see the invaders moving
			} catch(InterruptedException e) {e.printStackTrace();}	
			leftSpace--;
			}
		}else if(leftSpace<rightSpace){
			
			while(rightSpace>0){
			for(int i=Map.WIDTH-1; i>=0;i--){
				for (int j=Map.HEIGHT-1; j>=0; j--){

					if(map.getTile(i, j).getTileID()==Tile.INVADERS){
						map.setTile(i+1,j,Tile.INVADERS);
						map.setTile(i,j,Tile.EMPTY);
						//serverEngine.setMap(map);
						serverEngine.sendToClient(map);
					}
				}
			}try {
				sleep(1500);//so one can see the invaders moving
			} catch(InterruptedException e) {e.printStackTrace();}	
			rightSpace--;
			}
		}
	}
}
```


----------



## eMmiE (31. Dez 2013)

Könntest du das mit den Tiles nochmal genauer erläutern?
Ich kann mir darunter nicht wirklich was vorstellen.

Sind das Fragmente des Bildschirms?


----------



## annabanana (31. Dez 2013)

Ja genau! ich habe ein 14x8 Tiles, die zusammen die Spielfläche ergeben. 


```
package pp2013.gruppe12.shared;

import java.awt.Point;
import java.io.Serializable;

public class Map implements Serializable{
	
	private Tile [][] map;
	public static final int HEIGHT=8;
	public static final int WIDTH=14;
	public static final int HOUSE1=2;//x coordinate of the first house
	public static final int HOUSE2=5;
	public static final int HOUSE3=8;
	public static final int HOUSE4=11;
	
	/**
	 * This method creates a new Tile Map and
	 * sets the tildID first am EMPTY and then the houses in the right places
	 */
	public Map(){
		map = new Tile [HEIGHT][WIDTH];
		for(int i=0; i<WIDTH; i++){
			for (int j=0; j<HEIGHT; j++){
				Tile tile= new Tile(Tile.EMPTY);
				tile.setCoordinates(i, j);
				setTile(i,j,tile);
			}
		}
		setHouses();
		setMyPosition();
		setInvaders();	
	}
	
	public Tile [][] getMap(){
		return map;
	}
	
	public Tile getTile(int x, int y){
		return map [y][x];
	}

	public void setTile(int x, int y, Tile tile){
		map[y][x]=tile;
	}

	public void setTile(int x, int y, int tileID){
		getTile(x,y).setTileID(tileID);
	}
	
	
	/**
	 * This method sets the Houses in the right positions.
	 */
	public void setHouses(){
		
		setTile(2,6,Tile.HOUSE);
		setTile(5,6,Tile.HOUSE);
		setTile(8,6,Tile.HOUSE);
		setTile(11,6,Tile.HOUSE);
		
	}

	public Point getTilePosition(int tileID){
		for(int i=0; i<WIDTH; i++){
			for (int j=0; j<HEIGHT; j++){
				if(getTile(i,j).getTileID()==tileID){
					return new Point(i,j);
				}
			}
		}
		return null;	
	}

	public void setMyPosition(){
		setTile(0,7,2);	
	}

	public void setInvaders(){
		for(int i=2; i<12;i++){
			for(int j=1; j<5; j++){
				setTile(i,j,Tile.INVADERS);
			}
		}
	}

}
```


```
package pp2013.gruppe12.shared;

import java.awt.Point;
import java.io.Serializable;

public class Tile implements Serializable{
	
	private int x;
	private int y;
	private int tileID;
	public static final int EMPTY = 0;
	public static final int HOUSE = 1;
	public static final int MYPOSITION=2;
	public static final int INVADERS = 3;
	public static final int SHOT=4;
	public static final int EXPLOSION=5;
	public static final int BOMB=6;
	public static final int GAMEOVER=7;

	public Tile(int tileID){
		this.tileID=tileID;
	}
	
	public void setCoordinates(int x, int y){
		this.x=x;
		this.y=y;
	}
	
	public Point getCoordinates(){
		return new Point(x,y);
	}
	
	public int getTileID(){
		return tileID;
	}
	
	public void setTileID(int tileID){
		this.tileID=tileID;
	}
}
```


----------



## eMmiE (31. Dez 2013)

Also quasi 14*8 = 112 Möglichkeiten, ein Bildchen anzuzeigen?

Liegt das mit dem vielfachen Treffen (wahrscheinlich dann 2) daran, dass du in der Klass Shoot in Zeile 71 & 72 dem Programm sagst, dass das getroffene Feld auf Explosion und das darüber/darunter (=> y+1) liegende Feld auf Empty setzst?

Will heißen: Da müsste eigentlich noch ein Invader sein, wird aber weder angezeigt, noch in die Schadensberechnung aufgenommen, weil du ihn einfach verschwinden lässt.

1 Problem, 3 Lösungen:
1. Du kannt seinfach sagen, dass nur das eine Feld eine Explosion anzeigen soll, das andere nicht belangt wird

2. Du guckst bei einem Treffer nach dem nächsten Feld -> wenn auf dem nächsten Feld (y-Richtung) ein Alien ist, dann setzt du dieses Feld auf leer und das vorherige auf Explosion

3. Du gehst eine Ebene höher und verwaltest dein Spielfeld in Variablen und berechnest uch Schaden und Sonstiges danach. Deine Map wird somit zur puren Anzeigequelle. So kannst du auch leichter Dasten hin- und herschicken

Gruß eMmiE

P.S.: Guten Rutsch


----------



## annabanana (31. Dez 2013)

Leider glaube ich nicht, dass das Problem daran liegt. Es sind immer 3 Aliens die getroffen werden. Quasi einmal noch nach oben und einmal nach rechts. Es wird beim Schießen, ob ein Alien getroffen wird oder einfach ein leeres Feld, immer das letzte Feld (y+1) gelöscht. Ich vermute dass es daran liegt, dass diese Aliens auch noch zusätzlich sich nach rechts und links bewegen. Denn wenn ich diese Bewegungen ausschalte, dann schieße ich wirklich nur ein Aliens per Schuss ab.


----------



## eMmiE (1. Jan 2014)

Am Einfachsten kommst du jetzt wahrscheinlich weiter, wenn du erstmal dein Spiel für einen Invader testest (-> Wann wird der getroffen, Ausgabe: welche Felder werden von welchem Programmteil angesprochen)


----------



## annabanana (2. Jan 2014)

Bei einem Alien funktioniert das Schießen einwandfrei. Irgendwie schaffe ich es aber nicht, sobald der sich bewegt, alles zu synchronisieren.


----------



## eMmiE (3. Jan 2014)

Lass dir von jedem Programmteil (nur ein oder zwei mal) zurückgeben, wie er welches Feld in welcher Weise verändert hat


----------



## annabanana (5. Jan 2014)

Ich habe jetzt endlich das Problem verstanden. Die Aliens haben sich zu schnell bewegt und somit entstand das Problem. Nämlich wenn ein Alien angeschossen wurde, haben alle Aliens sich trotzdem weiter bewegt. D.h. zum Bsp. Tile (5,6) (5 ist die x Koordinate und 6 die y Koordinate) mit einem Alien(nennen wir mal Aliens a) wurde abgeschossen (und direkt danach ergibt sich auf diesem Feld eine Explosion) und parallel haben sich alle Aliens ein Feld nach links bewegt, aber das Alien (Nennen wir mal Aliens b) rechts von dem abgeschossenen Alien (a), welches ursprünglich auf (6,6) war, ist jetzt auf (5,6) gewechselt. Aber da dieses Feld mit Explosion belegt ist, verschwindet Alien b automatisch, da ich es so eingestellt habe, dass nach der Explosion dieses Feld (5,6) wieder auf leer gesetzt wird, obwohl es(b) gar nicht abgeschossen wurde. 

Vielen Dank eMmiE!


----------



## ursteiner (7. Jan 2014)

Hi, das Problem kommt mir bekannt vor. Habe auch mal ein Space Invaders gebastelt: Space Invaders

Wenn man einfach eine Liste der Invader hat, kann man den Invader einfach aus der Liste entfernen wenn er zerstört wurde.
Wichtig ist auch die Kollisionsbehandlung bei einem Treffer zu beenden, dadurch verhinderst du, dass weitere Invader vom gleichen Projektil getroffen werden.

Kann man deine Version irgendwo testen?


----------



## eMmiE (7. Jan 2014)

Ich denke der TO hat das direkt in der Zeichenebene gelöst, sodass eben Probleme entstanden. Du hast dann wahrscheinlich Variablen im Hintergrund laufen lassen, nach welchen du dann geabreitet hast. 
Ich halte Letzteres auch für zukünftige Entwicklungen für von Vorteil, weil man eine größere Kontrolle hat. Das Andere kann man sich alerdings wesentlich einfacher vorstellen


----------



## ursteiner (7. Jan 2014)

Anstatt ein Spielfeld als Array zu definieren und zu sagen in Zelle 4,5 ist ein Invader, beinhaltet die Invader Klasse die Information der x und y Position. Alle Invader sind dann in einer Liste List<Invader>.

Es hat natürlich beides seine Vorzüge.
In der Liste weiß man direkt wie viele Invader noch vorhanden sind über die größe der Liste.
Will man hingegen wissen ob an einer bestimmten Stelle ein Invader steht, kann man im Array direkt in der betroffenen Zelle nachsehen und muss nicht durch die Liste iterieren bis man evtl. einen Invader an dieser  Stelle findet.

Das sind aber ja Kleinigkeiten. Entscheidend ist der Erfolg wenn es dann so läuft wie man es erwartet. :applaus:


----------

