Scrollbare Map?

mavinatic

Bekanntes Mitglied
Hallo Community,

ich habe ein 2D-Array, welches meine Map darstellt. Dieses Array ist 128x128 Elemente groß. Nun habe ich das Problem mit einem Scrollbaren Bildschirm :-( und habe auch null peil wie ich das entwickeln kann....das der Hintergrund (Map) scrollbar ist. Im Internet habe ich eine Anleitung gefunden, jedoch versteh ich nicht so wirklich wie das funktionieren soll.

Link

Wie funktioniert das???
 

c_sidi90

Top Contributor
Das du im Internet nichts gefunden hast, kann ich mir nicht vorstellen, es gibt zahlreiche Tutorials in mehreren Programmiersprachen zu diesem Thema. Selbst hier im Forum wurde das schon oft diskutiert.

Um dir dein Vorhaben zu erleichtern, solltest du evtl auf eine Gamelib wie z.B. Slick2D zurückgreifen, diese hat eine sehr gute Tilemap bearbeitung.

Hier kannst du dir ein Beispiel loaden:

Slick - 2D Game Library based on LWJGL - Webstart Demos
 

Landei

Top Contributor
Es gibt zwei prinzipiell unterschiedliche Implementierungsvarianten: Ohne Animation (die Map "springt" um ein ganzes Feld beim Scrollen) oder mit Animation (die Map "gleitet" von einer Position in die nächste). Letzteres ist natürlich deutlich aufwendiger, und Slick2D kann da schon mächtig helfen.
 

Fu3L

Top Contributor

mavinatic

Bekanntes Mitglied
Das Problem ist die Lösung zu verstehen...da haperts...mathematisch zu verstehen wieso und dafinde ich selten kommentierten quelltext...
 

Fu3L

Top Contributor
attachment.php


Dieses blaue Tile liegt überm Rand
Code:
posX = xOffset + WIDTH
(Koordinate des Randes).
Wir möchten wissen, wo sein Beginn liegen muss.
Teilen wir die posX durch die Breite eines Tiles, wissen wir, wie viele Tiles vom Urpsrung aus gelegt werden müssen, um unser blaues Tile zu erreichen (gut, wenn man wissen will, um welches Tile es sich handelt). Nehmen wir nun den Rest der Division, wissen wir, wie viele Pixel vom Tile Beginn bis zum Rand (posX) fehlen:

Code:
beginnX = posX - (posX / tileWidth)

Für die nächsten Tiles müssen wir dann immer eine Tilebreite abziehen, bis beginnX einmal unterhalb von xOffset gelegen hat.

Für ein komplettes Beispiel siehe meinen oben verlinkten Post. (In der Berechnung von xOffset ist da allerdings ein mini-Fehler. Es müsste die Breite des Spielersprites genommen werden)
 

Anhänge

  • tile.png
    tile.png
    4,1 KB · Aufrufe: 216
Zuletzt bearbeitet:

4596

Mitglied
Willst du in dein Spiel eine Minimap hinzufügen, oder die "normale" Karte anzeigen, ohne, dass der Spieler über den Rand läuft und "verschwindet"? :bahnhof:

Für letzteres kenne ich eine Lösung:

Zeige in der Methode paint(Graphics) den Spieler immer an einer bestimmten Position an, und berechne die relative Position der anderen Objekte auf der Karte nach der Position des Spielers, hier die Formel für die X-Achse:
X(objekt)-X(spieler)+X(spielerposition-auf-dem-Bildschirm)
Die position auf der Y-Achse wird genauso berechnet, nur statt X einfach Y.
Bei entsprechender Einstellung im Code, ist der Spieler immer in der Mitte des Bildschirms, und alle Objekte in der Welt sind entsprechend zu ihm angeordnet.

(bei Fragen zur Methode paint(Graphics) hilft die offizielle Java-API von Oracle)

Hat das dein Problem gelöst?
 

mavinatic

Bekanntes Mitglied
Hallo Community,

ich bin momentan dabei, weiter das Scrolling zu implementieren. Ich bin leider zu doof das irgendwie zu verstehen.

Mein Problem bei dem aktuellen scrolling ist, dass die figur sich schneller bewegt als die karte im Hintergrund. Meine Grund Idee war, dass die Spielerposition immer absolut in der Mitte ist.

Hier meine grundlegende Gedanken:

- Aktuelle Spielerposition, daraus aktuelle Tile-Position errechnen

- Alle benötigten Tiles errechnen (50x50 Map => Bildschirmauflösung: 800x600 daher werden nur 32x24Tiles benötigt, bei einer Größe von 25px * 25px)

- Map zeichnen, aber irgendwie funktioniert das nicht so wie gewollt(Map scrollt zulangsam, Figur nicht im Mittelpunkt des Screens

Bisschen Code hier:
Java:
public class Screen implements Renderable 
{
	private Image backBuffer = null;
	private int screenWidth = 0;
	private int screenHeight = 0;
	private Game game = null;
	private Level currentLevel;
	
	public Screen(int windowWidth, int windowHeight, Game game) 
	{
		this.game = game;
		this.screenWidth=windowWidth;
		this.screenHeight=windowHeight;
	}
	
	public void setLevel(Level currentLevel)
	{
		this.currentLevel=currentLevel;
	}

	private int tx = 0;
	private int ty = 0;
	private int sx = 0;
	private int sy = 0;
	private int gamerEntityPosX = 0;
	private int gamerEntityPosY = 0;
	private int tl = 0;
	private int tf = 0;
	private int tb = 0;
	private int tr = 0;
	private int arrPosMinX = 0;
	private int arrPosMaxX = 0;
	private int arrPosMinY = 0;
	private int arrPosMaxY = 0;
	
	public void renderSpecificPart()
	{
		if(backBuffer == null)
			backBuffer = game.createImage(screenWidth, screenHeight);
		
		Graphics newGraphics = backBuffer.getGraphics();
		if(currentLevel != null)
		{
			GamerEntity gamerEntity = null;
			Entity[] entities = currentLevel.getEntities();
			for(Entity entity:entities)
			{
				if(entity instanceof GamerEntity)
				{
					gamerEntity = (GamerEntity) entity;
				}
			}
			if(gamerEntity != null)
			{
				gamerEntityPosX = gamerEntity.getPositionX();
				tx = gamerEntityPosX / Tile.SIZE;
				sx = screenWidth / Tile.SIZE;
				
				tl = sx/2;
				tr = sx/2;
				
				arrPosMinX = (tx - tl)+1;
				arrPosMaxX = (tx + tr)+1;
				
				gamerEntityPosY = gamerEntity.getPositionY();
				ty = gamerEntityPosY / Tile.SIZE;
				sy = screenHeight / Tile.SIZE;
				
				tf = sy / 2;
				tb = sy / 2;
				
				arrPosMinY = (ty - tf)+1;
				arrPosMaxY = (ty + tb)+1;
				
				if(arrPosMinY<0)
					arrPosMinY=0;
				if(arrPosMinX<0)
					arrPosMinX=0;
				if(arrPosMaxY>currentLevel.getMap()[0].length)
					arrPosMaxY=currentLevel.getMap()[0].length;
				if(arrPosMaxX>currentLevel.getMap().length)
					arrPosMaxX=currentLevel.getMap().length;
				
				Tile[][] map = currentLevel.getMap();
				
//				map[arrPosMinX][arrPosMinY];
//				map[arrPosMaxX][arrPosMaxY];
				
				Tile[][] customizedMap = new Tile[arrPosMaxX-arrPosMinX][arrPosMaxY-arrPosMinY];
				System.out.println("XLength="+(arrPosMaxX-arrPosMinX)+"YLength="+(arrPosMaxY-arrPosMinY));
				int x = 0;
				int y = 0;
				for(int i=arrPosMinX; i<arrPosMaxX; i++)
				{
					for(int j=arrPosMinY; j<arrPosMaxY; j++)
					{
						customizedMap[x][y] = map[i][j];
						y++;
					}
					y=0;
					x++;
				}
				
				for(int ci = 0;ci<customizedMap.length; ci++)
				{
					for(int cj = 0;cj<customizedMap[ci].length; cj++)
					{
						newGraphics.drawImage(customizedMap[ci][cj].getSpriteImage(), ci*Tile.SIZE, cj*Tile.SIZE, null);
						if(currentLevel.isRaster())
						{
							newGraphics.setColor(Color.black);
							newGraphics.drawRect(ci*Tile.SIZE, cj*Tile.SIZE, Tile.SIZE, Tile.SIZE);
						}
					}
				}
			}						
		}
		//Render entities
				if(currentLevel!=null)
				{
					Entity[] entities = currentLevel.getEntities();
					if(entities!=null)
					{
						for(Entity entity:entities)
						{
							entity.render(newGraphics);
						}
					}			
				}
	}

	//Parameter graphics in this case not used but for all other entities!
	@Override
	public void render(Graphics g) 
	{
		if(backBuffer == null)
			backBuffer = game.createImage(screenWidth, screenHeight);
		
		Graphics newGraphics = backBuffer.getGraphics();
		
		
		//RenderMap
		if(currentLevel != null)
		{
			Tile[][] map = currentLevel.getMap();
			for(int i=0;i<map.length;i++)
			{
				for(int j=0;j<map[i].length;j++)
				{
					Tile tile = map[i][j];
					newGraphics.drawImage(tile.getSpriteImage(), i*Tile.SIZE, j*Tile.SIZE, null);

					if(currentLevel.isRaster())
					{
						newGraphics.setColor(Color.black);
						newGraphics.drawRect(i*Tile.SIZE, j*Tile.SIZE, Tile.SIZE, Tile.SIZE);
					}
				}
			}
		}		
		
		//Render entities
		if(currentLevel!=null)
		{
			Entity[] entities = currentLevel.getEntities();
			if(entities!=null)
			{
				for(Entity entity:entities)
				{
					entity.render(newGraphics);
				}
			}			
		}
	}
	
	public void renderViewPort()
	{
		
	}
	
	public Image getBufferedImage()
	{
		return backBuffer;
	}
}
 

Fu3L

Top Contributor
Dein Ansatz sieht sehr kompliziert aus und ich verstehe es nicht nach 3 Minuten draufgucken^^ Vllt kannst du es noch mehr erklären oder anders versuchen?
Ich mache das immer so, dass die Zeichenposition der Figur nie(!) geändert wird. Die Bewegung wird auf die Spielerposition draufgerechnet und daraus dann die Koordinaten des Viewports berechnet. Das kannst du im verlinkten Thread nachlesen.
 

Quaxli

Top Contributor
Hallo Community,

...
Mein Problem bei dem aktuellen scrolling ist, dass die figur sich schneller bewegt als die karte im Hintergrund. Meine Grund Idee war, dass die Spielerposition immer absolut in der Mitte ist.
...
[/code]

Warum bewegst Du die Figur dann, wenn Sie doch in der Mitte bleiben soll? :D

Lösungsvorschlag von mir:

- die Figur (mit all ihren Animationen) bleibt immer in der Mitte und wird nicht bewegt.

- die Bewegung wird durch eine virtuelle Klasse übernommen, nennen wir sie mal MapDisplay. Diese erbt von Rectangle2D.Double und bekommt noch alle notwendigen Methoden um eine Bewegung simulieren zu können. Im einfachsten Falle wäre das bei horizontaler Bewegung x+1 (oder so) pro Gameloop.

- bei jedem GameLoop berechnet die Klasse MapDisplay, welche Tiles in Abhängigkeit Ihrer virtuellen Position sichtbar ist und zeichnet dieses an die korrekte Stelle.

Schon wird Bewegung simuliert ohne daß Deine Figur sich bewegt hätte. :D

Klingt kompliziert? Ist es nicht. Anbei eine kleine Grafik und noch mehr Text zur Verdeutlichung.

4355d1331803321t-scrollbare-map-tiles.png

(Die Zeichnung ist NICHT maßstabsgetreu!!!)

Die weißen Rechtecke sind Deine Tiles (erben auch von Rectangle2D.Double -aber das nur nebenbei). Das Tile links oben in der Ecke steht an Postion 0,0. (Und bleibt auch immer da)
Nehmen wir eine Tilegröße von 32 an, dann hätte das daneben 0,32. Und so weiter und so fort. Das sollte ja klar sein.

Der rot eingefärbte Bereich repräsentiert die Klasse MapDisplay. Dies ist Dein virtueller Bildschirm. Das was aktuell angezeigt wird. Sie erbt wie gesagt von Rectangel2D.Double und wir nehmen an, aß sie in dem Beispiel erzeugt worden ist mit einer Position bei 0,0 und einer Breite von 800 und eine Höhe von 600 (je nachdem wie groß Deine Zeichenfläche halt ist).
In der Beispielgrafik hat sich der Spieler schon ein Stück bewegt und virtuell den rot eingefärbten Bereich erreicht. Real steht er natürlich noch immer in der Mitte des Bildschirms. ;)
Alles was rot ist, müßte jetzt angezeigt werden.

Um heraus zu bekommen, welche Tiles aktuell angezeigt werden müssen, muß man wissen welche Tiles sich mit dem sichtbaren Bereich überschneiden. Da Tiles und MapDisplay von Rectangle erben, geht das ganz einfach mit intersects(). Man erhält alleTiles, die sich mit unserem sichtbaren Bereich zumindest ein bißchen überschneiden. In der Zeichnung sind das alle, die mit einem grünen Rand eingefaßt sind. Alle diese Tiles müssen zumindest teilweise (tileweise? ;) ) gezeichnet werden.

