libgdx, box2d, tiled maps: full working example, part 1/3

This will be a 3 part post demonstrating how to make a simple side scrolling game using libgdx (official project blog), a cross-platform library that allows you to simultaneously build for the desktop and for Android devices.

Updated 2011-09-19: Updated to use libgdx 0.9.2. No code changes required.

Updated 2011-09-18: Updated to use libgdx 0.9.1 (for real this time…) which meant renaming the pack file, TiledMapRenderer to TileMapRenderer, moving and renaming the tiles and pack file (and reindexing) due to new requirements from libgdx.

Updated 2011-06-12: Fixed some graphical glitches reported by Ed (in part 2′s comments). Thanks for all of your help Ed! Additionally, the tutorial is now based on libgdx 0.9.1 (it might have been libgdx 0.9.0 at the time, sorry folks) instead of an arbitrary nightly build, and it uses a modified TexturePacker in place of “manual” pack file generation.

This example is for anyone interested in making Android games, but is unsure where to start. By the end of part 3 you will have a basic Mario-like side-scrolling game with realistic physics, and (hopefully) you will be able to take this example and build something fun.

I’ve learned a few things since writing my previous post on using Tiled Map Editor with libgdx that I’ll apply in this series, things that make building a simple tiled-map game a lot easier. All code found here is released under the Apache 2.0 license, meaning, basically, you can use it for whatever you want. (IANAL, read the license for details.)

This is a much more involved tutorial than you have seen before on this site. There are a lot of different components used here and I tried to avoid cutting corners during explanations.

Post index

  1. Creating map tiles and the game map
    1. Where to begin
    2. Creating map tiles
    3. Creating the game map
    4. Saving the map data to the projects
    5. Loading and viewing the map in-game: TiledMapHelper
    6. Enable scrolling through the map by touch
    7. Summary
  2. Integrating box2d
    1. Define the collision boundaries for each type of map tile
    2. Introducing TileCollisionTool
    3. Programmatically configure the map’s collision boundaries
    4. Add a player character sprite
    5. Navigate the map with the player character
    6. Summary
  3. Final touches: Scoring, lives, game over [coming soon]
    1. Add breaking blocks
    2. Add score and score display
    3. Add player death and a game over screen
    4. Hey, you’ve got a game
    5. Summary

Where to begin

This series was written with the assumption that you have read through all of the beginner libgdx tutorials, up through Projection Viewport Camera. libgdx has a tutorial on working with tiled maps, however that tutorial does things a little differently than what you’ll find in this series.

The box2d library includes a JNI wrapper for box2d, a “rigid body simulation library”, more commonly known as a physics library. It is pretty easy to use, although there are some box2d idiosyncrasies that you need to be aware of, and I will point those out in part 2. The box2d manual is worth reading, although its examples are all written for C++.

Your first steps are to create your libgdx project pair, named “JumperTutorial” and “JumperTutorialAndroid”. You can name them whatever you want, of course, but this series will use these names.

Next, download and install Tiled Map Editor and a graphics program before you begin. I can easily recommend using either Pixen on Mac OS X or GIMP on Windows (GIMP’s interface has come a long ways in recent years, I really like it).

Download the JumperTutorialProjects.zip projects used in part 1 and follow along as its components are explained. (14.4MB)

Creating map tiles

Your map will be made up of small, identically sized tiles, all eventually stored in a single image file, which I’ll refer to as “the tile image”. You can create this image file in any program (including MS Paint or similar), but some programs have features that make drawing easier.

Before you begin creating your graphics, take a moment to draw out a diagram of the sort of level you want to create. This will help you figure out how many different tiles you’re going to need to construct a whole map, and that will help you determine the size of your tile image.

Rough sketch of what a game map might look like

Based on my sketched map I’ll need tiles for the open air, a regular ol’ ground tile with a floor on top, a ground tile with no floor (that will go under the floor tile), a set of three tiles for the stairs (left-to-right and right-to-left stairs and a tile to sit between), two more ground tiles with left and right facing cliffs (and two more tiles to continue those cliffs downward), and six tiles for mountains in the background (left-to-right ramp and right-to-left ramps, a solid mountain block, and three for the mountain top). I’m skipping the clouds and exclamation-point blocks for now, as they will be handled separately as sprites (in part 3). I’m also skipping the pipes (part 4? if there is interest). That makes 16 tiles in total.

