# Kollisionsabfrage implementieren



## kaoZ (19. Jul 2014)

Aloha, ich komme nun langsam aber sicher an den Punkt an welchem ich eine Kollisionsabfrage implementieren möchte,

Erstmal zum aktuellen stand, 

um mich näher mit dem Zeichnen unter Swing und Spiellogik / loops auseinanderzusetzen *habe ich das Projekt so einfach wie möglich gehalten*.

Hier noch ein kleiner Überblick, ich bin mir auch noch nicht ganz sicher ob der ansatz so vollkommen richtig ist.

zzt. besteht das Projekt aus folgenden  relevanten Klassen

Tile
TileMap
Field
Player
Hud

die Map welche dargestellt wird parse ich in der Klasse TilePmap aus einer solchen Datei



Spoiler: Map.map





```
20
20
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 1
0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0
0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0
0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 1 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0
0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
```




das sieht dann folgendermaßen aus:





_Wie man hier auch schön erkennen kann bewegt sich der Player noch unabhängig von der Map auf welcher er gezeichnet wird ( Ignoriert einfach den falschen Satzbau auf der Grafik , keine Ahnung was mich da geritten hat ^^ )_

Ich übergebe dem Player im Konstruktor die map auf welcher er sich bewegen soll, so habe ich die abfrage der Position zzt. implementiert.


```
public void update(){
		
		if (left) {
			nextX -= moveSpeed;
			if (nextX < 0) {
				nextX = 0;
			}
		}
		else if (right){
			nextX += moveSpeed;
			if (nextX > map.width - width) {
				nextX = map.width - width;
			}
		}
		
		if (jumping) {
			nextY = jumpStart;
			jumping = false;
			falling = true;
		}
		
		if (falling) {
			nextY += gravity;
			if (nextY > 0) {
				nextY = 0;
				falling = false;
			}
		}

		// check collision
		
		//checkCollision((int)nextX, (int)nextY);
		
		
	}
```

nun war mein Ansatz, mal abgesehen davon das ich mich noch darum kümmern muss das die Aktuelle Position auf der Map hochgezählt wird, das ich der hier auskommentierten Methode checkCollision() bei jedem Update die aktuelle Position des Players übergebe, und dort anhand der Bounds des Players, der hier ja zzt. nur ein Rechteck ist, und der einzelnen Tiles ( hierzu komme ich gleich noch) geprüft wird ob der Typ des Tiles entweder NORMAL oder BLOCKED ist und ob sich ggf. die kooridinaten überschneiden und dementsprechend dann die Position ggf. zurücksetzen.

Hier ist aber auch schon der jetzige Knackpunkt, ich habe die Klasse Tile zzt. folgendermaßen umgesetzt


```
public class Tile {
	
	final static int NORMAL = 0;
	final static int BLOCKED = 0;
	
	private int type;
	
	public Tile(int type) {
		this.type = type;
	}
	
	public int getType() 		{return this.type;}
	
}
```

und Frage mich jetzt ob es nicht dafür ggf. schlauer wäre hier ein BufferedImage zu halten, welche die einzelnen Tiles repräsentiert.

im Moment setze ich die TileMap so um das nur anhand des Typs der Tiles welche sich in einem 2Dim - Tile Array befinden unterschiedliche Farben für die Rechtecke gezeichnet werden, die Größe der einzelnen Tiles wird hier über die Frame bzw. Imagegröße errechnet auf dem dann beim rendern gezeichnet wird.

Hier nochmal die Klasse TileMap


```
import java.awt.Color;
import java.awt.Graphics;
import java.io.BufferedReader;
import java.io.InputStreamReader;

public class TileMap {
	
	Tile[][] tileMap;
	
	int xPosition;
	int yPosition;
	
	int width;
	int height;

	int numRows;
	int numCols;
	int tilesAcross;
	
	int tileSize;

	public TileMap() {}
	
	public void loadMap(String path){
		
		InputStreamReader in;
		BufferedReader reader;
		
		try {
			in = new InputStreamReader(getClass().getResourceAsStream(path));
			reader = new BufferedReader(in);
			
			numRows = Integer.parseInt(reader.readLine());
			numCols = Integer.parseInt(reader.readLine());

			tileSize = Field.WIDTH / numRows;
			tilesAcross = numRows;
			
			width	 = numRows * tileSize;
			height	 = numCols * tileSize;
			
			tileMap = new Tile[numRows][numCols];
	
			for (int row = 0; row < numRows; row++) {	
				String line = reader.readLine();
				String delimiter = "\\s+";
				for (int col = 0; col < numCols; col++) {
					String[] tokens = line.split(delimiter);
					tileMap[row][col] = new Tile(Integer.parseInt(tokens[col]));
				}
			}
			
		} catch (Exception ex) {
			ex.printStackTrace();
		}
		
	}
	
	public void update()	{}
	
	public void draw(Graphics g){
		
		for (int row = 0; row < numRows; row++) {
			for (int col = 0; col < numCols; col++) {
			
				if (tileMap[row][col].getType() == Tile.NORMAL) {
					g.setColor(Color.WHITE);
				}
				else{
					g.setColor(Color.BLACK);
				}
				g.fillRect(col * tileSize, row * tileSize, tileSize, tileSize);
			}
		}	
	}
	
	public int getX()			{return this.xPosition;}
	public int getY()			{return this.yPosition;}
}
```

Wäre super wenn mal jemand drüber schauen würde und mir sagt ob es so Sinn macht, oder ob es einen Sinnvolleren Weg gibt / der oben erwähnte Ansatz Sinn macht.

:rtfm:


----------



## Gucky (19. Jul 2014)

Ich kann mich nicht so ganz damit anfreunden, dass der Player nachguckt, ob er gegen eine Wand rennt.

Der Spieler fragt die Map, ob er irgendwo gegen gerannt ist und die Map antwortet ihm, ggf. mit einer Richtung oder mit 0 für keine Kollision.

Die Kollision kannst du bei rechtwinkligem Player und Map mit der API Klasse Polygon machen. Viele halten sie für ein Verbrechen an der Menschheit aber ich finde sie nicht so schlecht.


----------



## kaoZ (20. Jul 2014)

> Ich kann mich nicht so ganz damit anfreunden, dass der Player nachguckt, ob er gegen eine Wand rennt.



Du meinst ich sollte also nicht dem Player die Map übergeben, auf welcher er sich befindet, sondern eher umgekehrt , der Map den Player, und die Kollisionsabfrage dann dort realisieren ?

sprich anstelle von 


```
public Player(TileMap map){...}
```

lieber 


```
public TileMap(Player player){...}
```

Warum die Kollision nicht mit Rectangle ?

Wie genau müsste denn dann in diesem Fall die Implementierung aussehen ? mit Rectangle müsste man quasi um den Player ein Rectangle erzeugen anhand dessen Position dann die umliegenden Tiles (oben, unten, links, rechts, und alle Ecken) ( ggf. je nach typ ebenfals mit einem Rectangle versehen) auf Überschneidung der Koordinaten geprüft werden , und dann die Position des Player dementsprechend setzen.


----------



## Gucky (20. Jul 2014)

Bei rechteckigen Playern bestünde der Player aus vier Punkten, die getestet werden müssten. Sofern du die Möglichkeit mit dem Polygon nimmst.
Der Player bekommt dann noch eine Instanz von Map, damit er fragen kann.
Rectangle ginge auch nur damit hab ich noch nie gearbeitet.


----------



## Androbin (20. Jul 2014)

Leute, kommt mal wieder runter,
für die Kollision braucht man doch nicht mal ein Rectangle!
Man muss lediglich folgende Abfrage anpassen und implementieren:

```
public boolean collides( Direction dir ) {
		
		switch ( dir ) {
			
			case Links :
				return map.isBlockAt( ( player.x - 1 ) / Block.size, player.y / Block.size );
				
			case Rechts :
				return map.isBlockAt( ( player.x + player.width + 1 ) / Block.size, player.y / Block.size );
				
			case Hoch :
				return map.isBlockAt( player.x / Block.size, ( player.y - 1 ) / Block.size );
				
			case Runter :
				return map.isBlockAt( player.x  / Block.size, ( player.y + player.height + 1 ) / Block.size );
			
		}
		
	}
```
Ja, ich weiß, der Code ist nicht perfekt, aber es ist ja lediglich eine Richtungsweisung!


----------



## kaoZ (20. Jul 2014)

> Bei rechteckigen Playern bestünde der Player aus vier Punkten, die getestet werden müssten. Sofern du die Möglichkeit mit dem Polygon nimmst.



Mit Polygon habe ich bis jetzt noch nicht gearbeitet, zzt. besteht der Player ja aus einem Rechteck, welches einfach gezeichnet wird.

ich dachte da an eine art Hitbox (Rectange) die den Player umgibt.

Hier mal die Klasse Player :



Spoiler: Player.java





```
public class Player {
	
	double xPos;
	double yPos;
	
	double nextX;
	double nextY;
	
	int width;
	int height;
	
	double moveSpeed;
	double fallSpeed;
	double stopSpeed;
	double maxSpeed;
	double jumpStart;
	double gravity;
	
	
	boolean left;
	boolean right;
	boolean jumping;
	boolean falling;
	
	TileMap map;
	
	public Player(TileMap map) {
		
		width = 20;
		height = 20;
		
		moveSpeed = 2.6;
		maxSpeed = 4.2;
		fallSpeed = 40;
		jumpStart = -39;
		stopSpeed = 0.3;
		gravity	= 0.7;

		this.map = map;
	}
	
	private void left(boolean value)	{this.left = value;}
	private void right(boolean value)	{this.right = value;}
	
	private void jumping(boolean value){
		if (!falling) {
			jumping = true;
		}	
	}
	
	
	
	public void update(){
		
		if (left) {
			nextX -= moveSpeed;
			if (nextX < 0) {
				nextX = 0;
			}
		}
		else if (right){
			nextX += moveSpeed;
			if (nextX > map.width - width) {
				nextX = map.width - width;
			}
		}
		
		if (jumping) {
			nextY = jumpStart;
			jumping = false;
			falling = true;
		}
		
		if (falling) {
			nextY += gravity;
			if (nextY > 0) {
				nextY = 0;
				falling = false;
			}
		}

		// check collision
		
		//checkCollision((int)nextX, (int)nextY);
		
		
	}
	
	public void checkCollision(int x, int y){
		
	}
	
	public void setPosition(int xPos, int yPos){
		this.xPos = xPos;
		this.yPos = yPos;
	}
	
	public void draw(Graphics g){

		g.setColor(Color.RED);
		
		g.fillRect((int)nextX, (int)nextY, width, height);
	}

	public void keyPressed(int key){
		if (key == KeyEvent.VK_A) {
			left(true);
		}
		if (key == KeyEvent.VK_D) {
			right(true);
		}
		if (key == KeyEvent.VK_SPACE) {
			jumping(true);
		}
	}
	
	public void keyReleased(int key){
		if (key == KeyEvent.VK_A) {
			left(false);
		}
		if (key == KeyEvent.VK_D) {
			right(false);
		}
	}
	
	public int getXPos() 			{return (int)nextX;}
	public int getYPos()			{return (int)nextY;}
}
```




zzt. kann ich den Player damit im Rahmen der map steuern, anhand der noch ungenutzten xPos und yPos wollte ich die Position auf der map die ich im Konstruktor übergebe abfragen.

diese Position wollte ich dann der Methode checkCollision(int x, int y) übergeben, die die Position des Players mit der aktuellen Position auf der map vergleicht, und anhand der types der einzelnen Tiles an dieser Position feststellt ob eine Kollision stattfindet oder nicht .



> Leute, kommt mal wieder runter,
> für die Kollision braucht man doch nicht mal ein Rectangle!
> Man muss lediglich folgende Abfrage anpassen und implementieren:



Mag sein , aber da ich es wie gesagt möglichst einfach halten wollte arbeite ich hier in dem Projekt noch mit boolschen Ausdrücken anstselle von Direction / Block Objekten um unter anderem die Position des Players zu bestimme.

Wie gesagt kann sein das mein Ansatz da auch noch nicht ganz korrekt ist , ich habe bis dato eher funktionale Anwendungen geschrieben und Spiellogik ist quasi neuland


----------



## Androbin (20. Jul 2014)

kaoZ hat gesagt.:
			
		

> Mag sein , aber da ich es wie gesagt möglichst einfach halten wollte arbeite ich hier in dem Projekt noch mit boolschen Ausdrücken an Stelle von Direction / Block Objekten um unter anderem die Position des Players zu bestimme.



Wie bereits gesagt, sollte mein Code lediglich das Prinzip erläutern.
Also pass' es einfach deinen Interessen an, ok?

Aber an deiner Stelle würde ich mir wirklich Gedanken machen,
ob ich nicht vielleicht doch, ein Enum Direction, bzw. eine Klasse Block/Tile, etc. erstelle!

Zum Einstieg:

```
public enum Direction {
	
	Down( 0, 1 ), Left( -1, 0 ), Right( 1, 0 ), Up( 0, -1 );
	
	private int sX, sY;
	
	private Direction( int sX, int sY ) {
		
		this.sX = sX;
		this.sY = sY;
		
	}
	
	public int sX() { return sX; }
	public int sY() { return sY; }
	
}
```


----------



## Gucky (20. Jul 2014)

Die vier Punkte würden den Player repräsentieren. Malen kannst du ihn, wie du Lust hast.

Allerdings wird diese Möglichkeit falsch, sobald du Elemente auf der Map hast, die schmaler sind, als der Player. Denn dann wären die Punkte um die Wand herum, wären aber nicht in ihr und Map und Player wären zufrieden, du aber nicht. 
Da dies aber nicht der Fall zu sein scheint, ist diese Möglichkeit nicht schlecht.

Bei großen Maps müsstest du sehr viel abfragen, weshalb du dich dann mal mit einem speziellem System beschäftigen solltest, welches die Map in einen Baum einteilt. Die Wurzel ist alles und die Blätter sind vorher festgelegte Würfel, die einen Teil der Map beinhalten.


----------



## Androbin (20. Jul 2014)

Gucky hat gesagt.:
			
		

> Die vier Punkte würden den Player repräsentieren. Malen kannst du ihn, wie du Lust hast.
> 
> Allerdings wird diese Möglichkeit falsch, sobald du Elemente auf der Map hast, die schmaler sind, als der Player. Denn dann wären die Punkte um die Wand herum, wären aber nicht in ihr und Map und Player wären zufrieden, du aber nicht.
> Da dies aber nicht der Fall zu sein scheint, ist diese Möglichkeit nicht schlecht.
> ...



Na, dann müsste man doch lediglich dementsprechend viele Punkte an der Seite der Spielers machen,
ja nachdem, wie viele Blöcke in die Höhe/Breite des Spielers passen, klar?


----------



## Gucky (20. Jul 2014)

