Advertisement

Rendering sprite on isometric tilemap (LibGDX)

Started by November 21, 2014 10:46 PM
4 comments, last by Glass_Knife 10 years, 1 month ago

So I'm trying to create a isometric style action-RPG type game(similar to Diablo 1 and 2). I've got an isometric tiled map to render to the screen but the problem is getting a character to render on a specific square on the game map. I've tried reading a few articles and implementing their solutions but it doesn't seem to be working. Below is my code(Keep in mind this is just a prototype).


public class MapManager {

	public static int TILE_WIDTH = 64;
	public static int TILE_HEIGHT = 32;
	
	private TiledMap map;
	private IsometricTiledMapRenderer renderer;

	public MapManager() {
		
		map = new TmxMapLoader().load("testMap.tmx");
		
		renderer = new IsometricTiledMapRenderer(map);
	}
	
	public void render(){
		renderer.render();
		renderer.setView(D2C.camera);
	}

}

public class Player {

	Vector2 pos;
	float velocity;
	Sprite sprite;
	
	
	public Player() {
		sprite = new Sprite(new Texture(Gdx.files.internal("player.png")));
		velocity = 5f;
		pos = new Vector2(0,38);
	}

}

public class D2C extends Game{
	public static OrthographicCamera camera;
	Player player;
	SpriteBatch batch;
	MapManager mapManager;

	
	@Override
	public void create () {
		batch = new SpriteBatch();
		
		player = new Player();
		
		camera = new OrthographicCamera();
		
		mapManager = new MapManager();
		
		Gdx.input.setInputProcessor(new InputHandler());
	}

	@Override
	public void render () {
		Gdx.gl.glClearColor(0, 0, 0, 0);
		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);		
		
		Vector2 pos = MapUtil.cartToIso(player.pos);

		mapManager.render();
		
		batch.begin();
			batch.draw(player.sprite,pos.x,pos.y);
		batch.end();
	}
	
	public void resize(int width, int height){
		camera.viewportHeight = height;
		camera.viewportWidth = width;
		camera.update();
	}
}

public class MapUtil {

	
	public static Vector2 cartToIso(Vector2 initialPosition){
		Vector2 iso = new Vector2();
		iso.x = (initialPosition.x - initialPosition.y)*MapManager.TILE_WIDTH;
		iso.y = (initialPosition.x + initialPosition.y)*MapManager.TILE_HEIGHT;
		return iso;
	}
}

ISO maps have the exact same grid layout as a normal 2D map. Grid placement is calculated the exact same. Only difference is the map tiles are rotated using something like


AffineTransform 

Depending on WHICH way you rotate the image ( and what degree ) will determine the sprite offset.

If you rotate all your images positive 45d, your offset would be ( ( tile width / 2 ) * 1.414 ), while height is not affected . Grid position is not affected by offset .

If you do not know how to set up grids, it would be in your best interest to try to create a simple 2D map ( without 3rd party libraries ) before attempting ISO maps again .

I cannot remember the books I've read any more than the meals I have eaten; even so, they have made me.

~ Ralph Waldo Emerson

Advertisement

I've done work before with regular grids without an API and feel like I have a solid understanding on how those work. I don't want to rotate my sprite before I draw it, I want to draw it the way it is. I'm having trouble with screen placement though as it appears that they are not placed the same (I've tried). I'll include the API code for the isometric map renderer and hopefully someone will have an idea. All I want to do is for example, I want my player to be at (10, 3) on the map, what transformations will I need to do to that vector to get the actual screen coordinates.


public class IsometricTiledMapRenderer extends BatchTiledMapRenderer {

	private Matrix4 isoTransform;
	private Matrix4 invIsotransform;
	private Vector3 screenPos = new Vector3();

	private Vector2 topRight = new Vector2();
	private Vector2 bottomLeft = new Vector2();
	private Vector2 topLeft = new Vector2();
	private Vector2 bottomRight = new Vector2();

	public IsometricTiledMapRenderer (TiledMap map) {
		super(map);
		init();
	}