Und das war's dann auch schon fast:

Danach muß man nur noch ein bißchen rechnen, um die Tiles an der richtigen Position anzuzeigen. Das ist aber relativ einfach. Nehmen wir an die Tilegröße ist 32. Das Tile links oben hat immer noch die Postion 0,0.
Der sichtbare Bereich (rot) ist durch die Bewegung der Figur gewandert. Und liegt grob geschätzt bei 50,50 (ist als Beispiel einfacher zu berechnen ;) ). (Und wie gesagt, die Zeichnung ist nicht maßstabsgerecht).
Eines der zu zeichnenden Tiles wäre das links oben im grünen Rahmen (grün = Summe der benötigten Tiles). Dieses Tile steht prinzipiell an der Positon 32,32. Damit es richtig gezeichnet wird (teilweise außerhalb) muß es verschoben werden.

Die linke obere Ecke unseres sichtbaren Bereichs (also der Klasse MapDisplay) liegt ja durch die Bewegung bei 50,50. Demnach ergibt sich für das o. g. Tile eine Position von 32 - 50 (jeweils für x und y). Und damit temporär eine andere Position. Das erste sichtbare Tile wird also nicht bei 32,32 gezeichnet, sondern bei -27,-27 - und damit korrekt angezeigt. Nämlich so, daß für uns nur der kleine rot gefärbte Bereich in unserer linken oberen Ecke zu sehen ist.
Diese Berechnung für alle sichtbaren Tiles (grüner Bereich) verschiebt Deinen Hintergrund gemäß der Bewegung des Spielers.