It’s important to decide early on what size you want each of your tiles to be. Consider the size of your target platform’s screen and how much detail you want to work in to each tile. For this example, I have chosen to make the tiles 32×32 pixels. On a ~400×900 pixel screen, the game will show around 12×28 tiles on screen at a time, which will work quite well for a Mario-clone.

The 16 different tiles will be saved to separate files, and then will be combined in to one large tile image file using a customized version of libgdx’s TexturePacker. TexturePacker combines the images together and creates a separate “pack” file that specifies where each tile is located within the tile image. TexturePacker also adds a buffer around the edge of each tile. This buffer seems to come in handy when the map display is automatically resized just a tiny bit. (I have not personally verified this.)

Some tiles next to part of the packed tile set

In the JumperTutoralProjects.zip file you’ll find the modified TexturePacker source and project. You can use Eclipse to export it as a runnable jar (File menu -> Export -> Runnable Jar). The modifications include support for numbering the tiles by their position within the tile image, rather than based on the tile filename, and support for including the level name inside the resulting pack file. These modifications together make the pack file work unmodified in the example code. Additionally, the TexturePacker code has been configured to add a 2 pixel buffer around images.

Important note: The resulting image and its pack file need to be named or renamed to “level.png” and “level packfile” (with a space) once you are done and before you create a map in Tiled Map Editor.

Steps for using the modified TexturePacker for this project:

  1. Create an input directory to hold your individual and an output directory to hold the tool’s output
  2. Create all of your individual tile images and save them to the input directory
  3. Open a command prompt and run java -jar TexturePacker.java inputdirectory outputdirectory level where “level” is the name of the level file you’ll create later on, without its extension.
  4. Done. Inside the output directory you’ll see a single, larger png file and the “pack” file.

If you’re curious, go ahead and open the pack file in any standard editor. You shouldn’t need to make any changes in the file, however. In the file you’ll see the top left corners of each tile (xy) and the tile’s size and index values.

Creating the game map

Start up Tiled Map Editor and choose “New Map” in the File menu. Enter a map size at least a few times as wide as and a bit taller than your target screen, so you’ll have something to scroll around. I picked 60×15 (1920x480pixels) for the example map. Be sure to select “Orthogonal” under orientation and use the correct tile size (I’m using 32×32 as before).

Tiled Map Editor “New Map” dialog

In the Map menu, choose “New Tileset”. In the dialog that pops up, pick a name for the tiles (anything will do) and browse to your tiles. Leave the “Use transparent color” box unchecked — this tutorial is not using transparencies. Set your tile width and height (32 and 32 in this example) and set the spacing to 2.

Select a tileset

Now you’re ready to start drawing the map. Tiled Map Editor works just like a pixel image editor, with a standard paintbrush tool and a fill tool. It saves to a “TMX” file that contains instructions for rendering the tiled map. You can find my sample TMX file inside the project zip file (at the top of the page).

Screenshot of a map in Tiled Map Editor

Saving the map data to the projects

The file names and directory structure used for your tiled map assets are important. Create a “data” directory in your desktop project if you haven’t already. Inside “data” create “world” and a subdirectory “level1″. For now, this game will only have one level, but it will be easy to add another level should the time come. Copy your TMX file in to that “level1″ directory and name it “level.tmx”.

Inside “data” create a “packer” directory and save your tile image there as “level.png” and its pack file as “level packfile” (yes, with a space, required by libgdx).

The main project directory structure

Loading and viewing the map in-game: TiledMapHelper

I wrote a simple, reusable class to handle all of the map loading and framing of the map within the display (through positioning of the camera). This decouples the game code from the map code and makes it easy for the game to change from level to level. The class is based on code found in libgdx’s tiled map test suite, and it only works with single-layer maps. You can find it inside the zip file (beginning of the article).

Here’s some explanation about some of the class methods

45
46
47
48
49
50
51
52
53
54
	public void render() {
		tileMapRenderer.getProjectionMatrix().set(camera.combined);
 
		Vector3 tmp = new Vector3();
		tmp.set(0, 0, 0);
		camera.unproject(tmp);
 
		tileMapRenderer.render((int) tmp.x, (int) tmp.y,
				Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), layersList);
	}

The camera’s position controls what is shown on the display. This method copies the camera’s combined projection and view matrix (in effect, its current position) to the tiled map library’s projection matrix and then sets the coordinates (x, y, w, h) that will be used to render the map to the screen.