Ja schon aber dann kann man irgendwann auch ein Shape (z. B. Rectangle) nehmen und die Überschneidung überprüfen.

Das mit den vier Punkten macht IMHO nur Sinn, wenn es über vier oder maximal 8 Punkte nicht hinausgeht. (Wird auch irgendwann unübersichtlich)


----------



## Androbin (20. Jul 2014)

Gucky hat gesagt.:


> Ja schon aber dann kann man irgendwann auch ein Shape (z. B. Rectangle) nehmen und die Überschneidung überprüfen.
> 
> Das mit den vier Punkten macht IMHO nur Sinn, wenn es über vier oder maximal 8 Punkte nicht hinausgeht. (Wird auch irgendwann unübersichtlich)



1. Shapes?
Ja, aber wäre es nicht besser, man müsste nicht die Kollision mit allen Blöcken der Map machen?
==> Siehe mein Code-Beispiel unten!

2. Unübersichtlich?
Du sollst ja auch nicht von Hand alle Punkte prüfen, sondern mit Schleifen und Co. arbeiten!


----------



## turtle (20. Jul 2014)

Vielleicht gibt es hier ein paar Informationen?


----------



## kaoZ (20. Jul 2014)

schau ich mir gleich mal an , danke


----------



## Androbin (20. Jul 2014)

Hier gibt's ebenfalls ein paar gute Erklärungen zur Kollision:
YouTube.com - Let'S GameDev - Playlists - Kollsionserkennung


----------



## kaoZ (21. Jul 2014)

> Der Spieler fragt die Map, ob er irgendwo gegen gerannt ist und die Map antwortet ihm, ggf. mit einer Richtung oder mit 0 für keine Kollision.



Was ich augenscheinlich noch nicht so ganz checke, bzw. sag mir mal bitte ob ich da richtig liege ,

Der player hat folgende Eigenschaften (Koordinaten):

*x,y* | repräsentieren die Position des players auf der map

*dx,dy* | repräsentieren die Position des players an der er als nächstes erwartet wird

diese setzt sich folgendermaßen zusammen:

wenn des player nach rechts geht addiere ich  die laufgeschwindigkeit der erwarteten Position hinzu 
( dx += moveSpeed ), wenn der Spieler nach links geht genau umgekehrt ( dx -= moveSpeed) 

_muss ich dann nicht dx, und dy auch anfangs mit den akutellen x und y koordinaten initialisieren ? ansonsten wären diese ja 0,0_
_warum soll ich hier nicht gleich auch abfragen ob er sich noch innerhalb der map befindet ?_

gezeichnet werden soll der player an der aktuellen Position auf der map (also x,y), 
welche sich ja erst nach folgenden abfragen ergeben kann / ergibt, also muss ich bevor ich die x und y Position des Player setze folgendes machen :

 - die aktuelle Position auf der map bestimmen / abfragen
 - das Tile in welchem sich der player befindet abfragen
 - feststellen ob das Tile ein Kollisionsobjekt ist oder nicht ( wäre ja dann die Kollisionsabfrage )

die x und y Position des players , insofern keine Kollision mit Objekten auf der map stattfindet ebenfalls erhöhen, und auch abfragen ob sich der player noch innerhalb der Map befindet ( dx > map.widht ) wenn/falls die map nicht umgeben von Kollisionsobjekten ist.

Wo ich mir nun unsicher bin ist die implementierung:

ich wollte dem Player als Attribut ein Rectangle mitgeben (Hitbox), welches sich aus der Breite und höhe + 1 des players + 1 ergibt , und immer an der Position gezeichnet wird an welcher sich der player als nächstes befindet (dx,dy) und ihn somit umgibt,
(der player ist später ein Image, zzt. noch selber ein einfaches Rectangle).

Das gleiche hatte ich mit Kollisionsobjeten vor, diese sollte ebenfalls eine art Hitbox in Form eines Rectangles bekommen, da ja soweit ich das mitbekommen habe Rectangle die methode intersects() anbietet mit der eine Überschneidung an bestimmten Koordinaten festgestellt werden kann.

Ich wollte hierfür ein Interface erstellen welches alle Objekte implementieren die ich als Kollisionsobjekt behandeln möchte.

Die ist zzt. aber so nicht möglich da ich die map nur anhand der ausgelesenen werte verschiedenfarbig zeichne , ich müsste dann quasi erst wieder die einzelnen Tiles als Objekt Kapseln.

Hier die aktuelle draw() methode der Klasse TileMap:



Spoiler





```
public void draw(Graphics g){
		
		for (int row = 0; row < mapHeight; row++) {
			for (int col = 0; col < mapWidth; col++) {
				
				int rc = map[row][col];
				
				if (rc == 0) {
					g.setColor(Color.WHITE);
				}
				if (rc == 1) {
					g.setColor(Color.BLACK);
				}
				g.fillRect(x + col * tileSize, y + row * tileSize, tileSize, tileSize);
				
			}
		}
	}
```




Oder ist dieser Ansatz zu umständlich, bzw. hab ich irgendwo Denkfehler oder was übersehen ?


----------



## Androbin (21. Jul 2014)

kaoZ hat gesagt.:
			
		

> wenn des player nach rechts geht addiere ich die laufgeschwindigkeit der erwarteten Position hinzu
> ( dx += moveSpeed ), wenn der Spieler nach links geht genau umgekehrt ( dx *-=* moveSpeed)



Aufpassen, die moveSpeed musst du immer ADDIEREN, ist dir bewusst, wieso?



			
				kaoZ hat gesagt.:
			
		

> (der player ist später ein Image, zzt. noch selber ein einfaches Rectangle).



Du willst doch nicht allen Ernstes von BufferedImage erben, oder?



			
				kaoZ hat gesagt.:
			
		

> Ich wollte hierfür ein Interface erstellen welches alle Objekte implementieren die ich als Kollisionsobjekt behandeln möchte.



Wäre eine extra (abstrakte) Klasse nicht besser? Da kannst du gleich die nötigen Abfragen machen und müsstest nicht jedesmal das gleiche implementieren! Verstehst du, was ich meine?


----------



## kaoZ (21. Jul 2014)

> Aufpassen, die moveSpeed musst du immer ADDIEREN, ist dir bewusst, wieso?



Der movespeed repräsentiert hier ja die "Bewegungsgeschwindigkeit" in alle Richtungen, gehe ich nun nach rechts muss ich die 
	
	
	
	





```
erwartete position = aktuelle Position + die bewegungsgeschwindigkeit
```
 rechnen, gehe ich nun entgegengesetzt ist es doch genau umgekehrt, 
	
	
	
	





```
erwartete Position = aktuelle Position - bewegungsgeschwindigkeit
```
 man könnte nun noch hergehen und die bewegungsgeschwindigkeit bis zu einem maximum erhöhen sobald der player sich in eine richtung bewegt , und verringern wenn man keine taste mehr betätigt damit es flüssiger aussieht, ansonsten wüsste ich nicht warum ich es immer Addieren muss .



> Du willst doch nicht allen Ernstes von BufferedImage erben, oder?



Nein natürllich nicht, aber der Player bekommt als attribut ein BufferedImage, welches beim initialisieren geladen wird und dann anstelle des jetzigen rechtecks gezeichnet werden soll.
Ich hatte mich vielleicht falsch ausgedrückt, der player wird später durch ein BufferedImage repräsentiert, zzt. ist es einfach ein farbiges Rechteck.



> Wäre eine extra (abstrakte) Klasse nicht besser? Da kannst du gleich die nötigen Abfragen machen und müsstest nicht jedesmal das gleiche implementieren! Verstehst du, was ich meine?



Jop , eine Gute idee, so kann ich die Basisfunktionalität gleich festlegen , und brauch ggf. nurnoch die jeweilige größe des Objektes festlege, ähnlich wie in einer Engine die schon festlegt wie sich was verhält und ablaufen muss.

angenommen ich hab nun folgende implementierung in der Klasse player:


```
public void update(){
		
		
		//move left + right (rudimentär ohne max und stopSpeed)
		if (left) {
			dx -= moveSpeed; 
		}
		else if (right){
			dx += moveSpeed;
		}

		//jump + fall
		if (jumping) {
			dy = jumpStart;  
			jumping = false;
			falling = true;
		}
		
		if (falling) {
			dy += gravity;	
		}
		
		//check collision

		
		//set Postition
		x = dx;
		y = dy;
		
	}
```

mal abgesehen davon das hier nur die x und y position anhand der dx und dy position gesetzt wird da noch kollisionsabfrage und co fehlen , ist es so schon möglich den player zu bewegen. 
warum muss ich denn die bewegungsgeschwindigkeit nun immer Addieren, bzw wie meinst du das ? in welchem kontext.


----------



## Androbin (21. Jul 2014)

Ein Problem wirst du später mit Sicherheit haben werden, darum sage ich es dir lieber gleich:
Wenn du deine Bewegung und Kollision so zusammensetzt, wirst du später folgendes Problem haben:
Wenn die Geschwindigkeit größer ist, als die Größe eines Blockes, so kann man einfach hindurch glitchen.
Darum musst du die Kollisions-Abfragen ...
a) ... für jeden Pixeln machen, den du dich bewegst, oder später auch
b) ... dasselbe wie in a) machen, mit dem Unterschied, dass du bei den mittleren Blöcken nicht mehr alle Pixel prüfen musst
Ist dir das jetzt bewusst?
Falls es dich tröstet: Ich hatte dieses Problem auch mal!


----------



## kaoZ (21. Jul 2014)

Ich denke ich hab noch ein ganz anderes Problem, ich bewege zzt. den Player und lasse ihn dann an bestimmten x und y Koordinaten zeichnen, diese sind allerdings, logischerweise unabhängig von der map.......

Ich muss wahrscheinlich die Bewegung erst einmal so implementieren das ich mich auf der map bewege , und einfach den player an die Position zeichnen lasse an welcher ich mich auf der Map grade befinde, *jetzt erstmal mal unabhängig von der Kollision aus gesehen .*

Irgendwo hab ich da noch nen Denkfehler was die Steuerung angeht.

Sonst kann ich ja lange versuchen irgendwas abzufragen wenn sich die Position auf der map gar nicht ändert, sondern nur die Stelle an welcher der Player gezeichnet wird.

Ist meine Annahme soweit korrekt ?


----------



## Gucky (21. Jul 2014)

Die Map und der Player arbeiten beide mit denselben Koordinaten. Sonst funktioniert das nicht. Außer du translatest (
	
	
	
	





```
g.translate(int,int)
```
) aber das würde ich dir nicht raten.


----------



## kaoZ (21. Jul 2014)

> Die Map und der Player arbeiten beide mit denselben Koordinaten.



Bedeutet also ich muss entweder die player koordinaten auf die map internen übertragen oder umgekehrt?


und wenn ja höchstwahrscheinlich in der update() methode ?


----------



## Gucky (21. Jul 2014)

Wie zeichnest du denn die Map?
Bekommt ein Tile die Koordinaten 
	
	
	
	





```
x = BREITE_TILE * STELLE
```
 und 
	
	
	
	





```
y = HÖHE_TILE * STELLE
```
 oder bekommt ein Tile die Koordinaten 
	
	
	
	





```
x = 5
```
 und 
	
	
	
	





```
y = 5
```
 für das Tile T55?
Intern müsste aber trotzdem mit denselben Koordinaten gerechnet werden, da du es ja irgendwie an die richtige Stelle auf dem Frame bekommen haben musst. Wenn du also das Tile fragst, ob ein Punkt darin enthalten ist, dann wird es dir die Antwort für die richtigen Orte geben.


----------



## kaoZ (21. Jul 2014)

Also, zzt. zeichne ich die map unabhängig von einzelnen Tiles :


```
public void draw(Graphics g){
		
		for (int row = 0; row < mapHeight; row++) {
			for (int col = 0; col < mapWidth; col++) {
				
				int rc = map[row][col];
				
				if (rc == 0) {
					g.setColor(Color.WHITE);
				}
				if (rc == 1) {
					g.setColor(Color.BLACK);
				}
				g.fillRect(x + col * tileSize, y + row * tileSize, tileSize, tileSize);
				
			}
		}
	}
```

und ich vermute hier ist auch das Problem , x und y sind natürlich immer 0,

wenn ich also den Getter aufrufe ist x logischerweise auch immer 0.


```
g.fillRect(x + col * tileSize, y + row * tileSize, tileSize, tileSize);
```

hat also nicht wirklich einen Effekt solange x und y 0 sind.

Ich vermute stark hier werde ich auch nochmal neu implementieren müssen da das so ja nicht wirklich Sinn macht, die Frage ist nun,  ich zeichne die map ja unabhängig der koordinaten des players, also muss ich quasi , insofern die Bewegung auf der map stattfindet, die derzeitige x und y koordinate inkrementieren oder dekrementieren, und den player dann an die jeweiligen koordinaten zeichnen lassen ?!

Wo findet denn im Normalfall die Bewegung statt, bewegt sich der player und ich übertrage die koordinaten auf die der map , oder umgekehrt, also bewege ich den Cursor auf der Map und zeichne den Player auf die koordinaten  ?


----------



## Androbin (21. Jul 2014)

Zu deinem "Scrolling"-Problem:
Mach es nicht unnötig kompliziert:
Du brauchst nichts weiter als 2 Variablen "sX" und "sY", welche du beim Zeichnen den jeweiligen x-, bzw. y- Koordinaten addierst!

Nächstes Problem:
Du solltest keine Tiles zeichnen, welche sich nicht im sichtbaren Bereich befinden:

```
int bx1 = -sX / Block.size - 1;
int bx2 = ( getWidth () - sX ) / Block.size;

int by1 = -sY / Block.size - 1;
int by2 = ( getHeight() - sY ) / Block.size;

for ( int x = bx1; x <= bx2; x++ )
for ( int y = by1; y <= by2; y++ )
g.drawImage( tileimage, ..., this );
```
So stellst du sicher, dass du keine überflüssigen Tiles zeichnest, was zu enormen Performance-Einbußen führen würde!


----------



## kaoZ (21. Jul 2014)

Das kann bei der momentanen implementierung nicht passieren , da ich die Tiles so groß zeichne das sie genau in das Spielfeld passen, später wenn die map dann noch scrollbar wird hast du natürlich vollkommen recht.

meine map besteht zzt. nur aus einem int[][] welches pro feld nur entweder 0 oder 1 enthält, also sollte ich doch hergehen und lieber mit einer separaten klasse arbeiten und dann die map in einem 

Tile[][] array kapseln ? so könnte ich zumindest jedem Tile koordinaten zuweisen , so wie es momentan ist ja nicht .

So sieht die klasse TileMap momentan aus, 
	
	
	
	





```
hier sind die x und y koordinaten noch nicht genutzt, getter und setter sind dementsprechend noch nicht implementiert
```
 :




Spoiler: TileMap.java





