Tilemap Darstellungsfehler

Raziell

Bekanntes Mitglied
Hallo zusammen,
ich zeichne eine Tilemap mittels aktivem Rendering (BufferStrategy + VolatileImage)
allerdings habe ich ein Darstellungsproblem wenn die Tiles bis ans Ende gescrollt werden.
Ich hänge einfach mal mal ein Screenshot an, evtl. erkennt ja schon jmd. das Problem.
Auf Wunsch kann ich auch Code nachreichen wobei es nicht wirklich viel zu sehen gibt.

Danke
 

Anhänge

  • 1.jpg
    1.jpg
    101,1 KB · Aufrufe: 83

Raziell

Bekanntes Mitglied
Hier mal eine Zusammenfassung:

Java:
	private void initGUI(){	
        graphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment();
		graphicsConfiguration = graphicsEnvironment.getDefaultScreenDevice().getDefaultConfiguration();

		this.setPreferredSize(new Dimension(Configuration.FRAME_WIDTH, Configuration.FRAME_HEIGHT));
		JFrame frame = new JFrame("");
		Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
		int left = (screenSize.width - Configuration.FRAME_WIDTH) / 2;
		int top = (screenSize.height - Configuration.FRAME_HEIGHT) / 2;
		frame.setLocation(left, top);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.add(this);
		frame.pack();
		frame.setIgnoreRepaint(true);
		frame.setFocusable(false);
		frame.setVisible(true);
		setFocusable(true);
		setBackground(Color.BLACK);
		addKeyListener(this);

		createBufferStrategy(2);
		bufferStrategy = getBufferStrategy();
		createBackbuffer();
}
Java:
	private void createBackbuffer() {
		if (volatileImage != null) {
			volatileImage.flush();
			volatileImage = null;
		}
		graphicsEnvironment = GraphicsEnvironment.getLocalGraphicsEnvironment();
		graphicsConfiguration = graphicsEnvironment.getDefaultScreenDevice().getDefaultConfiguration();
		volatileImage = graphicsConfiguration.createCompatibleVolatileImage(getWidth(), getHeight());
	}
