2d libgdx - drawing only what camera sees

Hi,
I’ve created some code that draws a random procedurally generated dungeon - all good, can scroll around etc. Problem is my drawMap method:


public void drawMap(SpriteBatch batch, Texture img, float width,
			float height) {
		for (Tile[] t : tiles) {
			for (Tile tt : t) {
				if (tt.visible)
					batch.draw(img, tt.x, tt.y, width, height);
			}
		}
	}

Tiles is defined as:


public List<Tile[]> tiles;

It just draws the whole map, now I have a camera and it’s position, thus must be a way of just drawing what it sees. The screen width is 1024/16 tiles and 768/16 tiles high so
really should just be drawing that amount. ??? ???

Any help is appreciated.

Thanks,
Steve

When it comes to tile maps, not only do you not need to draw what you can’t see, you don’t even have to test visibility.

Knowing the:

  • Tile Size
  • Window Size
  • Camera Position

You can calculate directly what tiles you need to start drawing from, and where to draw to, it’s simply a few basic math operations. This is pretty much as simple as adding the mouse position to the camera position and dividing by the tile size. :slight_smile:

On another note, I would advise against have a type like List<Tile[]> instead of Tile[][] or List<List>, unless those two dimensions deal with different semantics.

Using the cameras position, and the viewportHeight/-Width you could create a Rectangle for the visible area and then simply check for each of your Tiles if it overlaps the cameras Rectangle -> camRectangle.overlaps(new Rectangle(tt.x, tt.y, width, height))

Thanks guys,

The List<Tile[]> is I guess quite slow, prob best as Tile[][] - going convert to that.

As always, great help and great advice :slight_smile:

Updated to use 2d array instead of a list collection, a lot easier.

Just to put in the code so we only draw what we can see and not the whole map!

Thinking of having a layer of clouds above the dungeon map, is it possible to add a layer and allow it to scroll slower than the map below?

Thanks again,
Steve

Poorly not, they did some research in the 80s but found no solution.

Simply scale the camera position with a value smaler 1.
But I guess that would look wrong, use the same camera and add the velocity to the clouds position with every update.

You may not need to have a velocity or position on the clouds. If they are not interacting with the logical game world, they can just be placed mathematically in the rendering process.

This is called Parallax scrolling. I’m not sure how it’s generally implemented, though I imagine you can achieve it by scaling your view matrix. If you wanted clouds to move around independent of the camera, then you would need to store a position for them, or you could calculate it as a function of time.

I suspect clouds might be more annoying to the user than appreciated, but I have no experience in that regard.

Thank you,

Yes I’ve done parallax scrolling before using Cocos2D on iPhone, guess just draw random ones that move across the screen now and then.

I’ve done the code to just draw what camera sees, was quite easy, basically: (so now draws a maximum of just 64*48 tiles each frame)


		int camX = (int)camera.position.x - MyGdxGame.SCREENWIDTH / 2;
		camX /= MyGdxGame.TILEWIDTH;
		int endX = MyGdxGame.SCREENWIDTH / MyGdxGame.TILEWIDTH + camX;
		
		int camY = (int)camera.position.y - MyGdxGame.SCREENHEIGHT / 2;
		camY /= MyGdxGame.TILEHEIGHT;
		int endY = MyGdxGame.SCREENHEIGHT / MyGdxGame.TILEHEIGHT + camY;
		
		for (int y = camY; y < endY; /*MyGdxGame.ROWS;*/ y++) {
			for (int x = camX; x < endX; /*MyGdxGame.COLS;*/ x++) {
				if(tilesArray[x][y].visible)
					batch.draw(img, tilesArray[x][y].x, tilesArray[x][y].y, width, height);
			}
		}


Although, I suspect the if condition above could be removed somehow…

I’d like to implement some form of lighting on the dungeon map - thus, tiles that are say 4 tiles away from player are darker, is this some sort
of texture lighting? Sorry, not got a lot of openGL experience. Maybe the setColor in SpriteBatch could be used?

Thanks,
Steve

Yes, you could use the setColor function in the SpriteBatch. I believe there is also a ‘setTint’ function, but I don’t know how they are different. Do you want the lighting to have a smooth transition over the edges of tiles, or do you want a more “blocky” tile by tile lighting? (think early Minecraft, that’s the only comparison I can think of).

Hi, blocky transition :slight_smile:

I guess it is a matter of getting the distance of how the tiles are from the player and setting the setColor method?

Thanks

[quote=“steg90,post:8,topic:53663”]
You are right, your code should end up being something like:

int firstTileInViewX = /* Top left position of camera converted to a tileX index */;
int firstTileInViewY = /* ^ */;

int lastTileInViewX = /* ^ */;
int lastTileInViewY = /* ^ */;

for (int y = firstTileInViewY; y < lastTileInViewY; y++)
	for (int x = firstTileInViewX; x < lastTileInViewX; x++)
		batch.draw(img, tilesArray[x][y].x, tilesArray[x][y].y, width, height);

The if condition checks if the tile is visible, if so it gets drawn - the visible isn’t a property if it is visible to the camera :slight_smile:

Anyone done any dungeon’s where the screens are loaded from the hard drive at certain points - basically remove those screens
that are far away out of the 2d array and then load new ones in - almost giving infinite amount of screens.

Thanks,
Steve