```
import java.awt.Color;
import java.awt.Graphics;
import java.io.BufferedReader;
import java.io.InputStreamReader;


public class TileMap {
	
	int x;
	int y;
	
	int width;
	int height;
	int tileSize;
	
	int[][] map;
	
	public TileMap(String path, int tileSize) {
		
		this.tileSize = tileSize;
		
		InputStreamReader in;
		BufferedReader br;
		
		try {
			
			in = new InputStreamReader(getClass().getResourceAsStream(path));
			br = new BufferedReader(in);
			
			width = Integer.parseInt(br.readLine());
			height = Integer.parseInt(br.readLine());
			
			map = new int[height][width];
			
			for (int row = 0; row < height; row++) {
				String line = br.readLine();
				String[] tokens = line.split("\\s+");
				for (int col = 0; col < width; col++) {
					map[row][col] = Integer.parseInt(tokens[col]);
				}
			}
			
			
		} catch (Exception ex) {
			ex.printStackTrace();
		}
		
	}
	
	public void update(){
		
	}
	
	public void draw(Graphics g){
		for (int row = 0; row < map.length; row++) {
			for (int col = 0; col < map.length; col++) {
				
				int rc = map[row][col];
				
				if (rc == 0) {
					g.setColor(Color.BLACK);
				}
				if (rc == 1) {
					g.setColor(Color.WHITE);
				}
				
				g.fillRect(col * tileSize, row * tileSize, tileSize, tileSize);
			}
		}
	}
}
```




[EDIT]So wie es jetzt ist könnte ich eine Kollision ja nur abfragen indem ich jedes Pixel abfrage ob es WEIß oder SCHWARZ ist , oder sehe ich das Falsch ?[/EDIT]


----------



## Androbin (21. Jul 2014)

Ich rate dir dringend, von deinem int[][] abzulassen und stattdessen eine HashMap<Point, Tile>
oder meinetwegen auch eine HashMap<Point, Boolean> zu nutzen!


----------



## kaoZ (21. Jul 2014)

> Falls du nicht weißt, was eine HashMap<K, V> ist: Tipp:



Ernsthaft ?, sicher weiß ich was eine HashMap ist.......

es geht im Grunde darum das ich so wie es jetzt implementiert ist , es ja keine einzelnen Tiles gibt , sondern ein einfaches mehrdimensionales int array welches die daten in Form von einfachen Ganzahlen enthält und eben keien Koordinaten für die einzelnen Felder !

Deswegen ja auch die Frage ob es nicht sinnvoll wäre diese Daten in einem Tile objekt zu kapseln,

und anstelle eines Integer Arrays , ein Tile Array oder eben eine HashMap zu nehmen, welche dann Information wie position und Typ enthält

Ich habe halt grade noch Probleme dabei nachzuvollziehen wie ich die Position von Player und map aufeinander abbilde, und nicht mit der Datenhaltung an sich, da ich sowas wie bereits erwähnt vorher noch nicht gemacht habe......


----------



## Androbin (21. Jul 2014)

Wenn du zu faul für eine Klasse "Tile" bist, könntest du doch auch einfach Boolean's nehmen!


----------



## kaoZ (21. Jul 2014)

> Wenn du zu faul für eine Klasse "Tile" bist



das zu implementieren dauert keine 2 Minuten, darum gehts ja auch garnicht, sondern darum ob es das ganze einfacher macht die Daten in je einem Separaten Objekt zu kapseln.

Es geht eigentlich immernoch um das hier :

[WR]Ich habe halt grade noch Probleme dabei nachzuvollziehen wie ich die Position von Player und map aufeinander abbilde.[/WR]


----------



## Androbin (21. Jul 2014)

kaoZ hat gesagt.:
			
		

> ... Ich habe halt grade noch Probleme dabei nachzuvollziehen wie ich die Position von Player und map aufeinander abbilde. ...



WO LIEGT DAS PROBLEM ???

```
g.setColor( Color.BLACK );
for ( int x = 0; x < lengthX; x++ )
for ( int y = 0; Y < lengthY; y++ )
if ( map[ x ][ y ] == 1 )
g.fillRect( x * tileSize + sX, y * tileSize + sY, tileSize, tileSize );

g.setColor( Color.RED );
g.fillRect( player.x + sX, player.y + sY, player.width, player.height);
```


----------



## Tobse (22. Jul 2014)

Wegen des Klassendeisgns bei der Kollision habe ich noch was beizutragen. Auch wenn das jetzt bissl komisch wirkt aber schon bei simplen Spielen wie Tetris stößt man auf genau dieses Problem. Meine Version ist jetzt zwar in JavaScript aber so flexibel sind wir meiner meinung nach:


```
Brick = function(stoneMatrix0deg, stoneMatrix90deg, stoneMatrix180deg,
    stoneMatrix270deg, color) {
    
    [...]
    
    var matrices = [stoneMatrix0deg, stoneMatrix90deg, stoneMatrix180deg,
        stoneMatrix270deg],
        rotation = 0;
    
    // übersetzt das übergebene boolean[][] in html-elemente um, zeichnet das aber NICHT auf die Spielfläche!
    this.updateUI = function(stoneMatrix)
    {
        [...]
    };
    
    this.updateUI(stoneMatrix0deg);
    
    this.x = 0;
    this.y = 0;
    
    this.setColor = function(color)
    {
        [...]
    };
    this.getWidth = function() {
        return this.stoneMatrix[0].length;
    };
    this.getHeight = function() {
        return this.stoneMatrix.length;
    };

    // diese Methode wird von der Zeichenfläche selbst aufgerufen um den Stein anzuzeigen
    this.getDOMElement = function() {
        return this.dom;
    };
    this.setPosition = function(x, y) {
        [...]
    };

    this.moveUp = function(n)
    {
        this.setPosition(this.x, this.y - n);
    };
    this.moveDown = function(n)
    {
        this.setPosition(this.x, this.y + n);
    };
    this.moveLeft = function(n)
    {
        this.setPosition(this.x - n, this.y);
    };
    this.moveRight = function(n)
    {
        this.setPosition(this.x + n, this.y);
    };

    // HIER DIE KOLLISIONSERKENNUNG
    // GMATRIX ist ein zwei-dimensionales array welches das spielfeld repräsentiert. Die Darstellung wird aus diesem Array abgeleitet
    // Methode collides weiter unten
    this.tryMoveDown = function(n, GMATRIX)
    {
        this.y += n;
        var s = this.collides(GMATRIX);
        this.y -= n;
        
        if (s)
        {
            return false;
        }
        else
        {
            this.moveDown(n);
            return true;
        }
    };
    this.tryMoveLeft = function(n, GMATRIX)
    {
        [...] Analog zu den anderen tryMoves
    };
    this.tryMoveRight = function(n, GMATRIX)
    {
        [...] Analog zu den anderen tryMoves
    };
    this.getPosition = function()
    {
        return {x: this.x, y: this.y};
    };
    this.rotateRight = function()
    {
        [...]
    };
    this.rotateLeft = function()
    {
        [...]
    };
    this.setRotation = function(r)
    {
        [...]
        this.updateUI(matrices[rotation]);
    }
    this.setGhostBrick = function(is) {
        [...]
    };
    this.isGhostBrick = function() {
        return $(this.dom).hasClass("ghost");
    };
    this.collides = function(GMATRIX)
    {
        // ausserhalb des spielfeldes?
        if (this.x < 0 || this.y < 0 ||
            GMATRIX.length <= this.y + this.getHeight() ||
            GMATRIX[0].length < this.x + this.getWidth())
        {
            return true;
        }
        for (var r = 0;r < this.stoneMatrix.length;r++)
        {
            for (var c = 0;c < this.stoneMatrix[0].length;c++)
            {
                if (this.stoneMatrix[r][c] == 1)
                { // do we need to check this stone?
                    if (GMATRIX[r + this.y][c + this.x] != 0)
                    {
                        // alert("Collides at: " + (r + this.y) + "x" + (c + this.x));
                        return true;
                    }
                }
            }
        }
        return false;
    }

    // Diese funktion überträgt nun diesen block in ein spielfeld. Diese Methode wird vom Logik-Code erst dann aufgerufen wenn eindeutig feststeht, dass der Block sich an seiner Aktuellen Position auch wirklich aufhalten kann
    this.applyTo = function(GMATRIX)
    {
        if (this.collides(GMATRIX))
        {
            return;
        }
        for (var r = 0;r < this.stoneMatrix.length;r++)
        {
            for (var c = 0;c < this.stoneMatrix[r].length;c++)
            {
                if (this.stoneMatrix[r][c] == 1)
                {
                    GMATRIX[r + this.y][c + this.x] = this.color;
                }
            }
        }
        $(this.dom).remove();
    };
    this.clone = function()
    {
        [...]
    }
};
IBrick = function() {
    Brick.apply(this, [[
        [1],
        [1],
        [1],
        [1]
    ], [
        [1, 1, 1, 1]
    ], [
        [1],
        [1],
        [1],
        [1]
    ], [
        [1, 1, 1, 1]
    ], "blue"]);

    this.getType = function()
    {
        return "i";
    };
    
    $(this.dom).addClass("brick-" + this.getType());
};
```

Hier sind gleich mehrere Sachen zu erkennen:
Die Blöcke haben ihre eigenen Koordinaten welche auch verändert werden können ohne dass das direkt einen Einfluss hat. Stößt der Nutzer eine Aktion an wird sie geprüft. Ist sie zulässig wird der neue zustand übernommen _und dann erst gezeichnet_:


```
$(document)
.ready(function() {
    [...]
    
    // register keys
    $(document).keydown(function(evt)
    {
        if (CURRENT_FALLING_BRICK == null)
        {
            return;
        }
        var action = getAction(evt);
        if (action == ACTION.HARD_DROP)
        {
            window.clearInterval(FALLING_TIMEOUT);

            // how long will it fall?
            do
            {
                CURRENT_FALLING_BRICK.moveDown(1);
            }
            while (!CURRENT_FALLING_BRICK.collides(GMATRIX));
            CURRENT_FALLING_BRICK.moveUp(1);

            applyBrick(CURRENT_FALLING_BRICK, GMATRIX);
        }
        else if (action == ACTION.MOVE_LEFT)
        {
            CURRENT_FALLING_BRICK.tryMoveLeft(1, GMATRIX);
        }
        else if (action == ACTION.MOVE_RIGHT)
        {
            CURRENT_FALLING_BRICK.tryMoveRight(1, GMATRIX);
        }
        else if (action == ACTION.SOFT_DROP)
        {
            CURRENT_FALLING_BRICK.tryMoveDown(1, GMATRIX);
        }
        else if (action == ACTION.ROTATE_RIGHT || action == ACTION.ROTATE_LEFT)
        {
            if (action == ACTION.ROTATE_RIGHT)
            {
                CURRENT_FALLING_BRICK.rotateRight();
            }
            else
            {
                CURRENT_FALLING_BRICK.rotateLeft();
            }
            
            // check whether the block hits the right edge
            if (CURRENT_FALLING_BRICK.getPosition().x + CURRENT_FALLING_BRICK.getWidth() > WIDTH)
            {
                do
                {
                    CURRENT_FALLING_BRICK.moveLeft(1);
                }
                while (CURRENT_FALLING_BRICK.getPosition().x + CURRENT_FALLING_BRICK.getWidth() > WIDTH);
                
                if (CURRENT_FALLING_BRICK.collides(GMATRIX))
                { // rotate not possible, rewind
                    if (action == ACTION.ROTATE_RIGHT)
                    {
                        CURRENT_FALLING_BRICK.rotateLeft();
                    }
                    else
                    {
                        CURRENT_FALLING_BRICK.rotateRight();
                    }
                }
            }
            else if (CURRENT_FALLING_BRICK.collides(GMATRIX))
            { // rotate not possible, rewind
                if (action == ACTION.ROTATE_RIGHT)
                {
                    CURRENT_FALLING_BRICK.rotateLeft();
                }
                else
                {
                    CURRENT_FALLING_BRICK.rotateRight();
                }
            }
        }
        else if (action == ACTION.PAUSE_RESUME)
        {
            if (FALLING_TIMEOUT == null)
            {
                RESUME();
            }
            else
            {
                PAUSE();
            }
        }
        
        updateGhost(CURRENT_FALLING_BRICK, GMATRIX);
    });
    
    [...]
});
```

(Gesamter Code hier)

Dieses Konzept funktioniert einwandfrei. Bei Komplexeren Spielen (alles was im 3D-Bereich abläuft) halte ich es so oder so für sinvoller, alles mit einer Physik-Engine zu machen. Die hat für Geschwindigkeit (Achtung: Geschwindigkeit ist ein Vektor, enthält also auch die Richtung) und positionen die passenden Klassen und wenn sie für Spiele ausgelegt ist auch entsprechende Methoden wie meine [c]collides[/c].


----------



## kaoZ (22. Jul 2014)

Ok, das muss ich mir in ruhe durchlesen, ich hab die Nacht mal ein wenig rumgespielt, und bin bis jetzt zu folgender lösung für mein Kollisionsproblem gekommen:

[EDIT]PS : Von JavaScipt hab ich null Plan , aber da Die Syntax quasi identisch ist , wird das schon klappen  danke dafür Tobse[/EDIT]

Ich erstelle eine abstrakte Basisklasse 



Spoiler: CollisionObjekt.java





```
public abstract class CollisionOject implements DrawableObject {
	
	private int x;
	private int y;
	
	private int width;
	private int height;
	private int type;
	
	public CollisionOject(int width, int height, int type) {
		this.width = width;
		this.height = height;
		this.type = type;
		
		setPosition(0, 0);
	}
	
	public void setPosition(int x, int y){
		this.x = x;
		this.y = y;
	}
	
	public Rectangle getBounds(){
		return new Rectangle(x, y, width, height);
	}
	
	public int getX()			{return this.x;}
	public int getY()			{return this.y;}
	public int getWidth()		{return this.width;}
	public int getHeight()		{return this.height;}
	public int getType()		{return this.type;}

}
```




von dieser habe ich zum testen erstmal eine Spezialisierung mit grundimplementationnen erstellt : 



Spoiler: Block.java





```
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;

public class Block extends CollisionOject {
	
	public Block(int width, int height, int type) {
		super(width, height, type);
	}

	@Override
	public void update() {
	}

	@Override
	public void render(Graphics g) {
		
		Graphics2D g2 = (Graphics2D) g;
		
		g2.setColor(Color.WHITE);
		g2.fillRect(super.getX(), super.getY(), super.getWidth(), super.getHeight());
		
		Rectangle collisionBox = this.getBounds();
		
		g2.setColor(Color.CYAN);
		g2.draw(collisionBox);
		
	}
}
```




diesen habe ich zum testen mal in der Klasse TileMap inclusive einer eigenen Hitbox zeichnen lassen:



Spoiler: TileMap.java