So, ich hoffe das ist jetzt einigermaßen verständlich beschrieben. Viel Spaß.
 

Anhänge

  • tiles.png
    tiles.png
    10,1 KB · Aufrufe: 40
Zuletzt bearbeitet:

mavinatic

Bekanntes Mitglied
Hallo Community,

danke an alle die mir helfen wollten, ich habe es nun so implmenetiert, dass meine Figur in der Bildschirmmitte ist :)

Nun habe ich eine Kollisionsabfrage noch eingebaut, jedoch funktioniert diese nicht richtig, kann es sein,dass es was mit dem Scrolling zutun hat?

Hier der Quelltext gezipped zum Download
Download

Gruß
 

Fu3L

Top Contributor
Nun habe ich eine Kollisionsabfrage noch eingebaut, jedoch funktioniert diese nicht richtig, kann es sein,dass es was mit dem Scrolling zutun hat?

Ich hab leider keine Zeit, mir den ganzen Quelltext durchzulesen, aber eigentlich sollte die Kollisionsabfrage vollkommen unabhängig von der Position der Gegner auf dem Bildschirm sein. Es interessiert nur, wo sie sich "logisch" befinden. Wenn du das sauber trennst, dürfte es zumidnest nicht am Scrolling liegen.
 

mavinatic

Bekanntes Mitglied
Da ich leider meinen Eintrag nicht editieren kann muss ich leider doppelt Posten :-(

Anbei findet ihr eine Grafik, welche mein aktuelles Problem sehr gut beschreibt. Wie ihr sehen könnt ist der Spieler außerhalb der Map, womit die Kollisionsabfrage nicht stimmt (Kollisionsabfrage links und oben stimmen, unten und rechts nicht :-(). Ich sitze nun schon 3 Tage an diesem Problem und finde keine Lösung. Könntet ihr mir einen Hinweis geben, warum der Spieler an dieser Position außerhalb vom Spielfeld gelangt.

Ebenso gebe ich euch ein Stück Quelltext ;-)
Java:
	public int getMapX()
	{
		return this.getPositionX()+windowPosX;
	}
	public int getMapY()
	{
		return this.getPositionY()+windowPosY;
	}
	@Override
	public void move()
	{	
//		System.out.println("movementData:");
//		System.out.println(this.getPositionX()+"|"+this.getPositionY());
//		System.out.println(this.getMapX()+"|"+this.getMapY());
		if(handler.isKeyForward() && isMovableOnTile('n') && !handler.isKeyMenu())	
			this.setPositionY(this.getPositionY() - this.getEntityMovementSpeed());
		
		if(handler.isKeyBackward() && isMovableOnTile('s') && !handler.isKeyMenu())
			this.setPositionY(this.getPositionY() + this.getEntityMovementSpeed());
		
		if(handler.isKeyLeft() && isMovableOnTile('w') && !handler.isKeyMenu())
			this.setPositionX(this.getPositionX() - this.getEntityMovementSpeed());
		
		if(handler.isKeyRight() && isMovableOnTile('e') && !handler.isKeyMenu())
			this.setPositionX(this.getPositionX() + this.getEntityMovementSpeed());	
	}
	
	/*
	 * CollisionDetectionViaPx
	 */
	public boolean isMovableOnTile(char dir)
	{
		float x = 0;
		float y = 0;
		
		if(dir=='n')
		{
			x = getMapX()/Tile.SIZE;
			y = (getMapY()-this.getEntityMovementSpeed())/Tile.SIZE;
			Tile currentTile = this.getCurrentLevel().getTile((int)x,(int)y);
			return currentTile.isMovable();
		}
		if(dir=='s')
		{
			x = getMapX()/Tile.SIZE;
			y = (getMapY()+this.getEntityMovementSpeed())/Tile.SIZE;
			Tile currentTile = this.getCurrentLevel().getTile((int)x,(int)y);
			return currentTile.isMovable();	
		}
		if(dir=='w')
		{
			x = (getMapX()-this.getEntityMovementSpeed())/Tile.SIZE;
			y = getMapY()/Tile.SIZE;
			Tile currentTile = this.getCurrentLevel().getTile((int)x,(int)y);
			return currentTile.isMovable();
		}
		if(dir=='e')
		{
			x = (getMapX()+this.getEntityMovementSpeed())/Tile.SIZE;
			y = getMapY()/Tile.SIZE;
			Tile currentTile = this.getCurrentLevel().getTile((int)x,(int)y);
			return currentTile.isMovable();	
		}
		return false;
	}
pic01t.png


Mfg
 

Fu3L

Top Contributor
Ich denke der Fehler wird in der Speicherung der Spielerposition liegen oder der Übergabe. Die Spielerposition hat nichts! mit der Position des Fensters zu tun oder wo die Figur im Fenster gezeichnet wird (da die Figur eh immer im Zentrum gezeichnet wird, wäre das ja eh unnütz) ;)