	public IsometricTiledMapRenderer (TiledMap map, Batch batch) {
		super(map, batch);
		init();
	}

	public IsometricTiledMapRenderer (TiledMap map, float unitScale) {
		super(map, unitScale);
		init();
	}

	public IsometricTiledMapRenderer (TiledMap map, float unitScale, Batch batch) {
		super(map, unitScale, batch);
		init();
	}

	private void init () {
		// create the isometric transform
		isoTransform = new Matrix4();
		isoTransform.idt();

		// isoTransform.translate(0, 32, 0);
		isoTransform.scale((float)(Math.sqrt(2.0) / 2.0), (float)(Math.sqrt(2.0) / 4.0), 1.0f);
		isoTransform.rotate(0.0f, 0.0f, 1.0f, -45);

		// ... and the inverse matrix
		invIsotransform = new Matrix4(isoTransform);
		invIsotransform.inv();
	}

	@Override
	public void renderObject (MapObject object) {

	}

	private Vector3 translateScreenToIso (Vector2 vec) {
		screenPos.set(vec.x, vec.y, 0);
		screenPos.mul(invIsotransform);

		return screenPos;
	}

	@Override
	public void renderTileLayer (TiledMapTileLayer layer) {
		final Color batchColor = spriteBatch.getColor();
		final float color = Color.toFloatBits(batchColor.r, batchColor.g, batchColor.b, batchColor.a * layer.getOpacity());

		float tileWidth = layer.getTileWidth() * unitScale;
		float tileHeight = layer.getTileHeight() * unitScale;
		float halfTileWidth = tileWidth * 0.5f;
		float halfTileHeight = tileHeight * 0.5f;

		// setting up the screen points
		// COL1
		topRight.set(viewBounds.x + viewBounds.width, viewBounds.y);
		// COL2
		bottomLeft.set(viewBounds.x, viewBounds.y + viewBounds.height);
		// ROW1
		topLeft.set(viewBounds.x, viewBounds.y);
		// ROW2
		bottomRight.set(viewBounds.x + viewBounds.width, viewBounds.y + viewBounds.height);

		// transforming screen coordinates to iso coordinates
		int row1 = (int)(translateScreenToIso(topLeft).y / tileWidth) - 2;
		int row2 = (int)(translateScreenToIso(bottomRight).y / tileWidth) + 2;

		int col1 = (int)(translateScreenToIso(bottomLeft).x / tileWidth) - 2;
		int col2 = (int)(translateScreenToIso(topRight).x / tileWidth) + 2;

		for (int row = row2; row >= row1; row--) {
			for (int col = col1; col <= col2; col++) {
				float x = (col * halfTileWidth) + (row * halfTileWidth);
				float y = (row * halfTileHeight) - (col * halfTileHeight);

				final TiledMapTileLayer.Cell cell = layer.getCell(col, row);
				if (cell == null) continue;
				final TiledMapTile tile = cell.getTile();

				if (tile != null) {
					final boolean flipX = cell.getFlipHorizontally();
					final boolean flipY = cell.getFlipVertically();
					final int rotations = cell.getRotation();

					TextureRegion region = tile.getTextureRegion();

					float x1 = x + tile.getOffsetX() * unitScale;
					float y1 = y + tile.getOffsetY() * unitScale;
					float x2 = x1 + region.getRegionWidth() * unitScale;
					float y2 = y1 + region.getRegionHeight() * unitScale;

					float u1 = region.getU();
					float v1 = region.getV2();
					float u2 = region.getU2();
					float v2 = region.getV();

					vertices[X1] = x1;
					vertices[Y1] = y1;
					vertices[C1] = color;
					vertices[U1] = u1;
					vertices[V1] = v1;

					vertices[X2] = x1;
					vertices[Y2] = y2;
					vertices[C2] = color;
					vertices[U2] = u1;
					vertices[V2] = v2;

					vertices[X3] = x2;
					vertices[Y3] = y2;
					vertices[C3] = color;
					vertices[U3] = u2;
					vertices[V3] = v2;

					vertices[X4] = x2;
					vertices[Y4] = y1;
					vertices[C4] = color;
					vertices[U4] = u2;
					vertices[V4] = v1;

					if (flipX) {
						float temp = vertices[U1];
						vertices[U1] = vertices[U3];
						vertices[U3] = temp;
						temp = vertices[U2];
						vertices[U2] = vertices[U4];
						vertices[U4] = temp;
					}
					if (flipY) {
						float temp = vertices[V1];
						vertices[V1] = vertices[V3];
						vertices[V3] = temp;
						temp = vertices[V2];
						vertices[V2] = vertices[V4];
						vertices[V4] = temp;
					}
					if (rotations != 0) {
						switch (rotations) {
						case Cell.ROTATE_90: {
							float tempV = vertices[V1];
							vertices[V1] = vertices[V2];
							vertices[V2] = vertices[V3];
							vertices[V3] = vertices[V4];
							vertices[V4] = tempV;

							float tempU = vertices[U1];
							vertices[U1] = vertices[U2];
							vertices[U2] = vertices[U3];
							vertices[U3] = vertices[U4];
							vertices[U4] = tempU;
							break;
						}
						case Cell.ROTATE_180: {
							float tempU = vertices[U1];
							vertices[U1] = vertices[U3];
							vertices[U3] = tempU;
							tempU = vertices[U2];
							vertices[U2] = vertices[U4];
							vertices[U4] = tempU;
							float tempV = vertices[V1];
							vertices[V1] = vertices[V3];
							vertices[V3] = tempV;
							tempV = vertices[V2];
							vertices[V2] = vertices[V4];
							vertices[V4] = tempV;
							break;
						}
						case Cell.ROTATE_270: {
							float tempV = vertices[V1];
							vertices[V1] = vertices[V4];
							vertices[V4] = vertices[V3];
							vertices[V3] = vertices[V2];
							vertices[V2] = tempV;

							float tempU = vertices[U1];
							vertices[U1] = vertices[U4];
							vertices[U4] = vertices[U3];
							vertices[U3] = vertices[U2];
							vertices[U2] = tempU;
							break;
						}
						}
					}
					spriteBatch.draw(region.getTexture(), vertices, 0, 20);
				}
			}
		}
	}
}