```
public class TileMap implements DrawableObject{

	private int tileSize;
	
	//CollisionObjekt test
	CollisionOject block;
	
	public TileMap(int tileSize) {
		this.tileSize = tileSize;
		
		//setup collisionobjekt for testing
		block = new Block(50, 50, 1);
		block.setPosition(100, 100);
	}
	
	@Override
	public void update() {
	}

	@Override
	public void render(Graphics g) {
		
		Graphics2D g2 = (Graphics2D) g;
		
		//draw collisionobjekt for testing
		g2.setColor(Color.WHITE);
		block.render(g2);
		
	}
	
	public int getTileSize()		{return this.tileSize;}
	public CollisionOject getCollisionObjekt()		{return this.block;}

}
```




in der Klasse Player habe ich nun eine Methode implementiert die die Hitbox welche ich auf die Bounds des Players + 1 setze auf collision mit einem Collisionobjekt testet, hier fehlt noch die implementierung der bewegungsposition und es war erstmal ein reiner funktionstest, klappt aber !:



Spoiler: Player.java





```
public class Player implements DrawableObject{

	private int x;
	private int y;
	
	private int dx;
	private int dy;
	
	private int moveSpeed;
	
	private int width;
	private int height;
	
	
	private boolean right;
	private boolean left;
	private boolean up;
	private boolean down;
	
	private Rectangle hitbox;
	
	private TileMap map;
	
	public Player(TileMap map) {
		
		width = 20;
		height = 20;
		moveSpeed = 3;
		
		this.map = map;
		
		hitbox = new Rectangle(0,0,0,0);
	}
	
	@Override
	public void update() {
		
		if (right) {
			dx += moveSpeed;
		}
		else if (left) {
			dx -= moveSpeed;
		}
		
		if (up) {
			dy += moveSpeed; // only for testing collision
		}
		else if (down) {
			
		}
		
		hitbox.setBounds(dx - 1, dy - 1, width + 1, height + 1);
		
		//check Collide testing
		if (checkCollision(map.getCollisionObjekt())) {
			dx = map.getCollisionObjekt().getX() - width;
		}
		
		x = dx;
		y = dy;
	}

	@Override
	public void render(Graphics g) {
		
		Graphics2D g2 = (Graphics2D) g;
	
		//hitbox
		g2.setColor(Color.GREEN);
		g2.draw(hitbox);
		
		//player
		g2.setColor(Color.RED);
		g2.fillRect(x, y, width, height);
		
	}
	
	public boolean checkCollision(CollisionOject object){
		if(hitbox.intersects(object.getBounds())){
			return true;
		}
		else{
			return false;
		}
	}
	
	public void keyPressed(int k){
		if (k == KeyEvent.VK_W) {
			moveUp(true);
		}
		if (k == KeyEvent.VK_A) {
			moveLeft(true);
		}
	
		if (k == KeyEvent.VK_D) {
			moveRight(true);
		}

	}

	public void keyReleased(int k){
		if (k == KeyEvent.VK_W) {
			moveUp(false);
		}
		if (k == KeyEvent.VK_A) {
			moveLeft(false);
		}
	
		if (k == KeyEvent.VK_D) {
			moveRight(false);
		}
	}
	
	public void moveRight(boolean dir)	{this.right = dir;}
	public void moveLeft(boolean dir)	{this.left = dir;}
	public void moveUp(boolean dir){
		if (!down) {
			up = dir;
		}
		
	}

	
	public int getX()			{return this.x;}
	public int getY()			{return this.y;}
	public int getWidth()		{return this.width;}
	public int getHeight()		{return this.height;}
	public int getMoveSpeed()	{return this.moveSpeed;}

}
```




nun kann ich mich darum kümmern die TileMap vernünftig zu implementieren , die Unterscheidung ob ein Object Kollisionsfähig ist oder nicht implementiere ich dann direkt in der Klasse CollisionObjekt, dazu übergebe ich hier im Konstruktor bereits einen typ anhand eines Integers.





_Hitboxen sind hier einmal grün und einmal Cyan angedeutet_

Zusätzlich werde ich mir nochmal gedanken darum machen die Steuerung in eine eigene Klasse auszulagern.

Ich denke mal der Ansatz mit der Hitbox ist besser als wirklich nur die Eckpunkte auf Kollision zu testen, vor allem war es auch einfacher zu implementieren, ich werde mal schauen wie weit ich komme und das Ergebnis hier mal posten.

Das Problem mit dem übertragen der Koordinaten vom Player auf die map habe ich ebenfalls gelöst, ich zeichne dann den Player erst nachdem eine Kollisionsabfrage mit den Koordinaten / Tile / CollisionObjekt der map stattgefunden hat, auf die erwartete Position.


----------



## Androbin (22. Jul 2014)

Wieso nicht gleich so?
Und wieso nicht einfach "CollisionObject" von "Rectangle" ableiten?

PS. Das 
	
	
	
	





```
setPosition(0, 0);
```
 kannst du dir sparen!

PPS. Das hier könnte dir noch mal nützlich sein: VaryCode.com - Online Code Language Converter


----------



## Tobse (22. Jul 2014)

Androbin hat gesagt.:


> Wieso nicht gleich so?
> [...]
> PS. Das
> 
> ...




Mich wundert woher auf einmal dein großes Wissen und deine Selbstsicherheit gegenüber den anderen Usern kommt - wo du doch vor nichtmal einer Woche nicht in der Lage warst eine NPE aufzuspüren! :O



Androbin hat gesagt.:


> PPS. Das hier könnte dir noch mal nützlich sein: VaryCode.com - Online Code Language Converter


Ach das ist doch totaler schwachsinn. Selbst wenn der Code richtig überzt wird - man muss ihn auf Herz und Nieren auf Bugs testen sonst stellt man sich selbst mehrere Beine. Wozu würde er den überhaupt brauchen?


----------



## kaoZ (22. Jul 2014)

> Wieso nicht gleich so?



Manchmal hab ich so meine Momente ^^



> Und wieso nicht einfach "CollisionObject" von "Rectangle" ableiten?



Vielleicht möchte ich später ja mal Runde Kollisionsobjekte implementieren, auch wenn das bedeutet das ich dann nicht mehr mit 
	
	
	
	





```
intersects(Rectangle)
```
arbeiten kann, heißt es ja nicht das ich nicht einfach eine eigene Methode anbiete welche dann die Kollision zwischen Kreis und Rechteck berechnet. 



> PS. Das setPosition(0, 0); kannst du dir sparen!



später ja, aber zum testen musste ich den block auf eine fixe Position Zeichen, da ich das einlesen der eigentlichen map daten noch nicht implementiert habe.


----------



## Androbin (23. Jul 2014)

: Tobse *8*


			
				Tobse hat gesagt.:
			
		

> Mich wundert woher auf einmal dein großes Wissen und deine Selbstsicherheit gegenüber den anderen Usern kommt - wo du doch vor nichtmal einer Woche nicht in der Lage warst eine NPE aufzuspüren! :O


Naja, ich lerne eben schnell 


			
				Tobse hat gesagt.:
			
		

> Wozu würde er den überhaupt brauchen?


Na, wegen dem JavaScript-Codeschnipsel weiter unten!

: kaoZ *8*


			
				kaoZ hat gesagt.:
			
		

> Androbin hat gesagt.:
> 
> 
> 
> ...


Du verstehst es immer noch nicht!
[TIPP] Integer werden automatisch mit 0 initialisiert! [/TIPP]


----------



## kaoZ (23. Jul 2014)

Wenn du den setter im Konstuktor meintest, der ist schon lange raus....

musst halt schon dazu schreiben in welchem Kontext es gemeint war...... dachte du meintest das setzen der Position im GamePanel.


----------



## Tobse (23. Jul 2014)

Androbin hat gesagt.:
			
		

> Integer werden automatisch mit 0 initialisiert!



Erzähl doch keinen Schwachfug!


```
int i;

System.out.println(i);
```



			
				javac hat gesagt.:
			
		

> Variable i might not have been initialized.


----------



## kaoZ (23. Jul 2014)

Isofern er sich damit auf instanzvariablen bezieht spricht nichts dagegen, bei lokalen allerdings isses bullshit, und deshalb bin ich auch nicht weiter darauf ein gegangen.


----------



## kaoZ (24. Jul 2014)

So, ich habe augenscheinlich noch ein kleines MovementProblem , die untere Grafik zeigt an wie es korrekt funktioniert, 

der Player befindet sich hier bei x = 0 | y = 0





die daten die ich mir in dem HUD ausgeben lasse stimmen also alle, das Tile ist als BLOCKED makiert und somit bei der späteren Kollisionsabfrage auch so nutzbar, 

wenn ich nun allerdings die Position des Players zu beginn auf sagen wir x = 50 | y = 50 setze, passen logischerweise die koordinaten nicht mehr.

ZZt. übergebe ich die dx und dy Position an die map und initialisiere so die x und y koordinaten der map 
beim Zeichnen der Map ziehe ich subtrahiere ich jeweils den übergebenen x und y wert

so bewegt sich mein player nach rechts und die map nach links, so wie es ja normalerweise eigentlich auch sein sollte?!

Hier nochmal die Klassen auf dem aktuellen stand :



Spoiler: Player.java





```
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;

import dev.eflow.game.drawable.Drawable;
import dev.eflow.game.movement.Direction;
import dev.eflow.game.tileMap.TileMap;

public class Player implements Drawable{

	private int x;
	private int y;
	
	private int dx;
	private int dy;
	
	private int moveSpeed;
	
	private int width;
	private int height;

	boolean topLeft;
	boolean topRight;
	boolean bottomLeft;
	boolean bottomRight;
	
	private TileMap map;
	
	public Player(TileMap map) {
		
		width = 20;
		height = 20;
		moveSpeed = 3;
		
		this.map = map;

	}
	
	@Override
	public void update() {
		
		if (Direction.RIGHT) {
			dx += moveSpeed;
		}
		else if (Direction.LEFT) {
			dx -= moveSpeed;
		}
		
		if (Direction.UP) {
			dy -= moveSpeed;
		}
		else if (Direction.DOWN) {
			dy += moveSpeed;
		}
		
		map.setX(dx);
		map.setY(dy);
	}

	public void setX(int x){
		this.x = x;
	}
	
	public void setY(int y){
		this.y = y;
	}
	
	@Override
	public void draw(Graphics g) {

		//player
		g.setColor(Color.RED);
		g.fillRect(x, y, width, height);

		
	}
	
	public void keyPressed(int k){
		if (k == KeyEvent.VK_W) {
			Direction.moveUp(true);
		}
		if (k == KeyEvent.VK_A) {
			Direction.moveLeft(true);
		}
	
		if (k == KeyEvent.VK_D) {
			Direction.moveRight(true);
		}
		if (k == KeyEvent.VK_S) {
			Direction.moveDown(true);
		}

	}

	public void keyReleased(int k){
		if (k == KeyEvent.VK_W) {
			Direction.moveUp(false);
		}
		if (k == KeyEvent.VK_A) {
			Direction.moveLeft(false);
		}
	
		if (k == KeyEvent.VK_D) {
			Direction.moveRight(false);
		}
		if (k == KeyEvent.VK_S) {
			Direction.moveDown(false);
		}
	}
	
	public int getX()			{return this.x;}
	public int getY()			{return this.y;}
	public int getWidth()		{return this.width;}
	public int getHeight()		{return this.height;}
	public int getMoveSpeed()	{return this.moveSpeed;}

}
```




Die klasse die für das Movement zuständig ist :



Spoiler: Direction.java





```
public class Direction {

	public static boolean LEFT;
	public static boolean RIGHT;
	public static boolean UP;
	public static boolean DOWN;
	
	public Direction() {
	}
	
	public static void moveLeft(boolean val)	{LEFT = val;}
	public static void moveRight(boolean val)	{RIGHT = val;}
	public static void moveDown(boolean val)	{DOWN = val;}
	
	public static void moveUp(boolean val){
		if (!DOWN) {
			UP = val;
		}
	}
}
```




und hier die TileMap auf dem jetzigen stand : 



Spoiler: TileMap.java





```
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import javax.imageio.ImageIO;

import dev.eflow.game.drawable.Drawable;

public class TileMap implements Drawable{

	public static String DELIMITER = "\\s+";
	
	private int x;
	private int y;
	
	private int mapHeight;
	private int mapWidth;
	
	private int[][] map;
	
	private Tile[][] tiles;
	private BufferedImage tileSet;
	
	private String path;
	private int tileSize;

	public TileMap(String path, int tileSize) {
		this.path = path;
		this.tileSize = tileSize;
		
		InputStreamReader in;
		BufferedReader reader;
		
		try {
			in = new InputStreamReader(getClass().getResourceAsStream(path));
			reader = new BufferedReader(in);
			
			mapHeight = Integer.parseInt(reader.readLine());
			mapWidth = Integer.parseInt(reader.readLine());
			
			map = new int[mapHeight][mapWidth];

			for (int row = 0; row < mapHeight; row++) {
				String line = reader.readLine();
				String[] tokens = line.split(DELIMITER);
				for (int col = 0; col < mapWidth; col++) {
					map[row][col] = Integer.parseInt(tokens[col]);
				}
			}
			
		} catch (IOException | NullPointerException ex) {
			ex.printStackTrace();
		}
	}
	
	public void loadTiles(String path){

		try {
			tileSet = ImageIO.read(getClass().getResourceAsStream(path));
			
			int numTilesAcross = tileSet.getWidth() / tileSize;
			BufferedImage subImage;
			
			tiles = new Tile[2][numTilesAcross];

			for (int col = 0; col < numTilesAcross - 1; col++) {
				subImage = tileSet.getSubimage(col * tileSize, 0, tileSize, tileSize);
				tiles[0][col] = new Tile(subImage, false);
				subImage = tileSet.getSubimage(col * tileSize, tileSize, tileSize, tileSize);
				tiles[1][col] = new Tile(subImage, true);
			}
			
		} catch (Exception ex) {
			ex.printStackTrace();
		}
		
	}
	
	@Override
	public void update() {

	}

	@Override
	public void draw(Graphics g) {
		
		for (int row = 0; row < mapHeight; row++) {
			for (int col = 0; col < mapWidth; col++) {
				
				//aktueller Wert der Position in der Map
				int rc = map[row][col];
				
				//aktuellen wert in position des tiles umrechnen
				int c = rc / tiles[0].length;
				int r = rc % tiles[0].length;

				//tile an stelle c,r zeichen
				g.drawImage(tiles[c][r].getImage(),col * tileSize - x,row  * tileSize - y, tileSize, tileSize, null);
				
				//raster zur besseren übersicht der Position
				g.setColor(Color.LIGHT_GRAY);
				g.drawRect(col * tileSize - x, row * tileSize - y, tileSize, tileSize);
			}
		}
	}
	
	public int getColTile(int x){
		return x / tileSize;
	}
	
	public int getRowTile(int y){
		return y / tileSize;
	}
	
	public int getTile(int row, int col){
		return map[row][col];
	}
	
	public boolean isBlocked(int row, int col){
		int rc = map[row][col];
		int r = rc / tiles[0].length;
		int c = rc % tiles[0].length;
		return tiles[r][c].isBlocked();
	}

	
	public void setX(int x){
		this.x = x;
	}
	
	public void setY(int y){
		this.y = y;
	}
	
	public int getX()							{return this.x;}
	public int getY()							{return this.y;}
	public int getWidth()						{return this.mapWidth;}
	public int getHeight()						{return this.mapHeight;}
	public String getPath()						{return this.path;}
	public int getTileSize()					{return this.tileSize;}

}
```