105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
	public void loadMap(String tmxFile) {
		if (packFileHandle == null) {
			throw new IllegalStateException("loadMap() called out of sequence");
		}
 
		map = TiledLoader.createMap(Gdx.files.internal(tmxFile));
		tileAtlas = new TileAtlas(map, packFileHandle);
 
		tileMapRenderer = new TileMapRenderer(map, tileAtlas, 16, 16);
 
		camera = new OrthographicCamera(Gdx.graphics.getWidth(),
				Gdx.graphics.getHeight());
 
		camera.position.set(0, 0, 0);
	}

This method will be used when your player switches levels. It basically just takes care of things that you would otherwise have to worry yourself with, like preparing the camera and tile atlas and renderer. Note: The 16 used in the TiledMapRenderer constructor was picked arbitrarily.

Enable scrolling through the map by touch

The code in the project detects touch and drag events by recording where the event began and then measuring the horizontal and vertical distance from that point. The code will also ensure that the user cannot scroll outside of the map area. You’ll find this in the render() method of JumperTutorial.java.

116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
		if (tiledMapHelper.getCamera().position.x < screenWidth / 2) {
			tiledMapHelper.getCamera().position.x = screenWidth / 2;
		}
		if (tiledMapHelper.getCamera().position.x >= tiledMapHelper.getWidth()
				- screenWidth / 2) {
			tiledMapHelper.getCamera().position.x = tiledMapHelper.getWidth()
					- screenWidth / 2;
		}
 
		if (tiledMapHelper.getCamera().position.y < screenHeight / 2) {
			tiledMapHelper.getCamera().position.y = screenHeight / 2;
		}
		if (tiledMapHelper.getCamera().position.y >= tiledMapHelper.getHeight()
				- screenHeight / 2) {
			tiledMapHelper.getCamera().position.y = tiledMapHelper.getHeight()
					- screenHeight / 2;
		}

This code is pretty self-explanatory. The main thing to consider, that might not be immediately obvious, is that the camera is pointed at the center of the screen. This means the camera’s position will need to be constrained within an imaginary rectangle inside of the map, inset by half the screen width and half screen height all around.

Summary

You should now have what you need to make a simple map and an application to navigate around that map using your finger or mouse. It’s not quite a game. Where to go from here? You could, if you like, make a few more levels and add some code that uses TWL to make a menu system of sorts. You could add an intermediate map screen that allows you to select which level you want to view. Or you could add detection for clickable regions on the map that will take you from level to level.

In part 2, I’ll describe how to integrate the box2d physics library, and I’ll include some code that will make adding collision detection to your map a whole lot easier.

