Map Tiles (4) – XNA Program

August 13th, 2007

The last three posts discussed map tiles in some detail but without getting into programming. First, we described terminology surrounding map tiles. Then we setup a sample TileMap from an image of the world. And finally, we covered some “tile math” examples. Now we will wrap everything up by developing a mapping program that uses map tiles.

The program we created was built off one of our previous projects (see World Mapper parts 1,2,3) and, as always, the XNA framework. This project turned out to be much more complex than any of our previous programs so it would be impossible to cover all the source code in detail. Instead, this post will provide an overview of the program’s structure and then provide the source code for you to download and explore on your own.

Program Structure

Below is a graphic of all the classes used in the program with our main class named WorldMapper.cs.

World Mapper Classes

We designed three classes to handle the maps tiles – a TileMap, TileSet, and Tile class. The main program only interacts with the TileMap class and the creation of TileSets and Tiles is done automatically.

We also needed a separate class to keep track of where the user is looking on the map. This concept of a “viewport” or “camera” is commonly used in game programming so the program knows which tiles need to be drawn on-screen. It also tracks which TileSet the user is viewing. The class was named GeoCamera2D and contains methods to pan, zoom, and recenter the map. Remembering the discussion in the last post on tile coordinates versus geographic coordinates, our camera stores all it’s location information in tile coordinates like 0,0 or 2.4, 1.3 and relies on the current TileSet class to convert the location to longitude/latitude coordinates when needed.

Both the Keyboard and Mouse classes have been covered in previous posts so we will skip those now. Some of the code was updated but the main functionality remained the same.

C# Programming

Our map tilling program follows the standard XNA structure but with the addition of our tile mapping classes. Some of the source code is covered below.

We first create several variables to store our new classes and constants to define some pixel parameters:

shTileMap _tileMap;
shGeoCamera2D _camera2D;
 
const int _mapWidth = 800;
const int _mapHeight = 400;
 
const int _tileWidth = 800;
const int _tileHeight = 400;

In the constructor, we initiate our TileMap class with some general information, including the extent of the entire map (in this case, the entire world). The camera class is also initiated:

_tileMap = new shTileMap("WorldMap", _tileWidth, _tileHeight, -180, -90, 180, 90);
_camera2D = new shGeoCamera2D(_mapWidth, _mapHeight, _tileWidth, _tileHeight);

Next we have to define each of our three TileSets (see part 2 for more information). We first use the AddTileSet method to create a new TileSet with information on it’s zoom level (0), and tile coordinate extent (0,0 to 1,1).

//--> setup TileSet 0
shTileSet newTileSet = _tileMap.AddTileSet(0, 0, 0, 1, 1, _graphics.GraphicsDevice);

We then have to define some conversion parameters (see part 3) to enable the TileSet to convert between tile coordinates and geographic coordinates.

newTileSet.DefineConversionParameters(new Vector2(-180, -90), 360, 180);

We repeat the TileSet creation twice more for the other TileSets.

In the Update method, we check for mouse and keyboard clicks. A mouse click simply shows the user the geographic coordinates of where they clicked on the map.

// check if a mouse click has occured
_mouse.Update();
if (_mouse.LeftButton == ButtonState.Pressed)
{
    //--> convert click position to grid position
    Vector2 gridPos = _camera2D.ConvertScreenPos2GridPos(_mouse.X, _mouse.Y);
 
    //--> convert grid position to real world position
    Vector2 realPos = _tileMap.ActiveTileSet.ConvertGridPos2RealPos(gridPos.X, gridPos.Y);
 
    //--> update last click position for display on-screen
    _lastClickPos = realPos;
}

World Mapper Screenshot

Arrow keys will pan around the map, the Z key zooms in, and the X key zooms out. One of the keyboard checks looks like this:

// check if a key has been pressed
_keyboard.Update(gameTime);
if (_keyboard.IsNewKeyDown(Keys.Left))
{
    _camera2D.PanLeft(_tileMap.ActiveTileSet.MinCol);
}

Every time a pan occurs, we send as a parameter the boundary being approached by the pan to ensure the user can’t go outside the bounding box of the map.

Finally, in the Draw method, displaying the map takes only one line:

_tileMap.Draw(_spriteBatch, _camera2D);

Conclusion

The code involved in this project is rather complex and represents only one approach to making a tile-based mapping program. The objective was to create a framework that could easily be applied to other TileMaps of different sizes, extents, and tile counts. There are aspects of the program that would need to be addressed though before it succeeded in that respect. For example, our program pre-loads all 22 map tiles for convenience but this wouldn’t be practical for a TileMap that has hundreds of tiles. Some form of a “tile cache” would need to be implemented that only loaded and stored tiles within the user’s view.

The source code and compiled program are available to download below. The source code requires the XNA Game Studio Express and the program download requires at minimum the XNA runtime redistributable to run.

Download Source Code
Download Program