Das einlesen und abbilden der Images funktioniert reibungslos, einzig und alleine Movement und Kamera/Mapführung machen mir noch kopfzerbrechen, ich weiß das ich die Aktuelle Position noch temporär festhalten muss wenn ich dann zur kollisionsprüfung komme, aber ich wollte erstmal wissen ob mein Ansatz so wenigstens richtig ist, was das movement betrifft.


----------



## kaoZ (24. Jul 2014)

Die x und y Position des Players verändert sich hier allerdings nicht , sondern lediglich die Map wird verschoben, wenn ich jetzt zusätzlich die Position des Players setze 


```
@Override
	public void update() {
		
		if (Direction.RIGHT) {
			dx += moveSpeed;
		}
		else if (Direction.LEFT) {
			dx -= moveSpeed;
		}
		
		if (Direction.UP) {
			dy -= moveSpeed;
		}
		else if (Direction.DOWN) {
			dy += moveSpeed;
		}
		
		map.setX(dx);
		map.setY(dy);
		
		setX(map.getX());  // <<--
		setY(map.getY());  // <<--
		
		if (Direction.DOWN && map.isBlocked(map.getRowTile(map.getY() + height), map.getColTile(map.getX()))) {
			
			System.out.println("Collide " + map.getX() + " " + map.getY());
		}
	}
```

Bewegt sich zwar der player und die map , aber es

passen die Berechnungen hinsichtlich spalte und Zeile ja nicht mehr, was wiederum dazu führt das die tiles nicht korrekt erkannst werden was wiederrum zu einer falschen kollisionsabfrage führt, wäre top wenn mir da ggf. schnell jemand mal nen überblick geben könnte wie wie man das ganze angeht, was das movement auf der map angeht.

Hier nochmal ein Screen wenn ich player und map position setze :


----------



## Androbin (24. Jul 2014)

Nur um das klarzustellen:
Du verschiebst NICHT alle Tiles, sondern zeichnest diese nur versetzt, korrekt?

Wenn Ja: WIESO?
Wenn Nein: Wo liegt dann das Problem?


----------



## kaoZ (24. Jul 2014)

So sieht die klasse TileMap aus :



Spoiler





```
package dev.eflow.game.tileMap;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

import javax.imageio.ImageIO;

import dev.eflow.game.drawable.Drawable;

public class TileMap implements Drawable{

	public static String DELIMITER = "\\s+";
	
	private int x;
	private int y;
	
	private int mapHeight;
	private int mapWidth;
	
	private int[][] map;
	
	private Tile[][] tiles;
	private BufferedImage tileSet;
	
	private String path;
	private int tileSize;

	public TileMap(String path, int tileSize) {
		this.path = path;
		this.tileSize = tileSize;
		
		InputStreamReader in;
		BufferedReader reader;
		
		try {
			in = new InputStreamReader(getClass().getResourceAsStream(path));
			reader = new BufferedReader(in);
			
			mapHeight = Integer.parseInt(reader.readLine());
			mapWidth = Integer.parseInt(reader.readLine());
			
			map = new int[mapHeight][mapWidth];

			for (int row = 0; row < mapHeight; row++) {
				String line = reader.readLine();
				String[] tokens = line.split(DELIMITER);
				for (int col = 0; col < mapWidth; col++) {
					map[row][col] = Integer.parseInt(tokens[col]);
				}
			}
			
		} catch (IOException | NullPointerException ex) {
			ex.printStackTrace();
		}
	}
	
	public void loadTiles(String path){

		try {
			tileSet = ImageIO.read(getClass().getResourceAsStream(path));
			
			int numTilesAcross = tileSet.getWidth() / tileSize;
			BufferedImage subImage;
			
			tiles = new Tile[2][numTilesAcross];

			for (int col = 0; col < numTilesAcross - 1; col++) {
				subImage = tileSet.getSubimage(col * tileSize, 0, tileSize, tileSize);
				tiles[0][col] = new Tile(subImage, false);
				subImage = tileSet.getSubimage(col * tileSize, tileSize, tileSize, tileSize);
				tiles[1][col] = new Tile(subImage, true);
			}
			
		} catch (Exception ex) {
			ex.printStackTrace();
		}
		
	}
	
	@Override
	public void update() {

	}

	@Override
	public void draw(Graphics g) {
		
		for (int row = 0; row < mapHeight; row++) {
			for (int col = 0; col < mapWidth; col++) {
				
				//aktueller Wert der Position in der Map
				int rc = map[row][col];
				
				//aktuellen wert in position des tiles umrechnen
				int c = rc / tiles[0].length;
				int r = rc % tiles[0].length;

				//tile an stelle c,r zeichen
				g.drawImage(tiles[c][r].getImage(),col * tileSize - x,row  * tileSize - y, tileSize, tileSize, null);
				
				//raster zur besseren übersicht der Position
				g.setColor(Color.LIGHT_GRAY);
				g.drawRect(col * tileSize - x, row * tileSize - y, tileSize, tileSize);
			}
		}
	}
	
	public int getColTile(int x){
		return x / tileSize;
	}
	
	public int getRowTile(int y){
		return y / tileSize;
	}
	
	public int getTile(int row, int col){
		return map[row][col];
	}
	
	public boolean isBlocked(int row, int col){
		int rc = map[row][col];
		int r = rc / tiles[0].length;
		int c = rc % tiles[0].length;
		return tiles[r][c].isBlocked();
	}

	
	public void setX(int x){
		this.x = x;
	}
	
	public void setY(int y){
		this.y = y;
	}
	
	public int getX()							{return this.x;}
	public int getY()							{return this.y;}
	public int getWidth()						{return this.mapWidth;}
	public int getHeight()						{return this.mapHeight;}
	public String getPath()						{return this.path;}
	public int getTileSize()					{return this.tileSize;}

}
```




*die X und Y Werte werden in der Update Methode der Klasse Player gesetzt  :*


```
@Override
	public void update() {
		
		if (Direction.RIGHT) {
			dx += moveSpeed;
		}
		else if (Direction.LEFT) {
			dx -= moveSpeed;
		}
		
		if (Direction.UP) {
			dy -= moveSpeed;
		}
		else if (Direction.DOWN) {
			dy += moveSpeed;
		}
		
		map.setX(dx);  // <<<
		map.setY(dy);  // <<<
		
		if (Direction.DOWN && map.isBlocked(map.getRowTile(map.getY() + height), map.getColTile(map.getX()))) {
			
			System.out.println("Collide " + map.getX() + " " + map.getY());
		}
	}
```


wenn ich nun also den player bewege , wird die map entsprechend an diese koordinaten gezeichnet :


```
g.drawImage(tiles[c][r].getImage(),col * tileSize - x, row  * tileSize - y, tileSize, tileSize, null);
```

also werden die tiles , wenn sich der player nach rechts bewegt, dementsprechend nach links bewegt und den Wert um welchen sich die Position des players nach rechts erhöht hat, dies schien mir logisch , kann natürlich durchaus sein das ich da irgendwo einen Denkfehler habe, aber ich war der Meinung das es so ablaufen muss :

Spieler bewegt sich nach rechts (Direction.RIGHT) :
   Player -> (x++)
   Map    <- (x--)

Spieler bewegt sich nach links (Direction.LEFT):
   Player <- (x--)
   Map    -> (x++)

ebenso für UP and DOWN.

Falls es nicht so sein sollte helft mir doch mal auf die Sprünge


----------



## turtle (25. Jul 2014)

Ich gebe zu dieser Diskussion nur "sporadisch" zu folgen

Ich hatte bereits das Buch "Killer game programming in java" erwähnt und zitiere daraus folgenden Text, der beschreibt, wie in einem Spiel Bewegungen durchgeführt werden können.

[EDIT]Though I talk about a player moving around the surface, the truth is that the user’s
sprite doesn’t move at all. Instead, the surface moves in the opposite direction,
together with the other objects and sprites.[/EDIT]


----------



## Feeder (25. Jul 2014)

Hier ein tolles Vide dazu: Kollision zweier Rechtecke - [012] - Let's GameDev ++ die nachfolgen Videos  https://www.youtube.com/watch?v=va2orSdXcxw


----------



## Androbin (25. Jul 2014)

Feeder hat gesagt.:
			
		

> Hier ein tolles Vide dazu: Kollision zweier Rechtecke - [012] - Let's GameDev ++ die nachfolgen Videos  https://www.youtube.com/watch?v=va2orSdXcxw



Danke, den Link zur Playlist habe ich jedoch bereits selbst gepostet!


----------



## kaoZ (25. Jul 2014)

Ich habe mich heute Nacht auch schon weiter belesen gebe ich zu, mein Problem War das ich den Player immer "aktiv" bewegen wollte und dies einfach nicht richtig ist, da sich die Position des Players unter anderem, erst daraus ergibt ob ein Kollisionsobjekt in Bewegungsrichtung Vorhandenen ist, oder eben nicht 

Buch ist bereits bestellt turtle 

Aber ich kann nachts zzt nicht pennen, da ich mit das Hirn darüber zermater xD


----------



## Androbin (25. Jul 2014)

Also, zur Zusammenfassung des aktuellen Problems:

Du hast dem Anschein nach Probleme mit der Kollision, da du sowohl den Player als auch die Map bewegst.

Allerdings verschiebst du nicht die Map selbst, sondern zeichnest diese nur versetzt.

Problem: Du bewegst den Player nicht wirklich, sodass er die ganze Zeit nur auf der Stelle läuft.

Lösung: Bewege auch den Player und zeichne ihn entsprechend der Scrolling-Variablen der Map.

PS. Die Schlaflosigkeit ist normal und geht wieder vorbei, glaub' mir, ich weiß, wovon ich schreibe.


----------



## kaoZ (28. Jul 2014)

Also die Kollision funktionierst soweit , ich checke aber immer noch nicht warum das scrolling nicht funktioniert  hier mal die klassen :

Es muss eindeutig mit den Positionen des Players und der Map zusammenhängen.... wenn ich versuche die position der Map folgendermaßen in der Klasse LevelOneState.java in deren Update methode zu setzen :


```
tileMap.setPosition(GamePanel.WIDTH / 2 - player.getx(), GamePanel.HEIGHT / 2 - player.gety());
```

wird zwar der player mittig angezeigt, allerdings wird die map dann immer im Wechsel an dieser Position *und* an *x=0 y=0*
gezeichnet, und ich find den Fehler einfach nicht.....,

falls ihr alle Klassen brauchen sollten lasst es mich wissen dann lade ich diese hoch , ich hab auch schon in dem Tutorial hierzu geschaut und die Klassen auf die Grundlegenden Funktionen verglichen , finde aber keine gravierenden Unterschiede die dieses Verhalten auslösen könnte,



Spoiler: TileMap.java





```
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import javax.imageio.ImageIO;
import dev.eflow.game.GamePanel;
import dev.eflow.game.entity.Drawable;


public class TileMap implements Drawable{
	
	//map
	private double x;
	private double y;

	private int numRows;
	private int numCols;
	
	private int width;
	private int height;
	private int[][] map;
	
	//
	private int rowsToDraw;
	private int colsToDraw;
	
	//tiles
	private BufferedImage tileSet;
	private int numTilesAcross;
	private Tile[][] tiles;
	private int tileSize;
	
	public TileMap(int tileSize){
		this.tileSize = tileSize;
	}

	public void loadMap(String path){
		
		InputStreamReader in;
		BufferedReader br;
		
		try {
			in = new InputStreamReader(getClass().getResourceAsStream(path));
			br = new BufferedReader(in);
			
			numRows = Integer.parseInt(br.readLine());
			numCols = Integer.parseInt(br.readLine());
			
			height = numRows * tileSize;
			width = numCols * tileSize;
			
			rowsToDraw = GamePanel.HEIGHT / tileSize;
			colsToDraw = GamePanel.WIDTH / tileSize;
			
			System.out.println(rowsToDraw + " " + colsToDraw );
			
			map = new int[numRows][numCols];
			
			String delimiter = "\\s+";
			for (int row = 0; row < numRows; row++) {
				String line = br.readLine();
				String[] tokens = line.split(delimiter);
				for (int col = 0; col < numCols; col++) {
					map[row][col] = Integer.parseInt(tokens[col]);
				}
			}
		}
		catch (Exception ex) {
			ex.printStackTrace();
		}
	}
	public void loadTiles(String path){
		
		try {
			tileSet = ImageIO.read(getClass().getResourceAsStream(path));
			numTilesAcross = tileSet.getWidth() / tileSize;
			
			tiles = new Tile[2][numTilesAcross];
			
			BufferedImage subImage;
			
			for (int col = 0; col < numTilesAcross; col++) {
				subImage = tileSet.getSubimage(col * tileSize, 0, tileSize, tileSize);
				tiles[0][col] = new Tile(subImage, Tile.NORMAL);
				
				subImage = tileSet.getSubimage(col * tileSize, tileSize, tileSize, tileSize);
				tiles[1][col] = new Tile(subImage, Tile.BLOCKED);
			}
		}
		catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	@Override
	public void update(){}

	@Override
	public void draw(Graphics g){
		
		//draw map
		for (int row = 0; row < rowsToDraw + 2; row++) {
			for (int col = 0; col < colsToDraw + 2; col++) {
				
				int rc = map[row][col];
				
				int r = rc / numTilesAcross;
				int c = rc % numTilesAcross;
				
				g.drawImage(tiles[r][c].getImage(), (int)x + col * tileSize, (int)y + row * tileSize, null);
			}
		}
	}
	
	public void setPosition(double x, double y){
		this.x = (x - this.x);
		this.y = (y - this.y);
	}
	
	public int getCurrentRow(double x){
		return (int)x / tileSize;
	}
	
	public int getCurrentCol(double y){
		return (int)y / tileSize;
	}
	
	public int getTileType(int row, int col){
		int rc = map[row][col];
		
		int r = rc / numTilesAcross;
		int c = rc % numTilesAcross;
		
		return tiles[r][c].getType();
	}
	
	public int getX()							{return (int)x;}
	public int getY()							{return (int)y;}
	public int getWidth()						{return width;}
	public int getHeight()						{return height;}
	public int getCurrRow(double y)				{return (int)y / tileSize;}
	public int getCurrCol(double x)				{return (int)x / tileSize;}
	public int getTileSize()					{return tileSize;}
	
}
```