Java:
	@Override
	public void run() {
		while (true) {
			calculateFPS();
			checkKeys();
			drawObjects();
			try {
				Thread.sleep(5);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

Java:
	private void drawObjects() {
		checkBackbuffer();

		Graphics g = volatileImage.getGraphics();
		render(g);
		g.dispose();

		Graphics g2 = bufferStrategy.getDrawGraphics();
		g2.drawImage(volatileImage, 0, 0, Configuration.FRAME_WIDTH, Configuration.FRAME_HEIGHT, this);
		g2.dispose();
		bufferStrategy.show();
	}

	private void checkBackbuffer() {
		if (volatileImage == null) {
			createBackbuffer();
		}
		if (volatileImage.validate(graphicsConfiguration) == VolatileImage.IMAGE_INCOMPATIBLE) {
			createBackbuffer();
		}
	}

	public void render(Graphics g) {
		renderer.draw(g);
		renderer.drawFPS(g, fps);
	}
 

Quaxli

Top Contributor
Ich denke nicht, daß es an den Methoden liegt, die das Rendering beinhalten.
Zeig' doch mal den Code,der die Tiles positioniert/bewegt.
 

Raziell

Bekanntes Mitglied
move() wird von einem Thread alle 10ms aufgerufen und bewegt die einzelnen Tiles.

Java:
	public void move(double horizontalSpeed, double verticalSpeed) {
		for (Tile tile : map.getTiles()) {
			tile.move(horizontalSpeed, verticalSpeed, getDisplay());
		}
	}

Die move Methode vom Tile bewegt das Tile in x oder y Richtung.

Java:
	public void move(double horizontalSpeed, double verticalSpeed, Display display) {
		if (horizontalSpeed != 0) {
			x += horizontalSpeed;
		}

		if (verticalSpeed != 0) {
			y += verticalSpeed;
		}
	}

EDIT:

Hm ich glaube mein Problem ist einfach das ich keine Begrenzung habe also ich scrolle die Tiles an den Enden der Map ja quasi über ihr dasein hinaus.
Ich müsste mir ja eigtl eine künstliche Grenze mit Bergen/Wäldern/Wasser schaffen bis zu der ich maximal scrolle.
Weil das Problem tritt ja nur auf wenn ich die Map mit dem Display "verlasse".
 
Zuletzt bearbeitet:

Evil-Devil

Top Contributor
Wieso bewegst du die Tiles und nicht die Kamera?
Spätestens wenn du auch noch andere Elemente auf deinen Tiles darstellst wirst du damit imho gegen die Wand fahren.

Frag besser ab ob die Tiles innerhalb des Sichtfeldes sind und entsprechend gerendert werden müssen. Dann sollte es mit dem scrollen auch ohne Probleme klappen.
 

Raziell

Bekanntes Mitglied
Hi,
sorry für die späte Antwort.

Also prinzipiell stellt die Kamera ja den sichtbaren Bereich auf der Tilemap dar.
Wenn sich nun die Kamera verschiebt, dann verschieben sich ja die Tiles oder sehe ich das falsch?
Ich wüsste jedenfalls nicht wie ich ein verschieben der Kamera anders realisieren könnte als die Tiles zu verschieben.

Spätestens wenn du auch noch andere Elemente auf deinen Tiles darstellst wirst du damit imho gegen die Wand fahren.
Die Objekte auf der Map werden ja genauso wie die Tiles verschoben, deshalb sehe ich da kein Problem.

Ist mein Ansatz denn falsch?
 

Evil-Devil

Top Contributor
Wenn die Kamera sich verschiebt, bleiben die Tiles an ihrer Position. Einzig der Einstiegspunkt in deiner Tilemap ändert sich.

Also wenn deine Kamera am Anfang 0/0 bis 9/9 darstellt und du sie um 5+ verschiebst, würde der Ausschnitt bei 5/5 bis 14/14 liegen. Die Tiles sollten also völlig unberührt davon bleiben.
 

Raziell

Bekanntes Mitglied
Ok ich verstehe, allerdings weiß ich nicht, wie ich das mit klassischem Java2D lösen soll.

Ich habe ja lediglich ein Canvas, auf dem ich zeichne, dass ist ja quasi meine "Kamera".
Bewegen kann ich das Canvas ja nicht aber ich kann das bewegen, was gezeichnet wird und das sind doch die Tiles.

Also irgendwie stehe ich da gerade aufm Schlauch :D
 

Evil-Devil

Top Contributor
Naja, ich würde eben nicht die Tiles bewegen, sondern einfach nur die Tiles zeichnen die nach dem Bewegen in der Kamera (deinem Canvas) liegen. Ein Tile hat bei dir doch sicherlich eine Größe oder und um wie viele Pixel man sich seit der letzten Bewegung bewegt hast weißt du vermutlich auch. Somit kannst du ermitteln an welcher Stelle deiner Tilemap du anfangen musst mit dem zeichnen. Der Rest wird einfach nicht gezeichnet, er bleibt aber an seiner Position bestehen.

Du brauchst also eine Art Welt-Koordinate für deine Tilemap, die sicherlich größer ist als das Canvas und die lokalen Koordinaten.
 

Mofi

Bekanntes Mitglied
Ok ich verstehe, allerdings weiß ich nicht, wie ich das mit klassischem Java2D lösen soll.

Ich habe ja lediglich ein Canvas, auf dem ich zeichne, dass ist ja quasi meine "Kamera".
Bewegen kann ich das Canvas ja nicht aber ich kann das bewegen, was gezeichnet wird und das sind doch die Tiles.

Also irgendwie stehe ich da gerade aufm Schlauch :D

Ich bin mir nicht sicher was du genau mit Canvas meinst, aber ich geh mal davon aus, dass du mit einem Graphicspbjekt zeichnest? Wenn ja gibt es eine Möglichkeit die "Kamera" (ich nenn es normalerweise Viewport...Liegt aber daran, dass ich sowas meist in Englisch lese) zu bewegen.
Bei Graphics gibt es eine Methode die heißt translate. Graphics (Java Platform SE 6)
Der Methode übergibst du die neuen Koordinate von dem was nun sichtbar sein soll. Dadurch kannst du die Kamera quasi hin und herschieben. Allerdings muss man das ein wenig üben, da es ein wenig tückisch sein kann :)

Bei mir hat das eine Weile gedauert bis ich den richtigen Dreh raus hatte ^^

Aber ich zum Beispiel muss meine Tiles nicht bewegen sondern nur die Kamera/Viewport damit sich mein Männchen auf der Karte weitläufig bewegen kann :)
 

Raziell

Bekanntes Mitglied
Ich bin mir nicht sicher was du genau mit Canvas meinst, aber ich geh mal davon aus, dass du mit einem Graphicspbjekt zeichnest?

Also ich extende Canvas und zeichne mittels aktivem Rendering.

Meine draw Methode sieht in etwa so aus:

Java:
    private void drawObjects() {
        checkBackbuffer();
 
        Graphics g = volatileImage.getGraphics();
        render(g);
        g.fillRect(0,0,Configuration.FRAME_WIDTH, Configuration.FRAME_HEIGHT);
        g.dispose();
 
        Graphics g2 = bufferStrategy.getDrawGraphics();
        g2.drawImage(volatileImage, 0, 0, Configuration.FRAME_WIDTH, Configuration.FRAME_HEIGHT, this);
        g2.dispose();
        bufferStrategy.show();
    }

Java:
    public void render(Graphics g) {
        renderer.draw(g);
    }

Das heisst ich muss theoretisch anstatt die Tiles zu bewegen die translate() Methode vor dem zeichnen
mit den jeweiligen x/y Koordinaten aufrufen, wenn ich das richtig verstehe?
Und wie ist das mit der Bewegung soll dann beispielsweise die Spielerfigur wirklich bewegt werden?
 
Zuletzt bearbeitet:

Mofi

Bekanntes Mitglied
Mal davon abgesehen, dass ich nicht verstanden habe, was du da machst (Also warum du das eine Graphicsobjekt vernichtest um danach eine neues zu erstellen). Darum gehts ja ansich auch nicht ;)