Habe mir außerdem die Freiheit genommen, dir eine etwas elegantere Lösung mit weniger Wiederholungen zu schreiben:

Java:
public void move() {
	if(!handler.isKeyMenu()) { //Besser vorher abfangen?
		float factorX = 0;
		float factorY = 0;	
		if(handler.keyIsForward()) {
			factorY = -1;
		}
		if(handler.keyIsBackward()) {
			factorY = 1;
		}
		if(handler.keyIsLeft()) {
			factorX = -1;
		}
		if(handler.keyIsRight()) {
			factorX = 1;
		}
		if(factorX != 0 || factorY != 0) {
			float newPosX = this.getPlayerX()+this.getEntityMovementSpeed()*factorX;
			float newPosY = this.getPlayerY()+this.getEntityMovementSpeed()*factorY;
			if(isPosMovable(newPosX, newPosY)) {
				this.setPosition(newPosX, newPosY);
			}
		}
	}
}

public boolean isPosMovable(float x, float y) {
	return this.getCurrentLevel().getTile((int)(x/Tile.SIZE),(int) (y/TILE.SIZE)).isMovable();
}

Dieser Code müsste korrekt funktionieren so lange getPlayerX() wirklich nur den logischen Ort zurückgibt und du da nichts anderes reinwurstest. ;) (Bzw. eigentlich müsste es ja einfach nur getX() oder getPosX() heißen, das würde zur allgemeinen "EntityMovementSpeed" Methode passen.. Dein Spieler scheint ja eine Subklasse von Entity zu sein).
Wenn der Spieler zu Beginn im Ursprung der Spielwelt steht, dann hat seine Position 0,0 zu sein, auch wenn er sich mitten im Bildschirm bei 500, 350 oder so befindet.

Edit: mir fällt grad auf, dass es so noch nicht ganz funktionieren kann, wenn du dich nicht diskret Feld für Feld bewegst (was du nicht tun wollen wirst, wenn du float nutzt)... Aber das kriegst du schon hin. So kann mal ein Stück vom Spieler in ein unbegehbares Tile reinhängen, aber er sollte zumindest irgendwann aufgehalten werden.
 
Zuletzt bearbeitet:

mavinatic

Bekanntes Mitglied
Danke erstmal für die Verbesserung.
Ich habe nur float gewählt um zu sehen, was für eine Zahl dabei herrauskommt, wenn ich das durch / Tile.SIZE dividiere ;-) Im Grunde genommen ists int.

Die Position 0,0 entspricht der MapKordinaten -375, -250 für die X und Y Kordinaten.
 

Fu3L

Top Contributor
Die Position 0,0 entspricht der MapKordinaten -375, -250 für die X und Y Kordinaten.

Das verstehe ich nicht^^ Was meinst du denn mit Position? Im Prinzip gibt es nur eine Art von Position und zwar die logische Position im Game, meist als Ort auf der Karte realisiert. Spieler ist an Position 0,0 auf der Karte => Position des Spielers 0,0.

Wenn es dann zum Zeichnen kommt, erst dann, was viel später und vollkommen unabhängig vom Rest ist, musst du Umrechnen: Player in die Mitte setzen (ohne dabei seine logische Position irgendwie zu verändern) und alle anderen Objekte entsprechend verschoben zeichnen.

Das ist wirklich wichtig zu verstehen.

Eine weitere allgemeine Anmerkung: Diese "isPosMovable()" Funktion dürfte besser in der Oberklasse Entity oder gar in der Map aufgehoben sein, da ja auch andere Entitites nicht alle Orte betreten dürfen.
 

mavinatic

Bekanntes Mitglied
Es ist schon richtig das nur die Klasse, diese Methode hat. ;-) Entity ist die Abstrakte Oberklasse. MovableEntity müsste es wenn bekommen, aber es passt schon so! :) Ich kann übrigens meine Figur nicht mehr bewegen -.- Nach dem "verbessern"

Java:
@Override
	public void move()
	{	
		if(!handler.isKeyMenu())
		{
			if(handler.isKeyForward())
				factorY = -1;
			if(handler.isKeyBackward())
				factorY = 1;
			if(handler.isKeyLeft())
				factorX = -1;
			if(handler.isKeyRight())
				factorX = 1;
			if(factorX != 0 || factorY != 0)
			{
				int posx = (this.getPositionX()) + (this.getMovementSpeed()*factorX);
				int posy = (this.getPositionY()) + (this.getMovementSpeed()*factorY);
				
				if(isTileMovable(posx, posy)) 
				{
					this.setPositionX(posx);
					this.setPositionY(posy);
				}
			}
		}
	}

Hier der komplette Quelltext...schaus dir mal an...ich versteh's nämlich auch nicht so warum ich 0,0 position nicht am MapUrsprung gezeichnet wird....also die Spielerfigur

Download
 

Fu3L

Top Contributor
Wenns erstmal läuft, musst du auf jeden Fall prüfen vorm Zeichnen, ob dein Tile überhaupt sichtbar ist^^

Java:
newGraphics.drawImage(tile.getSpriteImage(), (i*Tile.SIZE)-gamerEntity.getPositionX(), (j*Tile.SIZE)-gamerEntity.getPositionY(), null);