Spoiler: MapObject.java





```
import java.awt.Rectangle;
import dev.eflow.game.GamePanel;
import dev.eflow.game.sprites.Tile;
import dev.eflow.game.sprites.TileMap;


public class MapObject{

	//tile Stuff
	protected double xmap;
	protected double ymap;
	protected TileMap tileMap;
	protected int tileSize;
	
	//position and vector
	protected double x;
	protected double y;
	protected double dx;
	protected double dy;
	
	//dimensions
	protected int width;
	protected int height;
	
	//collision box
	protected int cwidth;
	protected int cheight;
	
	//collision
	protected int currRow;
	protected int currCol;
	protected double xdest;
	protected double ydest;
	protected double xtemp;
	protected double ytemp;
	protected boolean topLeft;
	protected boolean topRight;
	protected boolean bottomLeft;
	protected boolean bottomRight;
	
	//animation
	protected Animation animation;
	protected int currentAction;
	protected int previousAction;
	protected boolean facingRight;
	
	//movement
	protected boolean left;
	protected boolean right;
	protected boolean up;
	protected boolean down;
	protected boolean falling;
	protected boolean jumping;
	
	//movement attributes;
	protected double moveSpeed;
	protected double maxSpeed;
	protected double stopSpeed;
	protected double fallSpeed;
	protected double maxFallSpeed;
	protected double jumpStart;
	protected double stopJumpSpeed;
	
	
	public MapObject(TileMap map){
		this.tileMap = map;
		this.tileSize = map.getTileSize();
	}
	
	public Rectangle getBounds(){
		return new Rectangle((int) x - cwidth, (int) y + cheight, cwidth, cheight);
	}
	
	public boolean intersects(MapObject o){
		Rectangle r1 = this.getBounds();
		Rectangle r2 = o.getBounds();
		
		return r1.intersects(r2);
	}
	
	public void calculateCorners(double x, double y){
		int leftTile = (int)(x - cwidth / 2) / tileSize;
		int rightTile = (int)(x + cwidth / 2) / tileSize;
		int topTile = (int)(y - cheight / 2) /  tileSize;
		int bottomTile = (int)(y + cheight / 2) / tileSize;
		
		int tl = tileMap.getTileType(topTile, leftTile);
		int tr = tileMap.getTileType(topTile, rightTile);
		int bl = tileMap.getTileType(bottomTile, leftTile);
		int br = tileMap.getTileType(bottomTile, rightTile);
		
		topLeft = tl == Tile.BLOCKED;
		topRight = tr == Tile.BLOCKED;
		bottomLeft = bl == Tile.BLOCKED;
		bottomRight = br == Tile.BLOCKED;
	}
	
public void checkTileMapCollision() {
		
		currCol = (int)x / tileSize;
		currRow = (int)y / tileSize;
		
		xdest = x + dx;
		ydest = y + dy;
		
		xtemp = x;
		ytemp = y;
		
		calculateCorners(x, ydest);
		if(dy < 0) {
			if(topLeft || topRight) {
				dy = 0;
				ytemp = currRow * tileSize + cheight / 2;
			}
			else {
				ytemp += dy;
			}
		}
		if(dy > 0) {
			if(bottomLeft || bottomRight) {
				dy = 0;
				falling = false;
				ytemp = (currRow + 1) * tileSize - cheight / 2;
			}
			else {
				ytemp += dy;
			}
		}
		
		calculateCorners(xdest, y);
		if(dx < 0) {
			if(topLeft || bottomLeft) {
				dx = 0;
				xtemp = currCol * tileSize + cwidth / 2;
			}
			else {
				xtemp += dx;
			}
		}
		if(dx > 0) {
			if(topRight || bottomRight) {
				dx = 0;
				xtemp = (currCol + 1) * tileSize - cwidth / 2;
			}
			else {
				xtemp += dx;
			}
		}
		
		if(!falling) {
			calculateCorners(x, ydest + 1);
			if(!bottomLeft && !bottomRight) {
				falling = true;
			}
		}
		
	}
	
	public int getx() { return (int)x; }
	public int gety() { return (int)y; }
	public int getWidth() { return width; }
	public int getHeight() { return height; }
	public int getCWidth() { return cwidth; }
	public int getCHeight() { return cheight; }
	
	public void setPosition(double x, double y) {
		this.x = x;
		this.y = y;
	}
	public void setVector(double dx, double dy) {
		this.dx = dx;
		this.dy = dy;
	}
	
	public void setMapPosition() {
		xmap = tileMap.getX();
		ymap = tileMap.getY();
	}
	
	public void setLeft(boolean b) { left = b; }
	public void setRight(boolean b) { right = b; }
	public void setUp(boolean b) { up = b; }
	public void setDown(boolean b) { down = b; }
	public void setJumping(boolean b) { jumping = b; }
	
	public boolean notOnScreen() {
		return x + xmap + width < 0 ||
			x + xmap - width > GamePanel.WIDTH ||
			y + ymap + height < 0 ||
			y + ymap - height > GamePanel.HEIGHT;
	}
}
```




der Player als Unterklasse von MapObject



Spoiler: Player.java





```
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import dev.eflow.game.sprites.TileMap;


public class Player extends MapObject{

	public Player(TileMap map){
		super(map);
		
		width = 30;
		height = 30;
		cwidth = 30;
		cheight = 30;
		
		moveSpeed = 0.3;
		maxSpeed = 1.6;
		stopSpeed = 0.4;
		fallSpeed = 0.15;
		maxFallSpeed = 4.0;
		jumpStart = -4.8;
		stopJumpSpeed = 0.3;
		
	}
	
	private void getNextPosition() {
		
		// movement
		if(left) {
			dx -= moveSpeed;
			if(dx < -maxSpeed) {
				dx = -maxSpeed;
			}
		}
		else if(right) {
			dx += moveSpeed;
			if(dx > maxSpeed) {
				dx = maxSpeed;
			}
		}
		else {
			if(dx > 0) {
				dx -= stopSpeed;
				if(dx < 0) {
					dx = 0;
				}
			}
			else if(dx < 0) {
				dx += stopSpeed;
				if(dx > 0) {
					dx = 0;
				}
			}
		}
		
		if(up) {
			dy -= moveSpeed;
			if(dy < -maxSpeed) {
				dy = -maxSpeed;
			}
		}
		else if(down) {
			dy += moveSpeed;
			if(dy < maxSpeed) {
				dy = maxSpeed;
			}
		}
	}
	
	public void update(){
	
		getNextPosition();
		checkTileMapCollision();
		setPosition(xtemp, ytemp);
	}
	
	public void draw(Graphics g){
		
		setMapPosition();
		
		g.setColor(Color.RED);
		g.fillRect((int)(x + xmap - width / 2), (int) (y + ymap - width / 2), cwidth, cheight);
	}
	
	public void keyPressed(int key){
		if(key == KeyEvent.VK_W) {
			setUp(true);
		}
		if(key == KeyEvent.VK_A) {
			setLeft(true);
		}
		if(key == KeyEvent.VK_S) {
			setDown(true);
		}
		if(key == KeyEvent.VK_D) {
			setRight(true);
		}
	}
	
	public void keyReleased(int key){
		if(key == KeyEvent.VK_W) {
			setUp(false);
		}
		if(key == KeyEvent.VK_A) {
			setLeft(false);
		}
		if(key == KeyEvent.VK_S) {
			setDown(true);
		}
		if(key == KeyEvent.VK_D) {
			setRight(false);
		}
	}
}
```





und die klasse die den Level Repräsentiert



Spoiler: LevelOneState.java





```
import java.awt.Graphics;
import dev.eflow.game.GamePanel;
import dev.eflow.game.entity.Player;
import dev.eflow.game.sprites.Background;
import dev.eflow.game.sprites.TileMap;


public class LevelOneState extends GameState{
	
	//map
	TileMap tileMap;
	
	//player
	Player player;
	
	//background
	Background background;

	public LevelOneState(GameStateManager gsm){
		this.gsm = gsm;
		init();
	}
	
	@Override
	public void init(){
		tileMap = new TileMap(32);
		tileMap.loadMap("/maps/Level1-1.map");
		tileMap.loadTiles("/graphics/tilesets/leveloneset.gif");
		
		background = new Background("/graphics/backgrounds/clouds.gif", 0.1);
		
		player = new Player(tileMap);
		player.setPosition(100, 100);
	}

	@Override
	public void update(){
		
		background.update();
		player.update();
		tileMap.update();
		
		/*
		 * Wenn ich das hier setze springt die gesamte Map hin und her und ich find 
		 * den Fehler einfach nicht .....
		 */
		
		//tileMap.setPosition(GamePanel.WIDTH / 2- player.getx(), GamePanel.HEIGHT / 2 - player.gety());
	}

	@Override
	public void draw(Graphics g){
		
		background.draw(g);
		
		tileMap.draw(g);
		
		player.draw(g);
		
	}

	@Override
	public void keyPressed(int key){
		player.keyPressed(key);
	}

	@Override
	public void keyReleased(int key){
		player.keyReleased(key);
	}
}
```




Kurioser weise gibt es dermaßen viele verschiedene Aussagen im Netz, einmal wird nur die Map bewegt, hier wiederrum doch der Player, dann doch mal beide......

ziemlich strange das Ganze , ich dachte eigentlich ich orientiere mich an dem Ansatz den Turtle auch schon gepostet hatte, und bewege nur die Map um die koordinaten des Players herum , so das es den Anschein hat der Player würde sich bewegen , dabei zeichne ich nur die Map jeweils versetzt, dies ist hier anscheinen auch wieder anders, soweit ich das nun nachvollziehen kann wird erstmal mit temporären koordinaten die Kollision berechnet, und diese dann dem Player zugewiesen, sprich hier andern sich die koordinaten und werden dann auf die Map 'von hand' translatet, vielleicht sieht ja jemand gleich den Fehler beim Zeichenen der Map/des Players......

Fakt ist , zzt. bewegt sich zwar der Player aber die Map nicht ^^, genauso soll es eigentlich nicht sein ......


----------



## kaoZ (29. Jul 2014)

Vielleicht kann mir ja einer mal an diesem extrems simpel gehaltenen Beispiel verklickern wie das mit dem zeichnen der Positionen umzusetzen ist ,

ich habe hier mal so ziemlich auf alles verzichtet, mir geht es nur darum zu verstehen wo was an welcher stelle gezeichnet werden muss wenn die Map größer ist als der Sichtbare Bereich, 

Wäre super wenn mir da mal einer die Richtung weisen würde, ob sich nun map , player , oder beide bewegen müssen und die Position dann von der map auf den player translatet werden muss oder umgekehrt, so müsst ihr euch auch nicht durch den Code im vorherigen Post wühlen  geht mir nur um das Prinzip , sehe den Wald vor lauter Bäumen schon nicht mehr ^^

Mal abgesehen davon gibt es so dermaßen unvollständige und fehlerbehaftete, tutorials im Netz das es mir mittlerweile kalt den Rücken runter läuft..... hoffe mein Buch ist bald da :lol:

[EDIT]Die Kollision lassen wir hier mal außen vor, die habe ich bereits schon mehrmals erfolgreich implementiert, mir geht es strikt um das tilemap scrolling [/EDIT]



Spoiler: KSKB





```
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JPanel;



@SuppressWarnings("serial")
public class Foo extends JPanel implements Runnable, KeyListener{

	
	final static int WIDTH	= 320;
	final static int HEIGHT = 240;
	
	//player
	int x;
	int y;
	int xmove;
	int ymove;
	int width;
	int height;
	
	//map
	int tileSize;
	int xmap;
	int ymap;
	
	int[][] map = 
		{
			{0, 1, 0, 1, 0, 1, 0, 1, 0, 1},
			{1, 0, 1, 0, 1, 0, 1, 0, 1, 0},
			{0, 1, 0, 1, 0, 1, 0, 1, 0, 1},
			{1, 0, 1, 0, 1, 0, 1, 0, 1, 0},
			{0, 1, 0, 1, 0, 1, 0, 1, 0, 1},
			{1, 0, 1, 0, 1, 0, 1, 0, 1, 0},
			{0, 1, 0, 1, 0, 1, 0, 1, 0, 1},
			{1, 0, 1, 0, 1, 0, 1, 0, 1, 0},
			{0, 1, 0, 1, 0, 1, 0, 1, 0, 1},
			{1, 0, 1, 0, 1, 0, 1, 0, 1, 0},
		};
	
	//movement
	boolean left;
	boolean right;
	boolean up;
	boolean down;
	
	//loop
	Thread loop;
	boolean running;
	
	public Foo(){
		
		width = 20;
		height = 20;
		
		tileSize = 32;
		
		setPosition(20, 20);
		setMapPosition(0, 0);
		
		setPreferredSize(new Dimension(WIDTH, HEIGHT));
		setFocusable(true);
		requestFocus();
		
	}
	
	private void setPosition(int x, int y){
		this.x = x;
		this.y = y;
	}
	
	private void setMapPosition(int xmap, int ymap){
		this.xmap = xmap;
		this.ymap = ymap;
	}
	
	@Override
	public void keyTyped(KeyEvent e){}

	@Override
	public void keyPressed(KeyEvent e){
		if(e.getKeyCode() == KeyEvent.VK_LEFT) {
			left = true;
		}
		if(e.getKeyCode() == KeyEvent.VK_RIGHT) {
			right = true;
		}
		if(e.getKeyCode() == KeyEvent.VK_UP) {
			up = true;
		}
		if(e.getKeyCode() == KeyEvent.VK_DOWN) {
			down = true;
		}
	}
	
	@Override
	public void keyReleased(KeyEvent e){
		if(e.getKeyCode() == KeyEvent.VK_LEFT) {
			left = false;
		}
		if(e.getKeyCode() == KeyEvent.VK_RIGHT) {
			right = false;
		}
		if(e.getKeyCode() == KeyEvent.VK_UP) {
			up = false;
		}
		if(e.getKeyCode() == KeyEvent.VK_DOWN) {
			down = false;
		}
	}
	
	@Override
	public void addNotify(){
		super.addNotify();
		
		if(loop == null) {
			loop = new Thread(this);
			addKeyListener(this);
			loop.start();
		}
	}
	
	public void update(){
		if(left) {
			x -= 2;
		}
		else if(right) {
			x += 2;
		}
		
		if(up) {
			y -= 2;
		}
		else if(down) {
			y += 2;
		}

	}
	
	//without double buffering ! direct draw !
	public void draw(){
		
		Graphics2D g = (Graphics2D) getGraphics();
		
		//draw map
		for (int row = 0; row < map.length; row++) {
			for (int col = 0; col < map[row].length; col++) {
				if(map[row][col] == 1) {
					g.setColor(Color.BLACK);
				}
				if(map[row][col] == 0) {
					g.setColor(Color.WHITE);
				}
				g.fillRect(col * tileSize, row * tileSize, tileSize, tileSize);
			}
		}
		
		//draw Player
		g.setColor(Color.RED);
		g.fillRect(x, y, width, height);
		
		//draw info
		g.drawString("Cord. : " + x + "|" + y, x, y + height * 2);
		g.drawString("Pos. : " + y / tileSize + "|" + x / tileSize, x, y + height * 2 + 20);
		
	}


	@Override
	public void run(){
		
		while(true){
			
			update();
			draw();
			
			try {
				Thread.sleep(100);
			}
			catch (InterruptedException ex) {
				ex.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args){
		JFrame f = new JFrame();
		f.setDefaultCloseOperation(2);
		f.setContentPane(new Foo());
		f.pack();
		f.setLocationRelativeTo(null);
		f.setVisible(true);
		
	}
}
```