Bei mir bewegt sich meine Figur auf der Karte und die Kamera/der Viewport bewegt sich in einem bestimmten Verhältnis dazu.. Ich hab es so gemacht, dass die Figur zum Beispiel immer mittig ist solange sie nicht zu nah am Rand ist (Dann bewegt sich die Kamera nicht und die Figur bewegt sich quasi alleine an den Rand)

Ansonsten ja die translate Methode wird vor dem Zeichnen aufgerufen. Ich berechne halt noch vorher inwiefern sich die Koordinaten ändern müssen. Man muss nur halt bei Sachen aufpassen die sich mit bewegen sollen (wie irgendwelche Anzeigen)
 

Raziell

Bekanntes Mitglied
Mal davon abgesehen, dass ich nicht verstanden habe, was du da machst (Also warum du das eine Graphicsobjekt vernichtest um danach eine neues zu erstellen). Darum gehts ja ansich auch nicht

Dazu habe ich mir verschiedene Beispiele aus dem Internet bzw. hier aus dem Forum angeschaut.
Ob das jetzt die optimale Lösung ist, weiss ich nocht nicht allerdings funktioniert es bisher ganz gut.
Ich werde mir das aber auch noch einmal genauer anschauen und ein bisschen rumprobieren.

Bei mir bewegt sich meine Figur auf der Karte und die Kamera/der Viewport bewegt sich in einem bestimmten Verhältnis dazu.. Ich hab es so gemacht, dass die Figur zum Beispiel immer mittig ist solange sie nicht zu nah am Rand ist (Dann bewegt sich die Kamera nicht und die Figur bewegt sich quasi alleine an den Rand)

Ansonsten ja die translate Methode wird vor dem Zeichnen aufgerufen. Ich berechne halt noch vorher inwiefern sich die Koordinaten ändern müssen. Man muss nur halt bei Sachen aufpassen die sich mit bewegen sollen (wie irgendwelche Anzeigen)

Ich verstehe.

Am besten ich probiere das heute Abend wenn ich Zeit habe einfach mal aus.
Ist ja prinzipiell nicht viel Umbauaufwand.

Bis hierhin vielen Dank, ich melde mich dann wenn ich soweit bin und nicht weiterkomme :D
 

Raziell

Bekanntes Mitglied
Hi,
habe das ganze jetzt mit bewegen des Viewports realisiert.
Klappt auch soweit alles ganz gut.