Are you using a libGDX API to do the isometric rendering? I am not familiar with libGDX but I would imagine the API has gridToScreen and screenToGrid methods.

You could setup the modelView matrix and do the screen projection yourself but that will be complicated.

Edit: Looking at the API it seems that they hide everything.

This may help: http://clintbellanger.net/articles/isometric_math/

I think, therefore I am. I think? - "George Carlin"
My Website: Indie Game Programming

My Twitter: https://twitter.com/indieprogram

My Book: http://amzn.com/1305076532

Are you using a libGDX API to do the isometric rendering? I am not familiar with libGDX but I would imagine the API has gridToScreen and screenToGrid methods.

You could setup the modelView matrix and do the screen projection yourself but that will be complicated.

Edit: Looking at the API it seems that they hide everything.

This may help: http://clintbellanger.net/articles/isometric_math/

Yes, I'm using LibGDX for my isometric rendering.

I've also looked at that article and tried the methods listed but that doesn't seem to work either. I'm not at home so I can't describe exactly what happened but it wasn't desirable.

I was hoping I could figure how how they do it within the API and copy it's functionality perhaps.

Thanks.


I was hoping I could figure how how they do it within the API and copy it's functionality perhaps.

That shouldn't be too difficult. The algorithms for doing screen-to-world and world-to-screen are available and nothing but a matrix transform.

Maybe a small example of trying to put the sprite on the iso map might help?

I think, therefore I am. I think? - "George Carlin"
My Website: Indie Game Programming

My Twitter: https://twitter.com/indieprogram

My Book: http://amzn.com/1305076532

This topic is closed to new replies.

Advertisement