----------



## Keyone94 (29. Jul 2014)

hab mal ein paar Struktur Tipps für zukünftige Projekte:
1.) Klassen wie Player, MapObject und ähnliches sind im normalfall reine Informations Klassen, dh. sie haben hauptsächlich Variablen mit gettern und settern und fast nie konkrete Methoden (wie Kollisionkontrolle oder ähnliches)

2.) man schreibt statdessen eine Physik (oder eine verwaltung der Objekte oder ähnliches, welche einerseits die Map und andererer Seits alle Objekte die auf der Map laufen besitzt)
2.5.) die Sachen wie keyPressed / keyReleased sollten auch nicht in Player stehen, sondern entweder in der Klasse darüber oder in der Physik oder so

3.) wenn der Spieler als Mittelpunkt des Spiels gesehen wird (wenn man also nur einen Spieler hat, der besonders ist) wird dieser meistens als Sonderwurst behandelt (also es gibt z.B. eine Liste mit mapObjects  und eine Instanz von Spieler)


zu deinem Problem mit der bewegten Map:
du willst die Map abhängig von der Spieler Position zeichen, dh. 
1.) der Spieler wird immer genau in der Mitte des Bildes gezeichnet
2.) die Map wird abhängig von x und y gezeichnet
( 3.) falls du willst kannst du später noch einbauen, dass wenn man sich dem Rand nähert, die Position des Spielers verändert wird anstelle der Map (man also am Rand der Wand entgegen läuft) )



Spoiler: ungetesteter Code





```
//draw Player
        g.setColor(Color.RED);
        g.fillRect( 5*tilesize,  5*tilesize, width, height);

//draw map
        for (int row = 0; row < map.length; row++) {
            for (int col = 0; col < map[row].length; col++) {
                if(map[row][col] == 1) {
                    g.setColor(Color.BLACK);
                }
                if(map[row][col] == 0) {
                    g.setColor(Color.WHITE);
                }
                g.fillRect( col  * tileSize - x,  row  * tileSize - y, tileSize, tileSize);
            }
        }
```




[EDIT]Ich würde die Position der Map nicht als eigene Variable machen, denn:
je mehr Variablen man hat, die aufeinander abgestimmt sein müssen, um so Fehler anfälliger wird das ganze



> Mal abgesehen davon gibt es so dermaßen unvollständige und fehlerbehaftete, tutorials im Netz das es mir mittlerweile kalt den Rücken runter läuft..... hoffe mein Buch ist bald da


also ich hab Programmierung auch mit diesen Tutorials gelernt (nie in ein Buch geschaut und bis auf mein Info Studium inzwischen (wo man leider nichts wirklich lernt)) komplett mit dem Internet gelernt
[/EDIT]


----------



## kaoZ (29. Jul 2014)

> hab mal ein paar Struktur Tipps für zukünftige Projekte:



Deine dort aufgelisteten Punkte sind Plausibel, danach richte ich mich in meinen sonstigen Projekte sowieso, der hier gepostete Code , ist einem Online Tutorial nachempfunden, und nicht auf meinem Mist gewachsen, deswegen unter anderem auch meine Aussage das viele Online Tutorials hinsichtlich der Spieleprogrammierung nicht nur unübersichtlich sind,
sondern auch teilweise recht komplex, und oder sogar Fehlerhaft.

Da ich mich wie schon erwähnt vorher nicht mit Spielprogrammierung befasst habe , würde ich halt ganz gerne eine eigene Engine schrieben um das ganze auch nachvollziehen zu können, wie movement, scrolling, kollisionsabfragen usw. ineinandergreifen.
(ich vermute mal das meinstest du mit Physik)

Sicher könnte ich auch einfach irgendeine fertige Engine nutzen wie Libgdx, Slick2D, und wie sie nicht alle heißen, das ist aber nicht mein Ziel!


mein Problem ist letztendlich auch nicht die Gliederung, der Aufbau oder die Kollisionsabfrage, sondern lediglich das Verständnis dafür wie sich die Positionen von map und Player zueinander verhalten , deshalb hatte ich ein schnell programmiertes Beispiel gepostet, wo alle relevanten dinge in einer Klasse zusammengefasst sind, und ja dies ist nicht OO, sondern dient lediglich dazu , das ich leichter nachvollziehen konnte wie sich die einzelnen koordinaten zueinander verhalten müssen, bzw. an welchen stellen was gezeichnet wird.

Zu deiner Aussage das ein Player sich nicht um das Movement etc. kümmern sollte, gebe ich dir natürlich vollkommen recht, es sollten lediglich setter / getter angeboten werden, wie sich der Spieler / das Objekt dann bewegt sollte dem Objekt selbst dann egal sein .

Das hier würde den Player in die Mitte zeichenen :


```
//draw Player
		g.setColor(Color.RED);
		g.fillRect(WIDTH / 2 - width / 2, HEIGHT / 2 - height / 2, width, height);
```

in verbindung mit der Map:

```
//draw map
		for (int row = 0; row < map.length; row++) {
			for (int col = 0; col < map[row].length; col++) {
				if(map[row][col] == 1) {
					g.setColor(Color.BLACK);
				}
				if(map[row][col] == 0) {
					g.setColor(Color.WHITE);
				}
				g.fillRect(col * tileSize - x, row * tileSize - y, tileSize, tileSize);
			}
		}
```

würde auch das scrollen funktionieren , allerdings müsste ich mich dann noch um das zeichnen kümmern wenn der Player sich nun richtung Rand bewegt, da wird die map momentan nämlich nicht gezeichnet.


Zumal dann auch die koordinaten nichtmehr passen , die ich an dieser Stelle ausgeben lasse, und somit auch die Zeile und spalte an der sich der Player ja auf der map befindet , was wiederrum für die kollisionsabfrage notwendig ist .


```
//draw info
		g.drawString("Cord. : " + x + "|" + y, x, y + height * 2);
		g.drawString("Pos. : " + y / tileSize + "|" + x / tileSize, x, y + height * 2 + 20);
```


jetzt nehmen wir doch aber mal an ich habe eine map mit den ausmaßen 1200x1200
und einen aktiven Sichtbereich von 640x480, also die map ist größer als der Sichtbereich, und ich würde nun den Player und schlussfolgernd dessen die map aktiv über dessen x und y koordinaten bewegen, dann müsste ich ja da der Spieler ab einer x position von > 640 sonst aus dem Sichtbereich laufen würde , so zeichnen das er eben innerhalb des Sichtbaren bereiches bleibt, also muss ich map und player auf den sichtbaren bereich translaten , und ich denke das wird auch in dem Tutorial gemacht, nur das er nicht die translate() methode verwendet sondern anhand von player und map koordinaten diese so zeichnet das der player immer im sichtbaren bereich bleibt, und da haperts noch ziemlich ^^

ich kann den Player also nicht einfach an x und y koordinate zeichnen da diese ja irgendwann nicht mehr im Sichtbereich liegen, zusätzlich muss ich die map so zeichnen das ich die aktuelle player position mit einbeziehe, und da scheitert es nich irgendwie grade.....


----------



## kaoZ (29. Jul 2014)

Ich habe nun mal die Map vergrößert, hier wird der Player fix in die Mitte des Sichtbaren bereiches gezeichnet und die map anhand der x und y koordinaten bewegt,

(Das ist, soweit ich es nachvollziehen kann,*nicht *mein Ziel)
_falls du sowas meinstest_



Spoiler: KSKB





```
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JPanel;



@SuppressWarnings("serial")
public class Foo extends JPanel implements Runnable, KeyListener{

	
	final static int WIDTH	= 320;
	final static int HEIGHT = 240;
	
	//player
	int x;
	int y;
	int xmove;
	int ymove;
	int width;
	int height;
	
	//map
	int tileSize;
	int xmap;
	int ymap;
	
	int[][] map = 
		{
			{0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1},
			{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0},
			{0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1},
			{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0},
			{0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1},
			{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0},
			{0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1},
			{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0},
			{0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1},
			{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0},
			{0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1},
			{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0},
			{0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1},
			{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0},
			{0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1},
			{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0},
			{0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1},
			{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0},
			{0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1},
			{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}
		};
	
	//movement
	boolean left;
	boolean right;
	boolean up;
	boolean down;
	
	//loop
	Thread loop;
	boolean running;
	
	public Foo(){
		
		width = 20;
		height = 20;
		
		tileSize = 32;
		
		setPosition(20, 20);
		setMapPosition(0, 0);
		
		setPreferredSize(new Dimension(WIDTH, HEIGHT));
		setFocusable(true);
		requestFocus();
		
	}
	
	private void setPosition(int x, int y){
		this.x = x;
		this.y = y;
	}
	
	private void setMapPosition(int xmap, int ymap){
		this.xmap = xmap;
		this.ymap = ymap;
	}
	
	@Override
	public void keyTyped(KeyEvent e){}

	@Override
	public void keyPressed(KeyEvent e){
		if(e.getKeyCode() == KeyEvent.VK_LEFT) {
			left = true;
		}
		if(e.getKeyCode() == KeyEvent.VK_RIGHT) {
			right = true;
		}
		if(e.getKeyCode() == KeyEvent.VK_UP) {
			up = true;
		}
		if(e.getKeyCode() == KeyEvent.VK_DOWN) {
			down = true;
		}
	}
	
	@Override
	public void keyReleased(KeyEvent e){
		if(e.getKeyCode() == KeyEvent.VK_LEFT) {
			left = false;
		}
		if(e.getKeyCode() == KeyEvent.VK_RIGHT) {
			right = false;
		}
		if(e.getKeyCode() == KeyEvent.VK_UP) {
			up = false;
		}
		if(e.getKeyCode() == KeyEvent.VK_DOWN) {
			down = false;
		}
	}
	
	@Override
	public void addNotify(){
		super.addNotify();
		
		if(loop == null) {
			loop = new Thread(this);
			addKeyListener(this);
			loop.start();
		}
	}
	
	public void update(){
		if(left) {
			x -= 4;
		}
		else if(right) {
			x += 4;
		}
		
		if(up) {
			y -= 4;
		}
		else if(down) {
			y += 4;
		}

	}
	
	//without double buffering ! direct draw !
	public void draw(){
		
		Graphics2D g = (Graphics2D) getGraphics();
		
		//draw map
		for (int row = 0; row < map.length; row++) {
			for (int col = 0; col < map[row].length; col++) {
				if(map[row][col] == 1) {
					g.setColor(Color.BLACK);
				}
				if(map[row][col] == 0) {
					g.setColor(Color.WHITE);
				}
				g.fillRect(col * tileSize - x, row * tileSize - y, tileSize, tileSize);
			}
		}
		
		//draw Player
		g.setColor(Color.RED);
		g.fillRect(WIDTH / 2 - width / 2, HEIGHT / 2 - height / 2, width, height);
		
		//draw info
		g.drawString("Cord. : " + x + "|" + y, WIDTH - 100, 20);
		g.drawString("Pos. : " + y / tileSize + "|" + x / tileSize, WIDTH - 100, 40);
		
	}


	@Override
	public void run(){
		
		while(true){
			
			update();
			draw();
			
			try {
				Thread.sleep(100);
			}
			catch (InterruptedException ex) {
				ex.printStackTrace();
			}
		}
	}
	
	public static void main(String[] args){
		JFrame f = new JFrame();
		f.setDefaultCloseOperation(2);
		f.setContentPane(new Foo());
		f.pack();
		f.setLocationRelativeTo(null);
		f.setVisible(true);
		
	}
}
```


----------



## Androbin (29. Jul 2014)

kaoZ hat gesagt.:
			
		

> Ich habe nun mal die Map vergrößert, hier wird der Player fix in die Mitte des Sichtbaren bereiches gezeichnet und die map anhand der x und y koordinaten bewegt,
> 
> (Das ist, soweit ich es nachvollziehen kann,*nicht *mein Ziel)
> _falls du sowas meinstest_
> ...



Du willst das Scrolling wohl so implementieren, dass nur gescrollt wird, wenn der Spieler dem Rand des Sichtfeldes auf eine bestimmte Distanz nahekommt, oder?

In meinem Falle sieht das ganze so aus:



Spoiler





```
public void scroll() {

if ( spieler.x + world.getSX() < 0.2 * world.getWidth() )
world.setSX( (int) ( 0.2 * world.getWidth() + 0.5 ) - spieler.x );
		
else if ( spieler.x + spieler.width + world.getSX() > 0.8 * world.getWidth() )
world.setSX( (int) ( 0.8 * world.getWidth() + 0.5 ) - ( spieler.x + spieler.width - 1 ) );
		
if ( spieler.y + world.getSY() < 0.2 * world.getHeight() )
world.setSY( (int) ( 0.2 * world.getHeight() + 0.5 ) - spieler.y );
		
else if ( spieler.y + spieler.height + world.getSY() > 0.8 * world.getHeight() )
world.setSY( (int) ( 0.8 * world.getHeight() + 0.5 ) - ( spieler.y + spieler.height - 1 ) );

}
```




Schreib' es dir deinen Bedürfnissen entsprechend um und bau es ein!



			
				Keyone94 hat gesagt.:
			
		