Eine andere Frage ist:
Kennt jemand das Problem, dass es beim diagonalen leicht flackert/ruckelt/lagt (schwer zu beschreiben)?
Beim vertikalen/horizontalen Bewegen ist alles in Ordnung.

Danke
 

Mofi

Bekanntes Mitglied
Hi,
habe das ganze jetzt mit bewegen des Viewports realisiert.
Klappt auch soweit alles ganz gut.

Eine andere Frage ist:
Kennt jemand das Problem, dass es beim diagonalen leicht flackert/ruckelt/lagt (schwer zu beschreiben)?
Beim vertikalen/horizontalen Bewegen ist alles in Ordnung.

Danke

Nein bei mir läuft alles flüssig. (Also bei meinem Code ^^)
Vielleicht hast du zu viele Bilder die du gleichzeitig malst? Ohne Code wird man das nicht sagen können :)
Bei mir hatte es beispielsweise leicht geflackert (allerdings in alle Richtungen und nur auf langsamen Rechnern), weil ich die eine Methode nicht 100% richtig geschriebene hatte bzw es jetzt etwas umgeschrieben habe und es nun besser läuft.

Manchmal sind es einfach Kleinigkeiten die auch eher auf älteren Rechner auffallen, wollt ich damit sagen :)

Bei sowas spielt auch immer eine Rolle, wie viele Bilder gemalt werden. Hier ist eben wichtig, dass wirklich nur das gemalt wird, was man auch sieht und nicht mehr.
 

Raziell

Bekanntes Mitglied
Also bei mir läufts von Computer zu Computer sehr unterschiedlich.
Manchmal ruckelt es garnicht manchmal heftig.
Ich poste einfach mal ein bisschen Code aus unterschiedlichen Klassen zusammengefasst:

Java:
private void init() {
		createBufferStrategy(2);
		bufferStrategy = getBufferStrategy();

		last = System.nanoTime();
		Thread t = new Thread(this);
		t.start();
	}

	@Override
	public void run() {
		long lastLoopTime = System.currentTimeMillis();

		while (isVisible()) {
			try {
				delta = System.currentTimeMillis() - lastLoopTime;
				lastLoopTime = System.currentTimeMillis();

				moveObjects();
				drawObjects();
				checkKeys();
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	private void drawObjects() {
		Graphics2D g = (Graphics2D) bufferStrategy.getDrawGraphics();
		g.clearRect(0, 0, getWidth(), getHeight());
		render(g);
		g.dispose();
		bufferStrategy.show();
}

	public void moveObjects() {
		renderer.getViewport().move(delta);
		gameObjetcs.getPlayer().move(delta);
	}

	public void draw(Graphics g) {
		g.translate((int) viewport.getX(), (int) viewport.getY());
		map.draw(g, viewport);
		player.draw(g);
		viewport.draw(g);
		g.setColor(Color.CYAN);
		g.drawString("FPS: " + Long.toString(fps), (int) viewport.getWorldX() + 20, (int) viewport.getWorldY() + 20);
	}

	public void move(long delta) {
		// update the location of the entity based on move speeds
		x += (delta * dx) / 1000;
		y += (delta * dy) / 1000;
	}

	public void setHorizontalMovement(double dx) {
		this.dx = dx;
	}

	public void setVerticalMovement(double dy) {
		this.dy = dy;
	}

Meine Tiles sind 100x100px große JPGs und es werden nur die gezeichnet, die auch im sichtbaren Bereich liegen.

Ich hoffe jmd. hat ne Idee.
 

Fu3L

Top Contributor
Daran wird es bei Thread.sleep(10) wohl eher nicht liegen... Das wären, wenn das Berechnen und Zeichnen vernachlässigbar kurz dauert, 100 FP/s. 60 FP/s kann ein Mensch schon eigentlich nicht mehr als Bilderfolge wahrnehmen. Bei einer Schlafdauer von 5 ms wird Java wohl auch nicht mehr so schnell neu zeichnen.
Für genauere Aussagen bräuchte man meine ich auch Einsicht in die Map-Klasse.
 

Raziell

Bekanntes Mitglied
Hier die Map Klasse:

Java:
public class Map {

	private List<Tile> tiles = new LinkedList<Tile>();
	private int width;
	private int height;
	private int horizontalTileCount;
	private int verticalTileCount;

	public void draw(Graphics g, Viewport viewport) {
		for (Tile tile : getTiles()) {
			if (tile.intersects(viewport.getWorldX(), viewport.getWorldY(), Configuration.FRAME_WIDTH, Configuration.FRAME_HEIGHT)) {
				tile.draw(g);
			}
		}
	}

	public void addTile(Tile tile) {
		getTiles().add(tile);
	}

	public List<Tile> getTiles() {
		return tiles;
	}

	public void setTiles(List<Tile> tiles) {
		this.tiles = tiles;
	}

	private int getHorizontalTileCount() {
		return horizontalTileCount;
	}

	public void setHorizontalTileCount(int horizontalTileCount) {
		this.horizontalTileCount = horizontalTileCount;
	}

	private int getVerticalTileCount() {
		return verticalTileCount;
	}

	public void setVerticalTileCount(int verticalTileCount) {
		this.verticalTileCount = verticalTileCount;
	}

	public int getHeight() {
		if (height <= 0) {
			height = getVerticalTileCount() * Configuration.TILE_HEIGHT;
		}
		return height;
	}

	public void setHeight(int height) {
		this.height = height;
	}

	public int getWidth() {
		if (width <= 0) {
			width = getHorizontalTileCount() * Configuration.TILE_WIDTH;
		}
		return width;
	}

	public void setWidth(int width) {
		this.width = width;
	}

}

Meine Vermutung ist das die Berechnung des delta bzw. der Bewegungsgeschwindigkeit nicht optimal ist und es daher von System zu System unterschiedlich gut läuft.

Danke
 

Fu3L

Top Contributor
Versuch mal nicht mit System.currentTimeMillis(), sondern System.nanoTime(). Dann musste natürlich die Werte durch 1.000.000 teilen, ist aber wesentlich genauer. (Und ich meine nicht, weil du theoretisch auf die Nanosekunde genau messen kannst, sondern weil currentTimeMillis() nicht den besten Ruf hat.)

Ansonsten bitte auch noch die Klasse Tile.
 

Raziell

Bekanntes Mitglied
Versuch mal nicht mit System.currentTimeMillis(), sondern System.nanoTime(). Dann musste natürlich die Werte durch 1.000.000 teilen, ist aber wesentlich genauer. (Und ich meine nicht, weil du theoretisch auf die Nanosekunde genau messen kannst, sondern weil currentTimeMillis() nicht den besten Ruf hat.)
Könnte man mal probieren.

Hier die Tile Klasse:
Java:
public class Tile extends Rectangle2D.Double implements Drawable {

	private static final long serialVersionUID = 1L;

	private BufferedImage image;
	private String location;

	public Tile(BufferedImage image, int width, int height, int x, int y, String location) {
		this.image = image;
		this.width = width;
		this.height = height;
		this.location = location;
		this.x = x;
		this.y = y;
	}

	@Override
	public void draw(Graphics g) {
		g.drawImage(image, (int) x, (int) y, (int) width, (int) height, null);
		// g.setColor(Color.GRAY);
		// g.fill3DRect((int) x, (int) y, (int) width, (int) height, true);
	}

	public String getLocation() {
		return location;
	}

}
 

Raziell

Bekanntes Mitglied
Also ich habe es jetzt mit Nanosekunden probiert und konnte keinen Unterscheid feststellen.

Ich habe die aktuelle Version(mit Nanosekunden) mal hochgeladen:
Share-Online - dl/PXWBP4VLI5J

Wer möchte kann sie ja mal kurz laufen lassen und mitteilen ob und wie es läuft.

Danke
 

Fu3L

Top Contributor
Also ich kann leider nicht erkennen, woran die Ruckler liegen könnten. Bei mir läuft es jedenfalls wunderbar flüssig.

PS: Schöner Hintergrund ;)
 

Raziell

Bekanntes Mitglied
Erstmal danke das dus getestet hast.

Bei mir unter Win7 läuft auch alles super aber beim letzten Test an an der Arbeit unter Ubuntu konnte ich ein leichtes ruckeln/flackern feststellen.

Wäre super wenn es sich nochmal der ein oder andere anschauen könnte und mir ein kurzes Feedback geben kann wie es läuft.

Danke
 

Ähnliche Java Themen

Neue Themen


Oben