32 Comments

  1. Brice says:

    This is a great example. I’m looking forward to the Box2D code.

  2. Wes says:

    Great tutorial! Eagerly awaiting part 2 :D

  3. Aydin says:

    This is so inspirational! Thanks a lot for sharing,
    Cheers

  4. Jo-Ann says:

    can you help me?!!for a android code of box2D in our proposal thesis

  5. Leonhart says:

    It’s very useful!
    Thanks for sharing!

  6. Carl says:

    I was wondering whether you could write a tutorial using isometric tileset especially with adding sprites dynamically to the map?? That would be awesome.

    Totally love the tutorial.

    • dpk says:

      Thank you for your comment Carl. I hope to finish it up, one of these days…

      As to the isometric tilesets — unfortunately, at least at the time this tutorial was first started, libgdx did not support isometric maps. Maybe in the future, though!

  7. frosty says:

    This is a great example.But I want to know from where I can download the source code. Thank you!

  8. Higor says:

    Great tutorial, but I have some issues with the pack file. I don’t know why, but the index generate by the TexturePacker wasn’t right and i get the NullPointerException to load the map, so I create the pack file by my self and it works :)

  9. Mitch says:

    Just wondering what version of libGDX you are using?
    I’ve tried with the latest nightly build and the 0.9.1 build, and your examples fail to run.

    It seems in your helper class you refer to com.badlogic.gdx.graphics.g2d.tiled.TiledMapRenderer, which doesn’t exist. TileMapRenderer does exist (without the d).
    However if I change it to use that, some of the methods don’t exist such as getMapHeightPixels.
    Also the constructor to TileAtlas only takes 2 arguments not 3??

    Any hints??

    • dpk says:

      These were based on 0.9.1. I had the same problems with newer nightlies (renamed classes, method changes), for what it is worth. The library that comes with the zip file ought to work, though. I’m really looking forward to the next release because it will make some of the code I wrote unnecessary.

      Haven’t made any progress on part 3 yet. Got totally sidetracked.

      Edited to add: I’m looking in to this further. Perhaps I was mistaken, maybe I used 0.9.0. Sorry about that, I am now migrating it to 0.9.1 for sure.

  10. Karim says:

    I was using the nightlies and I was silly enough to not read the comments on the blog. It works now. My big problem now is that I have tiles labeled tile_01.png, tile_02.png, tile_03.png, ….etc and when the packer runs, the resulting pack file lists the images out of order. So if my images are 32×32 with a padding of 2, image with index of 1 will not have an xy coord of 2,2 but it may have 34,34. And the image at 2,2 will have an index of 26 or something. Which would be all good but when I load the tileset in the tiledmap editor, and grab the image in the top left corner, it says this as image 1. Unfortunately, image 1 was indexed to the image that may be at 34,34. So when I run the sample program code, I get the wrong tiles displayed! I’m not sure how to solve this problem without creating a pack file by hand. But I have 64 images so you can see the problem i’m having.

    • Fleces says:

      Did you figure out a resolution to this? I am having the same problems. I don’t want to manually redo the index myself as this could get tedious for a few hundred maps.

      • Fleces says:

        AAH! coworker just figured it out: in your tmx file change <tileset firstgid="1" to <tileset firstgid="0" – basically it was incorrectly applying the indices.

        Not sure if this is a bug or just my not knowing how to use the tools.

  11. Daniel says:

    Hi, I kept trying to make the jar file (from the TexturePacker folder), but it kept asking for a launch configuration (which had nothing). How were you able to extract the jar? Can you upload it here?

    Thanks.

    • dpk says:

      I didn’t have to do anything special, interesting. You could try making a new project and then copy the files in to place. I’ll see if I can upload the .jar, though.

  12. Lumalalelo says:

    hi !
    great tutorial, i wonder if there is an easy solution to just create an packfile from an already finished tileset(hundred of tiles) ?! :)

    • dpk says:

      I don’t know of a great one, unfortunately. I made a script at one point that built the pack file for me, but it didn’t work very well. You might be able to try making a better one. The pack file format is easy to understand.

  13. Lumalalelo says:

    oh and i get a “unable to access jarfile TexturePacker” when starting the TexturePacker jar from the cmd prompt (java -jar TexturePacker.java inputdirectory outputdirectory level)
    any ideas ?

  14. Hosein says:

    hi, thanks a lot. its so useful.
    i have a wish. can you make a tutorial for box2d?
    i know you explain box2d in chapter 2(i don’t read it, for now), but i need something like a reference. mario say examples in his tests. but it’s hard for me to understand it. i need something simple.
    tnx.

  15. Vishwanath Deshmukh says:

    Hi dpk,
    It’s really helpful.

    I’m stucked with an issue where I have to create grid of cubes eg. 32*32 grid. Here each cube has a different color and different height. So can you help me to sort it using libgdx.

  16. Vishwanath Deshmukh says:

    Hi dpk,

    I’m stucked with an issue where I have to create grid of cubes eg. 32*32 grid. Here each cube has a different color and different height. So can you help me to sort it using libgdx.

  17. Agur says:

    I just can’t get my transparent tiles to work. They always show a dark color regardles if I use proper PNG file, or fill the transparent part with pure green and then set it in tile editor… Any help? I’m actually growling at the screen right now!

  18. Moustafa Ismael says:

    I think this might be a bug, but whenever I run the TexturePacker GUI the images are reordered in a random way. For instance Tile_1 has an index of 1 but it might appear in the middle of the TextureAtlas. [ image | image | image | Tile_1 | image ]

    Problem here is that Tiled uses the image’s location as the Grid number.

    Any help?

  19. tandblekninng med laser says:

    tandblekninng med laser…

    [...]v Dude.. I am not much into reading, but somehow I got to read lots of articl qa[...]…

  20. M.Bilal says:

    I was wondering for a tutorial using isometric tileset especially with adding sprites dynamically to the map.Does libgdx support Isometric tiled map yet or not..As i have the carl reply of 2011 that libgdx does not support isometric yet..Kindly reply asap.thanks in advance

Leave a Reply