Versuche hier mal: screenWidth/2 - gamerEntity.getPositionX() + i*Tile.SIZE und korrespondierend bei y.

Du solltest außerdem factorX und factorY als Variablen in der Methode lassen. Sie werden nur dort benötigt und sollen deswegen auhc nur dort sichtbar sein. Außerdem müssen die jedes Mal wieder auf 0 gesetzt werden!

Wenns danach immer noch nicht bewegbar ist, muss man mal sehen.
 

mavinatic

Bekanntes Mitglied
Hey Fu3l,

ich kann mich nun auch bewegen, aber....die Abfrage stimmt dann nicht mehr?!
Java:
	@Override
	public void move()
	{	
		System.out.println("posx="+this.getPositionX()+"|posy="+this.getPositionY());
		int factorX = 0;
		int factorY = 0;
		
		if(!handler.isKeyMenu())
		{
			if(handler.isKeyForward())
				factorY = -1;
			if(handler.isKeyBackward())
				factorY = 1;
			if(handler.isKeyLeft())
				factorX = -1;
			if(handler.isKeyRight())
				factorX = 1;
			
			if(factorX != 0 || factorY != 0)
			{
				int posx = this.getPositionX() + (this.getMovementSpeed()*factorX);
				int posy = this.getPositionY() + (this.getMovementSpeed()*factorY);
				
				
				System.out.println("posx="+posx+"|posy="+posy);
				if(isTileMovable(posx, posy)) 
				{
					this.setPositionX(posx);
					this.setPositionY(posy);
				}
				
				factorX = 0;
				factorY = 0;
			}
		}
	}
	
	public boolean isTileMovable(int posx, int posy)
	{
		return this.getCurrentLevel().getTile(posx / Tile.SIZE, posy / Tile.SIZE).isMovable();
	}

Hier ein kleines "Beweisfoto" ;-)
justing.png
 

Fu3L

Top Contributor
Also als ichs grade gestartet hab (noch das alte) konnte ich mich wegen einer riesen Anzahl von NPE's nicht bewegen. Und auch jetzt, scheint der Spieler abseits zu stehen.

Du musst den Spieler irgendwo weiter weg spawnen lassen, sagen wir bei 500, 500 oder so, weil sonst der Array Index negativ wird, da ja links und oberhalb des Spielers auch noch Objekte angezeigt werden. Du kannst die Spielwelt nur im vierten Quadranten aufbauen. (Bzw. von den Wertigkeiten wärs der erste im normalen Koordinatensystem^^).
Dann könnten immer noch Fehler auftreten, dann lads nochmal hoch.
 

mavinatic

Bekanntes Mitglied
Hey Fu3l,

ich habe das Gefühl, dass nochmehr Probleme auftreten als vorher ;-) (ICH SCHÄTZE DEINE HILFE!!). Bei mir werden nach deinem Vorschlag keine Grenzen mehr angezeigt...
 

Fu3L

Top Contributor
Wenn du wirklich 500, 500 genommen hast, kannst ja sein, dass du mitten drin bist. Lads mal ganz hoch wieder, ich guck dann morgen nach meiner Klausur ;)
 

Fu3L

Top Contributor
Ich möchte generell erstmal sagen, dass der grobe Aufbau schon recht gut ist. Beginne jetzt mit der Fehlersuche und wenn mir was auffällt, ergänze ich.

Java:
//W
case 87:

Das geht gar nicht.KeyEvent.VK_W und nicht anders ;)

Wenn ich den Spieler anfangs auf 200, 200 stelle gehts übrigends mit dem Laufen. Wenn auch stockend, aber es geht.

Java:
for(int i=0;i<map.length;i++)
				{
					for(int j=0;j<map[i].length;j++)
					{
						if(gamerEntity!=null)
						{

Wieso erst durchlaufen und dann nach dem gamer fragen? Ob gamerEntity null ist, wird sich nicht ändern.

Java:
if(entity instanceof GamerEntity)
						{
							gamerEntity = (GamerEntity) entity;	
						}

Du hast doch GamerEntity als Argument in der Methode ,wieso also gamerEntity neu setzen?

Woher die Ruckler kommen, kannst du ja nochmal gucken.
 

mavinatic

Bekanntes Mitglied
Die Tastaturabfrage habe ich nun korrigiert...die Abfrage für GamerEntity habe ich eingebaut, weil kein Parameter übergeben wird, das Interface dient nur als allgemeine "Schablone" implementiert vom Interface "Renderable". In Game - Class werden nur null übergeben ;-) von daher stimmt das schon so, zu dem wird GamerEntity immer aktualisiert.... ;-)

Wenn ich von 200,200 starte, dann Ruckelt nichts.... ;-) aber die Abfragen stimmen noch nicht überein?!
 

Fu3L

Top Contributor
Eine wichtige Sache, die mir noch aufgefallen ist: Du hast die Bilder mit PNG am Ende benannt, aber im Quelltext steht .png... Kann dir auf Linux Systemen wo auf Groß- und Kleinschreibung geachtet wird,Probleme einbringen. (Hätte eigentlich gedacht, dass es auf Windows auch um die Ohren fliegt, weil Java das nicht nimmt)

Und die Abfrage stimmt auch nicht, weil du in initMap() nicht das gleiche stehen hast, wie in initMapFromFile ;) Danach ist es zwar immer noch etwas vermurkst, aber da darfst du nochmal selsbt gucken^^

Im übrigen könntest du nicht jedes Mal das gleiche Tile verwenden. Das erfordert zwar dann, dass du das Bild irgendwo cachest und dass (wen mans praktisch macht) zB in deinem Tile-Type Enum sowas wie GRASS.getNewInstanceOf() oder so einbaust, ermöglicht aber, auf den Tiles noch weitere informationen zu speichern, die für die KI wichtig sein könnten. Das ist aber nur eine von vielen Möglichkeiten.
 

mavinatic

Bekanntes Mitglied
Eine wichtige Sache, die mir noch aufgefallen ist: Du hast die Bilder mit PNG am Ende benannt, aber im Quelltext steht .png... Kann dir auf Linux Systemen wo auf Groß- und Kleinschreibung geachtet wird,Probleme einbringen. (Hätte eigentlich gedacht, dass es auf Windows auch um die Ohren fliegt, weil Java das nicht nimmt)

