Kollisionsabfrage implementieren

kaoZ

Top Contributor
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

Code:
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.

Java:
	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

Java:
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

Java:
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:
 
Zuletzt bearbeitet:

Gucky

Top Contributor
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

Top Contributor
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

Java:
public Player(TileMap map){...}

lieber

Java:
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.
 
Zuletzt bearbeitet:

Gucky

Top Contributor
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. :D
 

Androbin

Bekanntes Mitglied
Leute, kommt mal wieder runter,
für die Kollision braucht man doch nicht mal ein Rectangle!
Man muss lediglich folgende Abfrage anpassen und implementieren:
Java:
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

Top Contributor
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 :

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

Bekanntes Mitglied
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:
Java:
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

Top Contributor
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. :D
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

Bekanntes Mitglied
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. :D
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.

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?
 
Zuletzt bearbeitet:

Gucky

Top Contributor
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

Bekanntes Mitglied
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!
 

kaoZ

Top Contributor
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:

Java:
	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 ?
 
Zuletzt bearbeitet:

Androbin

Bekanntes Mitglied
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?
 
Zuletzt bearbeitet:

kaoZ

Top Contributor
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
Code:
erwartete position = aktuelle Position + die bewegungsgeschwindigkeit
rechnen, gehe ich nun entgegengesetzt ist es doch genau umgekehrt,
Code:
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:

Java:
	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.
 
Zuletzt bearbeitet:

Androbin

Bekanntes Mitglied
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

Top Contributor
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 ?
 
Zuletzt bearbeitet:

Gucky

Top Contributor
Die Map und der Player arbeiten beide mit denselben Koordinaten. Sonst funktioniert das nicht. Außer du translatest (
Code:
g.translate(int,int)
) aber das würde ich dir nicht raten.
 

kaoZ

Top Contributor
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

Top Contributor
Wie zeichnest du denn die Map?
Bekommt ein Tile die Koordinaten
Code:
x = BREITE_TILE * STELLE
und
Code:
y = HÖHE_TILE * STELLE
oder bekommt ein Tile die Koordinaten
Code:
x = 5
und
Code:
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

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

Java:
	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.

Java:
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 ?
 
Zuletzt bearbeitet:

Androbin

Bekanntes Mitglied
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:
Java:
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

Top Contributor
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,
Code:
hier sind die x und y koordinaten noch nicht genutzt, getter und setter sind dementsprechend noch nicht implementiert
:


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]
 
Zuletzt bearbeitet:

Androbin

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

kaoZ

Top Contributor
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......
 
Zuletzt bearbeitet:

kaoZ

Top Contributor
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]
 
Zuletzt bearbeitet:

Androbin

Bekanntes Mitglied
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 ???
Java:
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

Top Contributor
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:

Code:
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:

Code:
$(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

Top Contributor
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

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 :

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:

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 !:

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.
 
Zuletzt bearbeitet:

Tobse

Top Contributor
Wieso nicht gleich so?
[...]
PS. Das
Code:
setPosition(0, 0);
kannst du dir sparen!


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

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?
 
Zuletzt bearbeitet:

kaoZ

Top Contributor
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
Code:
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.
 
Zuletzt bearbeitet:

Androbin

Bekanntes Mitglied
:mad:: 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!

:mad:: kaoZ 8
kaoZ hat gesagt.:
Androbin hat gesagt.:
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.
Du verstehst es immer noch nicht!
[TIPP] Integer werden automatisch mit 0 initialisiert! [/TIPP]
 

kaoZ

Top Contributor
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.
 
Zuletzt bearbeitet:

kaoZ

Top Contributor
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

Top Contributor
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 :

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 :

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 :

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

Top Contributor
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

Java:
	@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 :

 
Zuletzt bearbeitet:

Androbin

Bekanntes Mitglied
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

Top Contributor
So sieht die klasse TileMap aus :

Java:
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 :

Java:
	@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 :

Java:
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 :)
 
Zuletzt bearbeitet:

turtle

Top Contributor
Ich gebe zu dieser Diskussion nur "sporadisch" zu folgen:oops:

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]
 

kaoZ

Top Contributor
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
 
Zuletzt bearbeitet:

Androbin

Bekanntes Mitglied
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.
 
Zuletzt bearbeitet:

kaoZ

Top Contributor
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 :

Java:
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,

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;}
	
}

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

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

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 ......
 
Zuletzt bearbeitet:

kaoZ

Top Contributor
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]

Java:
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);
		
	}
}
 
Zuletzt bearbeitet:
Ähnliche Java Themen
  Titel Forum Antworten Datum
A DoodleJump programmieren: Kollisionsabfrage Spiele- und Multimedia-Programmierung 6
T Problem bei Kollisionsabfrage Spiele- und Multimedia-Programmierung 4
S Polygon Kollisionsabfrage Spiele- und Multimedia-Programmierung 2
RalleYTN Erweiterte Kollisionsabfrage Spiele- und Multimedia-Programmierung 7
S Kollisionsabfrage zwischen Rechteck und Polygon Spiele- und Multimedia-Programmierung 1
J Java Kollisionsabfrage Spiele- und Multimedia-Programmierung 21
T Problem mit Kollisionsabfrage der NPC Spiele- und Multimedia-Programmierung 1
F Kollisionsabfrage bei schnellen Objekten Spiele- und Multimedia-Programmierung 2
J Problem bei pixelgenauer Kollisionsabfrage Spiele- und Multimedia-Programmierung 10
M Kollisionsabfrage Spiele- und Multimedia-Programmierung 7
N Quake - Kollisionsabfrage Spiele- und Multimedia-Programmierung 21
N Problem mit Kollisionsabfrage beim Fallen Jump & Run Spiele- und Multimedia-Programmierung 5
R Kollisionsabfrage haut nicht hin Spiele- und Multimedia-Programmierung 15
Gossi Quaxlis 2D Tutorial....Probleme nach hinzufügen der Kollisionsabfrage Spiele- und Multimedia-Programmierung 16
U Jump n' Run 2D Geometrie und Kollisionsabfrage? Spiele- und Multimedia-Programmierung 11
baddestpoet Problem mit Kollisionsabfrage Spiele- und Multimedia-Programmierung 18
D Kollisionsabfrage von 2 Autos Spiele- und Multimedia-Programmierung 2
G Kollisionsabfrage (Mario klon) Spiele- und Multimedia-Programmierung 6
gieser Buggy Kollisionsabfrage Spiele- und Multimedia-Programmierung 4
masta // thomas Kollisionsabfrage - inspiriert durch "pixelgenaue Kolli Spiele- und Multimedia-Programmierung 13
gieser pixelgenaue Kollisionsabfrage der Kreise Spiele- und Multimedia-Programmierung 9
T Kollisionsabfrage von einem Stein mit einem Ball Spiele- und Multimedia-Programmierung 5
N Kollisionsabfrage Spiele- und Multimedia-Programmierung 6
D Jump and Run Game -- Kollisionsabfrage Spiele- und Multimedia-Programmierung 30
J Kollisionsabfrage Ball <-> Paddle Spiele- und Multimedia-Programmierung 2
L Hörtest programmieren und implementieren Spiele- und Multimedia-Programmierung 2
Arif Maus-Objekt im Player Klasse implementieren !? Spiele- und Multimedia-Programmierung 2

Ähnliche Java Themen

Neue Themen


Oben