> falls du willst kannst du später noch einbauen, dass wenn man sich dem Rand nähert, die Position des Spielers verändert wird anstelle der Map (man also am Rand der Wand entgegen läuft



Also ich verändere beides, die Position des Spielers und die Scrolling-Variablen "sX" und "sY"!


----------



## lord239123 (30. Jul 2014)

So wie das auf dem Bild aussieht, hast du deine Welt rasterartig erstellt.
Das kannst du auch gleich ausnutzen und zum Beispiel sagen, dass der Spieler sich beim drücken auf die entsprechende Taste entsprechend viele Felder bewegt.
Bevor der Spieler sich allerdings bewegt, muss das Programm testen, ob das entsprechende Feld belegt ist(schwarzes Feld, repräsentiert durch 1) oder ob der Spieler sich auf das Feld bewegen kann(weißes Feld, repräsentiert durch 0).
Die Felder speicherst du intern in einem 2 dimensionalem Array, in das du immer nur reinschaust.

Bei größeren Maps ergibt sich dabei auch der Vorteil, dass du nicht immer die gesamte Map geladen haben musst, um dieses System anzuwenden.
Das Rastersystem ist für den Vorhaben das meiner Meinung nach beste System, da du nicht jeden einzelnen Punkt prüfen musst, sondern immer nur den linken oberen Punkt deines Spielers!


----------



## kaoZ (2. Aug 2014)

Selbstverständlich könnte man das Movement auch so implementieren das man immer nur ein Tile weitergehen kann, dies ist aber auch nicht mein Ziel , 

Das Tilemap scrolling funktioniert mittlerweile auch soweit, allerdings passt die Position noch nicht ganz ^^

in der Klasse LevelOne welche alle relevanten Informationen über player map und co enthält ,

wird beim Initialisieren die map auf die Position x = 0, y = 0 gezeichnet,

der Player wird an position x = 100, y = 100 gezeichnet,

die map wird in deren draw methode so gezeichnet :


```
g.drawImage(tiles[r][c].getImage(),(int)x + col * tileSize,(int)y + row * tileSize, null);
```

der Player wird folgendermaßen gezeichnet :


```
g.fillRect((int)(x + mapX - width / 2), (int)(y + mapY - height / 2), width, height);
```

mapX und mapY sind die aktuellen koordinaten der map.

Wenn ich nun in der update methode der Klasse LevelOne folgendes schreibe


```
tileMap.setPosition(GamePanel.WIDTH / 2 - player.getX(), GamePanel.HEIGHT / 2 - player.getY());
```

funktionieren scrolling der map und des players , allerdings wird es dann logischerweise so dargestellt :





setze ich die Position so nicht , passt zwar die map position wirder , aber scrollt logischerweise nicht mit , das movement des players funktioniert natürlich weiterhin, sieht dann so aus 





*unabhängig von der Kollisionsabfrage* muss das scrolling von map und player ja trotzdem funktionieren, und ich muss ja die map um eben diese koordinaten welcher der player aktuell hat in die entgegen gesetzte Richtung verschieben, allerdings find ich den Fehler / Denkfehler grade nicht, vielleicht kann mir ja nochmal jemand auf die Sprünge helfen  

Hier nochmal die kompletten klassen :



Spoiler: Tielmap.java





```
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import javax.imageio.ImageIO;
import eflow.game.GamePanel;
import eflow.game.drawables.Drawable;


public class TileMap implements Drawable{
	
	//position
	private double x;
	private double y;
	
	//attributes
	private int tileSize;
	private int width;
	private int height;
	private int map[][];
	private int numRows;
	private int numCols;
	private int xOffset;
	private int yOffset;
	
	//tileset
	private BufferedImage tileSet;
	private Tile[][] tiles;
	private int numTilesAcross;
	
	
	public TileMap(int tileSize){
		this.tileSize = tileSize;
	}
	
	public void loadMap(String path){
		
		InputStreamReader in;
		BufferedReader reader;
		
		try {
			in = new InputStreamReader(getClass().getResourceAsStream(path));
			reader = new BufferedReader(in);
			
			numRows = Integer.parseInt(reader.readLine());
			numCols = Integer.parseInt(reader.readLine());
			
			map = new int[numRows][numCols];
			
			width = numCols * tileSize;
			height = numRows * tileSize;
			
			xOffset = GamePanel.WIDTH / tileSize;
			yOffset = GamePanel.HEIGHT / tileSize;
			
			String delimiter = "\\s+";
			
			for (int row = 0; row < numRows; row++) {
				String line = reader.readLine();
				String[] tokens = line.split(delimiter);
				for (int col = 0; col < numCols; col++) {
					map[row][col] = Integer.parseInt(tokens[col]);
				}
			}
		}
		catch (Exception ex) {
			ex.printStackTrace();
		}
	}
	
	public void loadTileSet(String path){
		
		try {
			tileSet = ImageIO.read(getClass().getResourceAsStream(path));
			numTilesAcross = tileSet.getWidth() / tileSize;
			
			tiles = new Tile[2][numTilesAcross];
			
			BufferedImage subImage;
			
			for (int col = 0; col < numTilesAcross; col++) {
				subImage = tileSet.getSubimage(col * tileSize, 0, tileSize, tileSize);
				tiles[0][col] = new Tile(subImage, Tile.NORMAL);
				
				subImage = tileSet.getSubimage(col * tileSize, tileSize, tileSize, tileSize);
				tiles[1][col] = new Tile(subImage, Tile.BLOCKED);
			}
		}
		catch (Exception ex) {
			ex.printStackTrace();
		}
	}

	@Override
	public void update(){
		
	}

	//drawing whole map 
	@Override
	public void draw(Graphics g){
		
		for (int row = 0; row < numRows - 1; row++) {
			for (int col = 0; col < numCols - 1; col++) {
				
				int rc = map[row][col];
				int r = rc / numTilesAcross;
				int c = rc % numTilesAcross;
				
				g.drawImage(tiles[r][c].getImage(),(int)x + col * tileSize,(int)y + row * tileSize, null);
			}
		}
	}
	
	public int getColTile(double x){
		return (int)x / tileSize;
	}
	
	public int getRowTile(double y){
		return (int)y / tileSize;
	}
	
	public int getTileType(int row, int col){
		int rc = map[row][col];
		int r = rc / numTilesAcross;
		int c = rc % numTilesAcross;
		
		return tiles[r][c].getType();
	}
	
	public void setPosition(double x, double y){
		this.x += (x - this.x);
		this.y += (y - this.y);
	}

	public int getX()				{return (int)x;}
	public int getY()				{return (int)y;}
	public int getTileSize()		{return tileSize;}
	public int getWidth()			{return width;}
	public int getHeight()			{return height;}
	public int getXOffset()			{return xOffset;}
	public int getYOffset()			{return yOffset;}
	
}
```






Spoiler: Player.java





```
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import eflow.game.sprites.TileMap;


public class Player extends MapObject{
	
	public Player(TileMap map){
		super(map);
		
		width	 = 20;
		height	 = 20;
		
		moveSpeed	 = 0.3;
		maxSpeed	 = 4.2;
		fallSpeed	 = 0.15;
		maxFallSpeed = 11.9;
		stopSpeed 	 = 3.0;
		jumpStart 	 = -4.8;
	}
	
	private void getNextPosition(){
		
		//left + right movement
		if(right) {
			dx += moveSpeed;
			if(dx < maxSpeed) {
				dx = maxSpeed;
			}
		}
		else if(left) {
			dx -= moveSpeed;
			if(dx > -maxSpeed) {
				dx = - maxSpeed;
			}
		}
		else {
			if(dx > 0) {
				dx -= stopSpeed;
				if(dx < 0) {
					dx = 0;
				}
			}
			if(dx < 0) {
				dx += stopSpeed;
				if(dx > 0) {
					dx = 0;
				}
			}
		}
		
		//jumping
		if(jumping) {
			
		}
		
		if(falling) {
			
		}
	}

	@Override
	public void update(){
		
		getNextPosition();
		checkTileMapCollision();
		setPosition(tempx,tempy);
	}
	
	@Override
	public void draw(Graphics g){
		
		setMapPosition();
		
		g.setColor(Color.RED);
		g.fillRect((int)(x + mapX - width / 2), (int)(y + mapY - height / 2), width, height);
	}

	@Override
	public void keyPressed(int key){
		if(key == KeyEvent.VK_W) {
			
		}
		if(key == KeyEvent.VK_A) {
			setLeft(true);
		}
		if(key == KeyEvent.VK_S) {
			
		}
		if(key == KeyEvent.VK_D) {
			setRight(true);
		}
		if(key == KeyEvent.VK_SPACE) {
			setJumping(true);
		}
	}

	@Override
	public void keyReleased(int key){
		if(key == KeyEvent.VK_W) {
			
		}
		if(key == KeyEvent.VK_A) {
			setLeft(false);
		}
		if(key == KeyEvent.VK_S) {
			
		}
		if(key == KeyEvent.VK_D) {
			setRight(false);
		}
		if(key == KeyEvent.VK_SPACE) {
			setJumping(false);
		}
	}
}
```






Spoiler: MapObject.java





```
import java.awt.Graphics;
import java.awt.Rectangle;
import eflow.game.control.Moveable;
import eflow.game.drawables.Drawable;
import eflow.game.sprites.TileMap;

public abstract class MapObject implements Drawable, Moveable{

	//dimensions
	protected int width;
	protected int height;
	
	//position
	protected double x;
	protected double y;
	
	//collision
	protected double tempx;
	protected double tempy;
	
	//vector
	protected double dx;
	protected double dy;
	
	//map
	protected TileMap tileMap;
	protected int currCol;
	protected int currRow;
	protected int tileSize;
	protected double mapX;
	protected double mapY;

	//movement
	protected boolean left;
	protected boolean right;
	protected boolean up;
	protected boolean down;
	protected boolean jumping;
	protected boolean falling;
	
	//movement attributes
	protected double moveSpeed;
	protected double maxSpeed;
	protected double stopSpeed;
	protected double fallSpeed;
	protected double maxFallSpeed;

	protected double jumpStart;
	
	public MapObject(TileMap map){
		tileMap = map;
		tileSize = map.getTileSize();
	}

	@Override
	public void update(){
		x += dx;
		y += dy;
	}

	@Override
	public void draw(Graphics g){}	
	
	public void setPosition(double x, double y){
		this.x = x;
		this.y = y;
	}
	
	public void setVector(double dx, double dy){
		this.dx = dx;
		this.dy = dy;
	}
	
	public void setMapPosition(){
		mapX = tileMap.getX();
		mapY = tileMap.getY();
	}
	
	public Rectangle getBounds(){
		return new Rectangle((int)x, (int)y, width, height);
	}
	
	public boolean intersects(MapObject o){
		Rectangle r1 = getBounds();
		Rectangle r2 = o.getBounds();
		
		return r1.intersects(r2);
	}
	
	public void checkTileMapCollision(){
		
		//without World collision! 
		
		tempx = x;
		tempy = y;
		
		tempx += dx;
		tempy += dy;
	}
	
	public void setLeft(boolean b)			{left = b;}
	public void setRight(boolean b)			{right = b;}
	public void setJumping(boolean b){
		if(!falling) {
			jumping = b;
		}
	}
	
	public int getX()						{return (int)x;}
	public int getY()						{return (int)y;}
}
```


----------



## Androbin (4. Aug 2014)

kaoZ hat gesagt.:
			
		

> der Player wird folgendermaßen gezeichnet :
> 
> ```
> g.fillRect((int)(x + mapX - width / 2), (int)(y + mapY - height / 2), width, height);
> ```



Sollte das nicht so heißen?:

```
g.fillRect( (int) ( x + mapX ), (int) ( y + mapY ), width, height );
```
WIESO IN JAVA'S NAMEN ZIEHST DU "WIDTH / 2"/"HEIGHT / 2" AB ???


----------



## kaoZ (4. Aug 2014)

Weil der Player mittig, an die Koordinaten gezeichnet wird,  das ist auch nicht das Problem 
Und macht  auch nicht wirklich einen Unterschied, ich ziehe ja nur die Größe und Höhe des Players /2 ab, sprich wäre es ein fadenkreuz säße er mittig drauf und nicht mit der oberen linken Ecke


----------



## Androbin (4. Aug 2014)

Ja, aber damit versaust du dir deine ganze Kollision:
Die X/Y-Koordinaten beginnen schließlich an der linken oberen ECKE
und nicht etwa im MITTELPUNKT, sprich:
Du müsstest die deine komplette Kollision umstellen,
oder WILLST du etwa all das NOCHMAL von Hand implementieren?
Fazit: HÖR ENDLICH AUF, AM KOORDINATEN-SYSTEM HERUMZUSCHRAUBEN


----------



## kaoZ (4. Aug 2014)

Mag schon sein, und ja ich wollte die kollisionsabfragen eh nochmal komplett neu implementieren ( rein zu lern und übungszwecken), wenn ich so überall vorgehe , macht es doch aber keinen unterschied.

Hilft mir aber trotzdem immer noch nicht bei meinem tilemap scolling problem ^^, bzw dessen Platzierung / bzw. an welcher stelle sie gezeichnet wird, und wieso.


----------



## lord239123 (4. Aug 2014)

Eine Möglichkeit für dein Scrollingsystem wäre folgende:
Erstelle am besten ein Panel, welches die Größe der fertigen Map hat.
Wenn sich jetzt der Spieler bewegen soll, bewegst du das Panel in die eine Richtung und den Spieler genauso weit in die andere Richtung.
Um das ganze performanter zu gestalten, kannst du festlegen, dass immer nur die Felder um den Spieler herum gezeichnet werden sollen.
Da deine Map vermutlich nicht allzu groß sein wird, kannst du ruhig alle Felder zeichnen.

Bei meinem Pokemon Spiel, welches ebenfalls eine Rasterartige Welt besitzt, funktioniert das auf diese Weise zumindest.

Ich habe mir deinen Quellcode nicht komplet tdurchgelesen, weshalb es evtl ein bisschen Anpasungsbedarf gibt.


----------



## kaoZ (4. Aug 2014)

Das mit dem Panel in die eine und dem player in die andere richtung hatte ich bereits versucht, allerdings war es so das das irgendwann der player nach rechts aus dem bild lieft, vermutlich weil der movespeed nicht konstant geblieben ist sondern bis zu einem maximum inkrementiert wird, dies müsste ich ja dann ja ebenso auf die map umsetzen , oder sehe ich das falsch ?

sprich der player bewegt sich mit einer gewissen Anzahl an pixeln ( movespeed) nach rechts und die map dann invertiert, also quasi nach links mit eben dieser Geschwindigkeit bzw. anzahl an pixeln. ( bzw. wird die map dann an / ab den dementsprechenden koordinaten gezeichnet .


----------



## Androbin (5. Aug 2014)

1. ist die Idee mit dem Panel eine furchtbare Idee und

2. zurück zur Kollision (width/2, height/2):


			
				kaoZ hat gesagt.:
			
		

> wenn ich so überall vorgehe, macht es doch aber keinen unterschied.


Theoretisch ja, aber bedenke folgendes:

2.1 Du machst es dir unnötig kompliziert, denn
2.2 Frägst du die Felder, auf denen der Spieler "steht" ja so ab:
Code: 
	
	
	
	





```
X/Y-Koordinate / Feld-Größe
```
, was dir normalerweise angibt,
auf welchem Feld sich die linke obere Ecke des Spielers finden lässt,
dementsprechend 
	
	
	
	





```
( X/Y-Koordinate + Breite/Höhe ) / Feld-Größe
```
 für rechts unten

Das funktioniert aber [STRIKE]leider[/STRIKE] nicht, so wie du es machst


----------



## kaoZ (7. Aug 2014)

Ich werde es am wochenende einfach nochmal sauber und ohne schnickschnack neu implementieren und dann schau ich mal weiter


----------