Ja, das wird auch noch anders gelöst, jedoch ist es ja nur übergangsweise....mir geht es hauptsächlich nun um die Kollision und das Scrolling.

Und die Abfrage stimmt auch nicht, weil du in initMap() nicht das gleiche stehen hast, wie in initMapFromFile ;) Danach ist es zwar immer noch etwas vermurkst, aber da darfst du nochmal selsbt gucken^^

Es soll auch nicht das gleiche drinnen stehen, deshalb ist es ja auskommentiert :)

Im übrigen könntest du nicht jedes Mal das gleiche Tile verwenden. Das erfordert zwar dann, dass du das Bild irgendwo cachest und dass (wen mans praktisch macht) zB in deinem Tile-Type Enum sowas wie GRASS.getNewInstanceOf() oder so einbaust, ermöglicht aber, auf den Tiles noch weitere informationen zu speichern, die für die KI wichtig sein könnten. Das ist aber nur eine von vielen Möglichkeiten.
Auch Übergangslösung - Wie gesagt hauptsächlich gehts mir um die Kollision und das Scrolling, wäre nett wenn mir da wer ein Tipp geben kann, wie die kollisionsabfrage richtig ist?!
 

Fu3L

Top Contributor
Hätte schwören können, dass es besser funktionierte^^

Aber jetzt glaube ich wieder es zu haben^^

Java:
screenHeight / 2 - gamerEntity.getPositionY() + (j - 1) * Tile.SIZE,

Nutzt du das beim Zeichnen als y-Position funktionierts. Warum das so muss, kann ich dir aber leider gerade nicht beantworten^^ Vllt ist es auch nur ein Bug, der einen anderen aufhebt^^
Dass der eine zwischendrin kaum zum Kollidieren taugt, liegt einfach darin, dass die Kollisionsabfrage momentan auf diskreten Bewegungen aufbaut. Wenn du da 4 Stück machst, klappts auch.
(Dass man etwas ins blaue reingeht von rechts und unten ist normal wegen der diskreten Bewegung).

PS: Du lädst deine Map falschrum ein ;) Aus
Code:
111
111

wird im Spiel
Code:
11
11
11
 

mavinatic

Bekanntes Mitglied
PS: Du lädst deine Map falschrum ein ;) Aus
Code:
111
111

wird im Spiel
Code:
11
11
11

Nein, denn dann könnte ich nicht einfach Tiles ändern, was ich gerade gemacht habe^^ :-O Probiers aus ;-)

Die Kollision stimmt jetzt nur rechts?! wenn ich j-1 mache bzw und i-1
 
Zuletzt bearbeitet:

Fu3L

Top Contributor
Das praktische ist: Durch den factorX und Y weiß man, in welche Richtung die Figur geht, daher kann man das leicht verbessern.

Irgendwo muss aber nochn Fehler drin sein, weils noch nicht genau abschließt.. Das vermute ich aber eher beim Zeichnen^^

Java:
	@Override
	public void move()
	{
		System.out.println("posx=" + this.getPositionX() + "|posy=" + this.getPositionY());
		int factorX = 0;
		int factorY = 0;

		if(!handler.isKeyMenu())
		{
			if(handler.isKeyForward())
				factorY = -1;
			if(handler.isKeyBackward())
				factorY = 1;
			if(handler.isKeyLeft())
				factorX = -1;
			if(handler.isKeyRight())
				factorX = 1;

			if(factorX != 0 || factorY != 0)
			{
				int posx = this.getPositionX() + (this.getMovementSpeed() * factorX) + factorX * this.getEntityImage().getWidth(null);
				int posy = this.getPositionY() + (this.getMovementSpeed() * factorY) + factorY * this.getEntityImage().getHeight(null);

				if(isTileMovable(posx, posy))
				{
					this.setPositionX(posx);
					this.setPositionY(posy);
				}
			}
		}
	}
 

Fu3L

Top Contributor
Wenn du alles zusammengefügt haben solltest, dürften nur noch etwas die Ränder überlappen. Sollte es anders sein, solltest du es genauer beschreiben.
Und hier darfste selbst einbauen: Denke, dass es daran liegt, dass die Spielerposition die Position einer der Ecken ist und somit beim Zeichnen die Tiles nicht ganz perfekt gemalt werden (da muss dass Zentrum des Spielers gewählt werden.) Das solltest du aber hinbekommen ;)
 

Fu3L

