Tile Maps

Introduction

This tutorial is hopefully a quick overview of how to use a simple tile-map for detecting when game entities hit walls and preventing them walking through them. The tutorial and code makes the assumption that the reader understands about Java 2D accelerated rendering and has some basic Java knowledge.

The final game can be see here. The complete source for the tutorial can be found here. Its intended that you read through this tutorial with the source code at your side. The tutorial isn’t going to cover every line of code but should give you enough to fully understand how it works.

Context highlighted source is also available here:

Game.java

Entity.java

Map.java

Disclaimer: This tutorial is provided as is. I don’t guarantee that the provided source is perfect or that that it provides best practices.

Tile Maps

What are they?

A tilemap is essentially an grid of cells where the value in the cell indicates what should be at that location. So we we might define “1” as grass and “2” as mud and define a map using these values describing an area in the game.

The first nice thing about tilemaps is they’re simple. Its very easy to implement a tilemap using a standard two dimensional array. You simply fill it full of values and access them using x/y coordinates.

The second nice thing about tilemaps is that they often save space. You only define the properties of a particular tile type once. You then reuse that set of properties over and over by referencing it form the tilemap.

The last nice thing (that I can think of) about tilemaps is that they’re quick to access. Given a particular location on the map it can be very fast to get to the properties associated with that area. Normally its simply a matter of accessing into an array based on the x/y location of an entity.

How do we use them for simple collision?

So, why are they are good for collision. Well, lets look at your standard maze style game. You’ve got a grid full of cells that are either blocked by a maze wall or free to move through. What are you going to do? Using more advanced techniques you might define a bounding area for each cell and then check your entity against every one! Yuck. Thats a lot of checking! Normally you’d do a broader check first, work out which cells to check first and hence do less bounding shape checks. Tilemaps can be used to perform this broader check – however, in many cases you don’t need to perform the more complicated bounds check at all :)

How does a tilemap help you? Well, instead of checking against every block, you can simply check against the properties for the blocks that are close to your entity. Of course, theres a trade off, its not as accurate as full bounds checking – but normally its more than enough. Lets look at this in more detail:

The diagram above shows a simply tile map with a couple of entities (red blobs). The blue cells represent blocked areas and the white cells clear ones. Around the entity we have a simple box collision bounds. The green points mark locations that we’ll check for collision. So, the entity in the top left is in an invalid location (its partly into the wall). The entity is the middle is valid.

Now, how many points you check and where the points are specific to the application – and obviously this isn’t perfect collision for everything. However, when entities are simply moving round a map at slow speeds even this 4 point checking can work fine (as we’ll see in the tutorial code). Note that with this simple corner checking system the entity bounding box needs to be smaller than the cells of the grid. This is better in some cases because instead of checking reasonably complicated bounds checks on bunch of cells, we check explicitly the ones we want to in a very quick tile map based way.

Tutorial Classes

The tutorial code is built up of 3 classes. Lets just go over what they’re for.

Game

The game class is our central hub for everything. It handles window creation and the game loop. Its also responsible for creating the map and player.

Map

The map is unsurprisingly our tile map. It contains a data array of values indicating whether each cell is blocked or not. The map is also responsible for rendering itself using simple rectangles.

Entity

The entity class represents things that are wandering the map colliding with the walls. In this example this is simply the player. The entity class is responsible for rendering a sprite representing itself.

Collision Logic

Let’s look at how the collision code works, starting in Game we see this:

public void logic(long delta) {
    float dx = 0;
    float dy = 0;
    if (left) {
	dx -= 1;
    }
    if (right) {
	dx += 1;
    }
    if (up) {
	dy -= 1;
    }
    if (down) {
	dy += 1;
    }

    if ((dx != 0) || (dy != 0)) {
	player.move(dx * delta * 0.003f,
  		    dy * delta * 0.003f);
    }
}

Elsewhere we’ve recorded which keys are pressed, we use this to work out which way the player wants their character to move. We then ask the player to move based on the direction and the amount of time thats passed. Player.move() looks like this:

public boolean move(float dx, float dy) { float nx = x + dx; float ny = y + dy; if (validLocation(nx, ny)) { x = nx; y = ny; return true; } return false; }

Doesn’t do much does it? The actual work is being performed in validLocation(). This method simply works out where the entity will be if the move is performed. It checks if this is a valid location (which performs the collision checks). If it is a valid location it moves the player, if not it doesn’t. This way you can’t move the player into walls.

Right, so the important bit is in validLocation() which looks like this:

public boolean validLocation(float nx, float ny) {
	if (map.blocked(nx - size, ny - size)) {
		return false;
	}
	if (map.blocked(nx + size, ny - size)) {
		return false;
	}
	if (map.blocked(nx - size, ny + size)) {
		return false;
	}
	if (map.blocked(nx + size, ny + size)) {
		return false;
	}

	return true;
}

Again, this is pretty simple right? We go away to the map and check the four points around our player that represent its collision box based on the “size” associated with this entity. If any of the points are blocked then this isn’t a valid location – otherwise it is.

Now you can see how this might cause problems in certain cases but for simple situations its fast and most importantly simple. Finally, lets just finish off the picture by seeing what happens in the Map.blocked() method:

public boolean blocked(float x, float y) {
    return data[(int) x][(int) y] == BLOCKED;
}

Really simple and really quick! We take the floating point values passed in (which are in grid cells) – cast them down to ints which instantly gives us the grid cell we need to check. Check the cell and return yes or no.

Thats it for code. The tutorial code does some other bits and pieces to create the window, capture key input and render the scene but this has all been covered in the space invaders tutorial.

Conclusion

Tilemaps make sense for certain situations. In most cases they’re based combined with other techniques. For instance, it might make sense to use the tilemap to work out which cells need to be checked and then using bounds checking to work out the actual collision.

Tilemaps don’t fit in every game but in many cases they are a powerful tool for both rendering and collision detection.

Suggested Extensions

Implement Sliding – If you’ve tried the tutorial example you’ll find that you don’t slide along walls like you do in so many games. This is pretty easy to implement by just apply the x and y axis movement separately.

Tile based Rendering – Implement a system to rendering a different sprite based on the value in the cells, giving a nice looking map

References

Tutorial written and coded by Kevin Glass

Java is available from http://www.java.net

Leave a Reply

Your email address will not be published. Required fields are marked *


eight − 4 =