Top Contributor
Habe noch folgendes geändert:
Java:
int posx = this.getPositionX() + (this.getMovementSpeed() * factorX);
				int posy = this.getPositionY() + (this.getMovementSpeed() * factorY);

				if(isTileMovable(posx + factorX * this.getEntityImage().getWidth(null), posy + factorY * this.getEntityImage().getHeight(null)))
				{

Daher kam die schnelle Bewegung vorher. Das muss natürlich nur bei der Abfrage berücksichtigt werden^^

Das lustige ist übrigends, dass ich mich bis 1 Feld vor den rechten und unteren Rand nur bewegen kann und es links und oben klappt^^ xD
Ich würde empfehlen: Denk nochmal drüber nach, was du in diesem Thread gelernt hast und schreibs nochma neu.. Das hilft meistens^^ Ich bin hier auch ein wenig am Ende^^
Ich lade mal das hoch, wies bei mir jetzt aussieht.
 

Anhänge

  • Jx.zip
    29 KB · Aufrufe: 3

mavinatic

Bekanntes Mitglied
Hey,

ich habe das nun alles nochmal neu geschrieben und habe gemerkt dass einige Verbesserungen möglich sind.

Jedoch hänge ich wieder bei der Kollisionsabfrage. Links und Oben funktioniert die Kollisionsabfrage nicht richtig und unten und rechts sind die Abfragen absolut korrekt.

QuellCode:
Java:
	public void move() {
		int factorX = 0;
		int factorY = 0;
		
		if(!handler.isMenuKey()) {
			if(handler.isKeyW()) {
				factorY = -1;
			}
			if(handler.isKeyS()) {
				factorY = 1;
			}
			if(handler.isKeyA()) {
				factorX = -1;
			}
			if(handler.isKeyD()) {
				factorX = 1;
			}
			if(factorX != 0 || factorY != 0) {
				int newPosX = getPosX() + (factorX * getSpeed());
				int newPosY = getPosY() + (factorY * getSpeed());
				if(isMovableOnTile(newPosX, newPosY)) {
					setPosX(newPosX);
					setPosY(newPosY);
				}
			}			
		}
	}
	
	public boolean isMovableOnTile(int posX, int posY) {
		/* X-Achse */
		int tileX = posX / Tile.SIZE;
		
		if(posX % Tile.SIZE > 0) {	
			tileX++;
		}
		
		/* Y-Achse */
		int tileY = posY / Tile.SIZE;
		
		if(posY % Tile.SIZE > 0) {
			tileY++;
		}
		
		return game.getLevel().getTile(tileX, tileY).isMovable();
	}
Gruß
 

Fu3L

Top Contributor
Nehmen wir an, Tile 0,0 sei nicht begehbar. Es ist 100 lang. Du kommst von rechts drauf zu und bist noch auf Tile 1,0 und dementsprechend auf Weltpostion 10x,0.
Nun betrittst du Tile 0,0:

int tileX = posX / Tile.SIZE;
tileX = 95 / 100 = 0

posX % Tile.SIZE > 0 then tileX++
95 % 100 = 95, also tileX = 1

Ist Tile 1 begehbar? Ja.

Diese Modulo Abfrage zeigt, dass du etwa in die richtige Richtung gedacht hast, aber sie wird (fast) immer wahr sein. Du musst hier wohl anstatt dessen noch die Bewegungsrichtung mit einbauen müssen. Einen Ansatz hatte ich schonmal geliefert (und er müsste eigentlich tun) oder du nutzt halt deine alte Variante wieder, dass du die Richtung mit übergibst (was vllt nicht soo schön wäre).

Edit: Merke grade, dass der doch net gaaanz klasse war. Wenn die Position eines Sprites in einer der linken Ecken gemessen würde, sähe es so aus:
Java:
if(factorX == 1) {
posX += getImage().getWidth();
}
Soll heißen: Bei Bewegung nach rechts muss noch einmal die Bildbreite addiert werden, bevor kontrolliert wird.

Das müsste eigentlich funktionieren, wenn das Modulo verschwindet.
 
Zuletzt bearbeitet:

mavinatic

Bekanntes Mitglied
Danke Fu3l du hast es echt drauf ;-)

Java:
public void move() {
		int factorX = 0;
		int factorY = 0;
		
		if(!handler.isMenuKey()) {
			if(handler.isKeyW()) {
				factorY = -1;
			}
			if(handler.isKeyS()) {
				factorY = 1;
			}
			if(handler.isKeyA()) {
				factorX = -1;
			}
			if(handler.isKeyD()) {
				factorX = 1;
			}
			if(factorX != 0 || factorY != 0) {
				int newPosX = getPosX() + (factorX * getSpeed());
				int newPosY = getPosY() + (factorY * getSpeed());
				if(isMovableOnTile(newPosX, newPosY, factorX, factorY)) {
					setPosX(newPosX);
					setPosY(newPosY);
				}
			}			
		}
	}
	
	public boolean isMovableOnTile(int posX, int posY, int factorX, int factorY) {
		if(factorX == 1) {
			posX = getPosX() + getImage().getWidth(null);
		}
		if(factorY == 1) {
			posY = getPosY() + getImage().getHeight(null);
		}
				
		return game.getLevel().getTile(posX/Tile.SIZE, posY/Tile.SIZE).isMovable();
	}

Das wäre die Lösung, danke schön :) Klappt gut, jedoch habe ich nun ein Tile in die Mitte gemacht aber die Kollision stimmt manchmal manchmal ebend nicht :-( Hat jemand da Ahnung woran es liegen könnte, dass die Kollision nicht immer stimmt? Die Kollision außen stimmt jedoch IMMER :-(
 

Fu3L

Top Contributor
Java:
posX = getPosX() + getImage().getWidth(null);

Das ist so nicht ganz richtig. Du nutzt die Position ohne Bewegung. posX += getImage.getWidth(null); So würde immer ein wenig fehlen bei der Kollisionserkennung.

Beschreibe bitte das Verhalten, wenn du ein Tile irgendwo innerhalb der Karte hast, genauer. Tut nicht, hilft nicht ;) Von welcher seite oder kann man ganz durchgehen, sowas halt.
 

mavinatic

Bekanntes Mitglied
Java:
posX = getPosX() + getImage().getWidth(null);

Das ist so nicht ganz richtig. Du nutzt die Position ohne Bewegung. posX += getImage.getWidth(null); So würde immer ein wenig fehlen bei der Kollisionserkennung.

Beschreibe bitte das Verhalten, wenn du ein Tile irgendwo innerhalb der Karte hast, genauer. Tut nicht, hilft nicht ;) Von welcher seite oder kann man ganz durchgehen, sowas halt.

Das ist so richtig, denn ich kollidiere so richtig mit den Objekten, wenn ich das nicht so machen würde, dann würde ich immer 5 Pixel vorher kollidieren :-(

Zu meinem aktuellen problem habe ich einen screenshot angehangen:
ieeehaaeehh.jpg


Das passiert immer wenn ich nicht genau, d.h. schwarze( Spieler ) schneidet das blaue Rechteck nur zum Teil., jedoch funktionierts manchmal, wenn ich von oben oder unten komme.

Spielbare Demo :-D
Download
 

mavinatic

Bekanntes Mitglied
Hallo Community,

verzeiht mir bitte den Doppelpost, denn ich kann meinen alten Post nicht editieren :-(

Ich hänge immernoch an der Kollision. Ich habe nun mein Tile mit der Klasse Rectangle2D.Double() erweitert um die intersects funktion nutzen zu können.

Ich überprüfe alle im Umfeld liegenden Tiles ob die "Movable" sind und dann gebe ich zurück ob man gehen kann oder nicht, aber irgendwie bleibt der Spieler fest stecken und weiß einfach nicht wieso.

Das ist so kompliziert irgendwie :-(

Java:
private List<Rectangle2D> rectAngles = new ArrayList<Rectangle2D>();
	public boolean isMovable() {
		boolean move = true;
		int x = getPosX()/Tile.SIZE;
		int y = getPosY()/Tile.SIZE;
		
		int xi = x-1;
		int xiMax = x+1;
		int yi = y-1;
		int yiMax = y+1;
		
		if(xi<0) xi=0;
		if(yi<0) yi=0;
		
		rectAngles.clear();
		for(int cx = xi; cx<=xiMax; cx++) {
			for(int cy = yi; cy<=yiMax; cy++) {
				Tile cTile = game.getLevel().getTile(cx, cy);
				if(cTile!=null) {
					this.setRect(getPosX(), getPosY(), getWidth(), getHeight());
					
					Rectangle2D.Double r = new Rectangle2D.Double();
					r.setRect(cx*Tile.SIZE, cy*Tile.SIZE, Tile.SIZE, Tile.SIZE);
					if(this.intersects(r)) {
						System.out.println("Intersect with x="+cx+"|y="+cy);
						rectAngles.add(r);
					}
				}
			}
		}
		
		
		for(int i=0;i<rectAngles.size(); i++) {
			Rectangle2D r = rectAngles.get(i);
			int tx = (int) (r.getX()/Tile.SIZE);
			int ty = (int) (r.getY()/Tile.SIZE);
			
			if(!game.getLevel().getTile(tx, ty).isMovable()) {
				move = false;
			}
		}
		return move;
	}

Habt ihr eine Idee?
 

Fu3L

Top Contributor
Ich hab mir mal die Freiheit genommen, selbst so ein Spiel mit TileMap zu bauen, um zu sehen, wo vielleicht der Fehler liegt. Werde es hier mit hochladen.
Ich habe die Kollisionsprobleme jetzt damit gelöst, dass ich jede Ecke des Spielers auf Bewegbarkeit prüfe. Das geht so lange, wie der Spieler nicht breiter wird als ein Tile.
Sollte man nun Pokemon-ab-Rubin-Style mit seinem Kopf noch ein Wassertile verdecken können wollen, würde ich tatsächlich über den Rectangle2D Ansatz gehen und eine Art "Bodendecker" Rechteck verwenden, dass weniger hoch ist als der Spieler. Dann muss man in einem Loop alle in Frage kommenden Tiles in der Nähe des Spieler prüfen.

Das einzige was noch ein kleines Problem bereitet, ist, dass der Spieler bei einer Geschwindigkeit von 5 pro Frame bis zu 4 Pixel vom Rand entfernt stehen bleibt. Da kann man sicherlich eine Lösung für finden.

(PS: Der GameLoop ist copy&paste von einem anderen Spiel, daher etwas übergroß für diesen Test^^ (und vllt nicht besonders schön, da in Eile geschrieben))
 

Anhänge

  • TileTest.zip
    17,1 KB · Aufrufe: 11
Zuletzt bearbeitet:

mavinatic

Bekanntes Mitglied
Hallo Fu3l ich habe probiert mal meine Kollisionsabfrage zu programmieren, aber irgendwie schaff ich's nicht, vielleicht könntest du / ihr mal drüber gucken. Vielleicht mache ich einfach nur'n dämlichen Fehler?!

Java:
private boolean isMovable(double npx, double py, int factorX, int factorY) throws Exception {
		if(factorX==1) //Wenn die Richtung nach RECHTS ist, dann die aktuelle Position + Image.Width
			npx += images[0].getWidth();
		if(factorY==1) // Wenn die Richtung nach UNTEN ist, dann die aktuelle Position + Image.Height
			py += images[0].getHeight();
		
		//Wenn X<0 und Y<0 bzw X>Map.SIZE und Y>Map.SIZE
		if(npx<0) 
			npx=0;
		
		if(py<0)
			py=0;
		
		if(npx>(level.getWidth()*Tile.SIZE))
			npx=level.getWidth()*Tile.SIZE;
		
		if(py>(level.getHeight()*Tile.SIZE))
			py=level.getWidth()*Tile.SIZE;
		
		int tileX = 0;
		int tileY = 0;
		
		//Wenn X ungerade Tile.SIZE d.h. der Spieler steht auf zwei Tiles. 
		if(npx % Tile.SIZE != 0) {
			//if links dann wird ein Tile von dem abgezogen um das Tile davor zu kontrollieren
			if(factorX==-1) {
				tileX = (int) (npx % Tile.SIZE);
				tileX--;
			}
			//if rechts bei rechts wird das tile rechts daneben überprüft
			if(factorX==1) {
				tileX = (int) (npx % Tile.SIZE);
				tileX++;
			}
		} else {
			tileX = (int) (npx / Tile.SIZE);
		}
		
		if(py % Tile.SIZE != 0) {
			//if up
			if(factorY==-1) {
				tileY = (int) (py % Tile.SIZE);
				tileY--;
			}
			//if down
			if(factorY==1) {
				tileY = (int) (py % Tile.SIZE);
				tileY++;
			}
		} else {
			tileY = (int) (py/Tile.SIZE);
		}
		
		
		System.out.println("X="+(int)npx / Tile.SIZE+"||Y="+(int)py / Tile.SIZE+"||||NMX="+tileX+"||NMY="+tileY);
		return level.getTile(tileX,tileY).isMovable();
	}

Mein Spieler kann sich kaum bewegen, aber von der Logik her müsste das doch so stimmen?!
 

Fu3L

Top Contributor
Java:
 if(npx % Tile.SIZE != 0) {
            //if links dann wird ein Tile von dem abgezogen um das Tile davor zu kontrollieren
            if(factorX==-1) {
                tileX = (int) (npx % Tile.SIZE);
                tileX--;
            }
            //if rechts bei rechts wird das tile rechts daneben überprüft
            if(factorX==1) {
                tileX = (int) (npx % Tile.SIZE);
                tileX++;
            }
        } else {
            tileX = (int) (npx / Tile.SIZE);
        }

Wie groß ist der Spieler? Wenn der Spieler hier kleiner als eine Tile.SIZE ist, dann ist deine Annahme falsch, dass das Tile daneben überprüft werden muss.
Ansonsten scheint das wohl recht gut. Deine Ausgabe mit anzugeben wäre hilfreich (zusammen mit Spielerposition und factor vllt).

Ist es denn so, dass der Spieler, oder irgendeine andere Kreatur für die man diese Methode recyclen könnte, jemals größer wird ein Tile? Dann würde meine Variante nämlich auch super und eifach funktionieren ;) Insbesondere braucht man auch nicht unbedingt den Levelrand checken, weil meist will man das Level ja eh umranden.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
D scrollbare Karte per JScrollPane? Spiele- und Multimedia-Programmierung 5

Ähnliche Java Themen

Neue Themen


Oben