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

This is part 2 of a 3 part post demonstrating how to make a simple side scrolling game using libgdx and box2d. See part 1.

Updated 2011-09-19: Heh, now uses libgdx 0.9.2. No longer has its own Box2DDebugRenderer, figured out a better way using the new Matrix4 argument to renderer. Improved the controls by removing linear damping and replacing it with friction. (I am learning as I go with this, for what it is worth.) Discovered that I made a rather embarrassing mistake in leaving out rudimentary on-screen controls for Android devices. They are still pretty cruddy, though. Maybe I’ll come up with something better, or maybe someone else will and will contribute it? :) Either way is fine.

Updated 2011-09-18: Now uses libgdx 0.9.1.

In part 1 you saw how to make what is essentially a tiled map viewer. After you are through with this part you will have a character that you can move around the screen in a “natural” way, or at least a familiar way. As in part 1, download JumperTutorialProjects.zip for part 2 and follow along as the code is explained.

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

Define the collision boundaries for each type of map tile

Adding collision support to a tiled map can be done any number of ways. For example, you could create a map (using Tiled Map Editor or whatever tool) and then use a separate tool to trace over a the map, defining where the collisions should occur. You would then write some code that loads those definitions at run time. If you used this method you would need to redraw your collision boundaries every time you change your map, in effect drawing the map twice (once with the map editor and again with the collision tracer). It would be easy for the map and the collision definitions to become out of sync.

An alternative method, the one used in this example, involves describing where the collisions should occur on each type of tile and then automatically prepare the boundaries at run-time. By setting the collision boundaries per tile, you will ensure that your environmental collisions will always be consistent with your map.

For an example of this per-tile approach consider a “floor” tile, with air above and dirt below. It would have a collision boundary across its top. A ramp would have a diagonal collision boundary from the top of one side to the bottom of the other.

Highlighted collision boundaries on selected tiles, enlarged for detail

The collision boundaries will need to be saved somewhere accessible to the game. In this example, the collision boundaries will be stored in a file, with one entry per tile. Each entry will define the line segments that make up the tile’s boundaries. The boundary file will be read when the map is initialized.

A tile that has no boundaries (such as a sky tile) will have no x and y coordinate pairs. All other tiles should have at least two pairs (minimum for a line segment). Here is the boundaries file used for the tile set that was created for part 1 of this series:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1 
2 
3 0x31,0x16 0x16,16x16 16x16,16x0 16x0,31x0 31x0,31x31 0x31,31x31
4 0x0,27x0 27x0,29x2 29x2,29x31
5 
6 
7 29x0,29x31
8 
9 
10 0x31,0x0 0x0,15x0 15x0,15x16 15x16,31x16 31x16,31x31 31x31,0x31
11 2x0,2x31
12 
13 
14 0x0,31x0
15 
16 0x31,0x0 0x0,31x0 31x0,31x31 31x31,0x31
17 
18 
19 2x31,2x2 2x2,4x0 4x0,31x0
20 
21

Take a look at the tile set and compare it to what you see in this list. Tile 3, the stair-step tile in the top rightcorner of the tile image has collision boundaries all around its outer edge. Tile 7, the right-facing cliff tile, has a collision boundary that follow its edge. Some tiles have no collision boundaries at all, because they’re background pieces or pieces that can never be reached by a player.

You can create these tile boundary files by hand, but it requires a lot of work, and it is easy to make mistakes.

Introducing TileCollisionTool

That’s why I wrote TileCollisionTool. With TileCollisionTool, you can upload your tile image and then draw the specific collision boundaries for each tile. The tool also ensures that the boundaries you create fit within the tile’s width and height, and it handles spacing. It requires JavaScript and has been tested on Chrome, Safari, and Firefox 3.

TileCollisionTool in action

The resulting file will look much like the example boundary file above and should be saved as “collisions.txt” in the same project directory as tiles.png.

Programmatically configure the map’s collision boundaries

Now that each type of tile has its collision boundaries configured, the code can simply loop through the tiles defined in the map file, adding static box2d bodies as it goes. As the name implies, static bodies are unmoving. When the game is running your player character will be a dynamic fixture (see below) and will be unable to pass through these static bodies. This requires no serious effort on the part of the programmer. box2d handles it all.

As you probably suspect, adding a separate body or set of bodies for every tile means the game will have to keep track of a lot of bodies. This may not be a big deal, but why make the device work harder than it has to? The problem can be somewhat mitigated by adding code that detects whether one tile simply “continues” the collision line set up by the previous tile. For example, if you have a four tiles of flat ground next to each other, a “naive” implementation would result in four separate static bodies. If the code can detect that situation, the code can make one large body encompassing all four tiles.

In the spirit of decoupling and compartmentalizing, the map collision boundary code is written as a new method for TiledMapHelper. The following snippet is the code used to populate the world with the collision boundaries (the whole method is too large to reasonably paste here, but you’ll find it in the .zip):

208
209
210
211
212
213
214
215
216
217
218
		BodyDef groundBodyDef = new BodyDef();
		groundBodyDef.type = BodyDef.BodyType.StaticBody;
		Body groundBody = world.createBody(groundBodyDef);
		for (LineSegment lineSegment : collisionLineSegments) {
			PolygonShape environmentShape = new PolygonShape();
			environmentShape.setAsEdge(
					lineSegment.start().mul(1 / pixelsPerMeter), lineSegment
							.end().mul(1 / pixelsPerMeter));
			groundBody.createFixture(environmentShape, 0);
			environmentShape.dispose();
		}

Take special note of the pixelsPerMeter variable there. box2d works well with small numbers, and typically people use the term “meter” for box2d’s distance unit. You’ll need to pick a conversion value to use that will take you from pixels to meters and meters to pixels. In this example project, pixelsPerMeter is 60.0, which results in expected physical behavior.

Add a player character sprite

Draw a small image to use as your player character’s sprite. The sprite can be any size. I suggest making it something like the size of a single tile, at least for this example. The sprite will need to be saved inside of an image with powers-of-2 dimensions, due to the aforementioned OpenGL ES requirements. The sample image included in the project (and displayed below) meets the requirements.

Example player character sprite file

Loading the sprite in the game is simple. First, you need to know the size of your sprite and its position within the overall sprite file. In this example, the jumper sprite is at the top left corner and is 21 pixels wide and 37 pixels tall. This code in JumperTutorial’s create method does the job:

139
140
141
142
		overallTexture = new Texture(Gdx.files.internal("data/sprite.png"));
		overallTexture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
 
		jumperSprite = new Sprite(overallTexture, 0, 0, 21, 37);

Basically, the entire sprite image is loaded in to the system as a Texture, and then a chunk of that Texture is defined as a Sprite. The sprite will be drawn on screen after the TiledMapRenderer.render call.

Navigate the map with the player character

As alluded earlier, the player character will be represented in the box2d world as a dynamic fixture. For simplicity and sanity, this code will assume that the character is rectangular:

152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
		BodyDef jumperBodyDef = new BodyDef();
		jumperBodyDef.type = BodyDef.BodyType.DynamicBody;
		jumperBodyDef.position.set(1.0f, 3.0f);
 
		jumper = world.createBody(jumperBodyDef);
 
		/**
		 * Boxes are defined by their "half width" and "half height", hence the
		 * 2 multiplier.
		 */
		PolygonShape jumperShape = new PolygonShape();
		jumperShape.setAsBox(21f / (2 * PIXELS_PER_METER),
				37f / (2 * PIXELS_PER_METER));
 
		/**
		 * The character should not ever spin around on impact.
		 */
		jumper.setFixedRotation(true);
 
		/**
		 * The density of the jumper, 70, was found experimentally. Play with
		 * the number and watch how the character moves faster or slower.
		 * 
		 * The linear damping was also found the same way.
		 */
		jumper.createFixture(jumperShape, 70);
		jumperShape.dispose();
 
		jumper.setLinearVelocity(new Vector2(0.0f, 0.0f));
		jumper.setLinearDamping(5.0f);

The player character’s sprite will be drawn based on the coordinates maintained in the character’s dynamic body object (jumper). The coordinates must be converted from box2d world units back to pixel units:

331
332
333
334
335
336
337
338
339
		spriteBatch.setProjectionMatrix(tiledMapHelper.getCamera().combined);
		spriteBatch.begin();
 
		jumperSprite.setPosition(
				PIXELS_PER_METER * jumper.getPosition().x
						- jumperSprite.getWidth() / 2,
				PIXELS_PER_METER * jumper.getPosition().y
						- jumperSprite.getHeight() / 2);
		jumperSprite.draw(spriteBatch);

There are a number of possible ways to control a character in a game. In this example I am using the keyboard. Left and right arrows move the character left and right, and the space bar causes the character to jump. Character movement is performed using “impulses”, which, in physics, is a way to apply momentum to a body that results in reasonably smooth action. I’ve pasted the movement code below, however the actual code in the project differs slightly:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
		if (Gdx.input.isKeyPressed(Input.Keys.KEYCODE_DPAD_RIGHT)) {
			jumper.applyLinearImpulse(new Vector2(12.0f, 0.0f), new Vector2(
					jumperSprite.getWidth() / (2 * PIXELS_PER_METER),
					jumperSprite.getHeight() / (2 * PIXELS_PER_METER)));
		} else if (Gdx.input.isKeyPressed(Input.Keys.KEYCODE_DPAD_LEFT)) {
			jumper.applyLinearImpulse(new Vector2(-12.0f, 0.0f), new Vector2(
					jumperSprite.getWidth() / (2 * PIXELS_PER_METER),
					jumperSprite.getHeight() / (2 * PIXELS_PER_METER)));
		}
 
		if (Gdx.input.isKeyPressed(Input.Keys.KEYCODE_DPAD_UP)
				&& Math.abs(jumper.getLinearVelocity().y) < 1e-9) {
			jumper.applyLinearImpulse(new Vector2(0.0f, 90.0f), new Vector2(
					jumperSprite.getWidth() / (2 * PIXELS_PER_METER),
					jumperSprite.getHeight() / (2 * PIXELS_PER_METER)));
		}

Summary

Right on. This is looking more and more like a real game. At this point, you’ve seen how to construct a 2 dimensional game world and how to navigate a character around that world. Where to go from here? It might be worth adding some code to reset the game when it detects that the character falls between cliffs, so you don’t have to restart the whole program every time. You could also play with different, more detailed tile sets, making a more complicated and interesting world. Maybe make a few more dynamic objects, attach them to sprites, and see how they interact with your character. In part 3 you’ll see how to add kinematic bodies to the game (such as breakable blocks), how to add superimposed scoring, and a way to detect game over (although that is pretty easy!)

By the way, as Jon pointed out, the code has the debug renderer enabled. You’ll see boxes all over the place, describing the box2d fixtures. You can disable it by commenting out the renderer.render() call.

70 Comments

  1. “You don’t have permission to access /android/jt/part2/JumperTutorialProjects.zip on this server.” :(

  2. Mandeep says:

    I’m glad to see the second part of this get done, I wrote a simple avoider game to try out libgdx and seeing the first part helped me out.

  3. Ed says:

    Hi Awesome tutorial, im testing it out on my Archos 101 running Android 2.2.1 tablet and get this display.

    http://imageshack.us/photo/my-images/862/archos101tablet.jpg/

    and when I test it on my G1 running Android 1.6, I don’t have any transparency on the blocks.

    http://imageshack.us/photo/my-images/36/tmobileg1.jpg/

    Any tips to fix the above ?

    Your doing awesome work, thanks for your time and effort, its relay appreciated :)

    • dpk says:

      Interesting. I haven’t seen that before, but I have been using 2.1+ devices. I will see if I can duplicate it. It sort of looks like some of the tiles have the appropriate transparency (the tiles on the left side of the hole, excluding the one with grass). Does that look correct?

      I have a feeling it is going to involve enabling GL alpha testing or some sort of blending feature that may be automatic for some devices.

      Also, thank you and everyone for your encouragement. I enjoy writing these posts. I write them, in part, to focus my own learning. I have plans for more after this series.

  4. MarkO says:

    I found a tool called PhysicsEditor (http://www.physicseditor.de) which can create polygons from non regular sprites. It really looks cool.

    My question is: Can we make it work with this?

  5. Renz says:

    This is great! Will wait for the part 3!

    Thanks,
    /Renz

  6. dpk says:

    Ed, I am able to duplicate the problem now, on the emulator. I don’t yet have a solution.

    Unfortunately, I made the mistake of building the example based on a nightly libgdx, which is in extreme flux right now. I plan to rebuild the example zips when libgdx 1.0 comes out. For now, I am trying to use the most recent nightly to see if the problem is resolved there. It’s not a drop-in replacement (tiled map code is experiencing a huge shake-up) but I expect to have something soon.

    MarkO, I will check it out, sounds neat.

  7. Ed says:

    Hi I played a game of Jumpoid II on my Archos 101 Tablet, and some of the blocks have the same graphic glitch, so im guessing its a Archos glitch.

  8. Ed says:

    Thanks for the info, im playing around with your example like a madman atm, moving to android GL from 2d Java api, is a massive culture shock, but a awesome one too :)

  9. dpk says:

    As near as I can tell the problem has something to do with the version of OpenGL ES that is run by the device (or emulator). Too low a version (<2.0?) and the alpha channel does not seem to work properly — at least not in the map code. I’m afraid I’m just not experienced enough to figure out exactly why, but I have a “workaround”:

    I’ve filled in all of the transparent areas in the tiles.png file.

    I’ve uploaded it here: http://dpk.net/android/jt/part2/JumperTutorialProjects_test.zip

    If this works for you, I will re-do the projects in part 1 and change the text to reflect the fact that transparencies don’t seem to work.

    I also replaced the libgdx library in the zip with libgdx 0.9 (no longer a nightly).

  10. Ed says:

    Works a treat. Great Job, tested on my Android HTC T-mobile G1 Running (Android 1.6)

  11. Ed says:

    I also tested on my Archos 101 10inch Tablet (android 2.2.1)

    I get this line effect. which is worse then before.
    http://img818.imageshack.us/img818/9900/imag0402g.jpg

    and when I move a bit I get this clear screen
    http://img864.imageshack.us/img864/1812/imag0403g.jpg

    What you did to clear up transparency for the under 2.1 did something to the archos implementation. it looks like it reading a line (column) every 32 pixels from the next block as its scrolling and then clearing when its not hitting a 32 pixel boundery. I can take a video if you want. but i guess you have bigger things to do atm :) just give me a shout.

    • dpk says:

      I’ve made some more changes to the projects, based on some research I’ve done suggesting that textures kind of “bleed” funny when set next to each other. Something to do with floating point math.

      Would you mind trying this latest zip?

  12. Ed says:

    Tested yesterday. Will post pics today. spent the rest of yesterday testing my archos as I found a usb fault. that seems widespread with the device.

  13. Ed says:

    ok the 1st pic shows that you now have it so close thats its just dots
    http://img853.imageshack.us/img853/5236/pic01dots.png

    The second is the black space that grows and disrepairs as your moving across the screen. I believe this is because of the soft on screen buttons. here I have the big buttons displayed, and when I have the stranded smaller soft buttons on the blank gap is smaller too.
    http://img842.imageshack.us/img842/2508/pic02blankside.png

    I came across this problem before and fixed it. going to look for the code I used. (Its just a different way of reading the screen width)

  14. Ed says:

    Nope its not what I thought.
    Gdx.graphics.getWidth(); is reporting the change of soft buttons from big to small.
    I’ll make a video and post it

  15. Ed says:

    theres the vid, Archos 101 running Android 2.2.1
    http://www.youtube.com/watch?v=HDW6ws6ZKV4

    • dpk says:

      Thank you for this video and the other dot problem report. This is all very interesting. I am working on a fix for the dots problem, and I’ll try to find a way to duplicate that weird tile-fill issue. I have some ideas about it, though. Given that it’s drawing several tiles at once I think the fix will involve changing the values passed to the TiledMapRenderer constructor.

  16. Ed says:

    Im playing around with the TiledMapRenderer constructor today, but for me its more of a learning thing than a fixing thing :), if theres any help I can provide please let me know.

  17. dpk says:

    I’ve updated the tiles again, removed all of the transparencies between the tiles. If this works, I’m going to go ahead and make a whole new tileset that will be easier to demonstrate with. (It’ll be hard to show how to let a 32×32 tile kind of “bleed” to 34×34 if all of the colors are the same.)

    I found a possible solution for the Archos soft-button issue but I don’t really like it:

    http://stackoverflow.com/questions/4391059/archos-101-internet-tablet-is-it-possible-to-hide-default-android-software-but

    Taking away home and back in this demo app, without providing a way to exit, is just going to frustrate users. Then I found this:

    http://www.andengine.org/forums/tutorials/adapt-camera-to-screen-t1918.html

    and adapted it for use in libgdx. It ain’t pretty and I haven’t tested it beyond my G2. Basically, it allows the Android activity to pass in the display size. From what I’ve gathered that size excludes the soft buttons.

    I’ve uploaded these fixes to here, would you mind trying it out? I’m also experimenting with different motion physics because I wasn’t very happy with how it worked between phone and desktop. It’s still not quite right.

    (Also, embarrassingly, I managed to release the original version without phone screen controls(!!)… I’ll be sure to fix that for real later, but for now I roughed in some invisible controls (left, right, up by pressing left, right, and top parts of the screen)).

  18. Ed says:

    Hi, the tile bleeding is now gone :). The side fill problem has changed and looks different. I’ll make a video tonight, with the buttons big and small. It might give you an idea of whats going on. well done on getting the tiles fixed.

  19. Ed says:

    I took 2 Vids, the

    Small Buttons
    http://www.youtube.com/watch?v=RLf4OK2LBN4

    Big Buttons
    http://www.youtube.com/watch?v=8lJwAuor_HQ

    The gaps seem to be relative to the button size

    Hope this helps :)

  20. Ed says:

    ok why not use ‘JumperTutorial.this.screenWidth’ in tiledMapHelper.render
    it gaves the full screen and no errors with big buttons or smallbuttons

    also I found 1 pixel problem
    http://img846.imageshack.us/img846/2369/dot01.png
    http://img861.imageshack.us/img861/8396/dot02.png

    only seems to be on the mountain
    pic’s where taken with the JumperTutorial.this.screenWidth sent to tiledMapHelper.render.

  21. dpk says:

    Awesome Ed, thanks for finding that width fix. I’ll get the new tutorials written up.

    I’ll try to figure out the dot issue, too.

  22. Ed says:

    interesting thing I noticed when I switch from bug buttons to small buttons the screen stretches to fit so big buttons make the screen squash, instead of overlaying on top of the screen.

    I just point this out is it might have implications on scripts or or trigger blocks, that are set off, just off screen.

  23. dpk says:

    I think that’s because I didn’t implement the resize() method.

  24. Aydin says:

    Hi!
    Any chance that there will be an offline-version of the tile-collision tool?
    Cheers,
    Aydin

  25. Aydin says:

    plus: the collision-tool is not designed to handle spaces between tiles or?

    • dpk says:

      Hi Aydin,

      I would definitely like to make an offline version of it. My first attempt was offline, but I got frustrated with the UI, so I went with something I was more comfortable with.

      The tool as it exists does not support gaps. That’s actually one of the reasons I’m rewriting parts 1 and 2. The existing code does not work well on devices that have to scale the images (at least I think that’s the problem) so I am going to go back to using TexturePacker (from libgdx), which leaves gaps between the tiles. I’ll then need to update the tile collision tool to support gaps, and it might well be easier to just go ahead and make the offline tool at that point.

  26. Aydin says:

    Hi David,
    thanks for the quick reply. So hopefully the collision-tool will stay online for quite a while :-)

    I am struggling at the moment with the given example[code] and a larger tileset (16x16 tiles, 256x256 in complete):
    The tiles are not drawn at all, although tmx-, collision and packfile are created and present. hmm...
    Any idea/hint where to look closer?

    Grüße :-)
    Aydin

  27. dpk says:

    Hi Aydin,

    I just want to be sure I have this right — 16×16 (256) tiles that are each 256×256 in size? For a total of 4096×4096 pixels?

  28. Aydin says:

    Hi David,
    no: 16×16 pixel per tile, the complete tileset is in total 256×256 pixel in size (so 16×16 tiles).

    After changing the map-height from 100 to 15 the map is finally shown on the jogl/desktop (seems that it was a camera positioning issue).

    Now, the collisions are deferred (y = -16 pixel):
    http://i53.tinypic.com/jfj7si.png

    I’ll proceed tomorrow.

    If you want to check as well, I’ve uploaded the used files (tmx,pack,tileset,collision):
    http://www.mediafire.com/?ilp6cuqc322wz4v

    • dpk says:

      I think I know the cause of that. I made a dumb error in the zip in part 2. I left some hard-coded 32×32 values. It’s one of those things I plan to fix in the new release.

      Change the call to addOrExtendCollisionLineSegment to:

      addOrExtendCollisionLineSegment(x * getMap().tileWidth
      + lineSeg.start().x, y * getMap().tileHeight
      - lineSeg.start().y + getMap().tileHeight, x
      * getMap().tileWidth + lineSeg.end().x, y
      * getMap().tileHeight - lineSeg.end().y
      + getMap().tileHeight, collisionLineSegments);

  29. Aydin says:

    Thanks a lot, this fixed it.

  30. Ed says:

    Hi, Is it possible to Zoom In and Out with this type of camera ?, And how would I do that, Ive played around with several calls but no success. Thanks

  31. Aydin says:

    David, did you try this example on a Desire HD? On my DHD it is running on (felt) 0.1 fps :-/
    On the normal/first Desire, speed is better: It’s playable (still a bit slow but looks tunable).

    This is strange, as the DHD is more powerful than the normal desire. hhmmmm…

    • dpk says:

      I didn’t. That’s unfortunate. I don’t have a good way to test on multiple devices as I only have a G1 and a G2. (I didn’t test on the G1, though.)

      Any ideas on the cause? There are some places in the code that allocate memory per frame, which is not ideal, but I don’t know if that’s going to be the problem.

      • Aydin says:

        Found the origin for the slowness, it’s the call to
        drawSolidPolygon(vertices, vertexCount, color);
        in Box2DDebugRenderer line 133. If I comment it out, the rendering is very fast on the DHD.

        drawSolidCircle in contrast is not slowing down the device (I tested it with a jumping ball).

  32. Aydin says:

    I’ll try to debug tomorrow (ddms) and let you know,

  33. dpk says:

    Don’t know Ed. I’ll try some stuff after finishing part 3. :)

  34. dpk says:

    Quick update: I’m almost done rewriting parts 1 and 2. libgdx had a release since I first wrote the JumperTutorial code, and the tiled map code got some updates. (The upcoming version 1.0 has some really neat changes that may make TiledMapHelper obsolete, which I’d be totally fine with.) Anyway, I ran in to some weird behavior because of some changes to the underlying assumptions I made for the previous version. In particular, the pack files are built differently now, which tends to affect everything.

  35. Ed says:

    wow cant wait too see. good luck!!!!

  36. Ed says:

    any updates you can tell us about ?

    • dpk says:

      I’ve updated part 1 and updated part 2 a bit, but I haven’t made any progress on part 3. Been real busy with work. I will try to dedicate some time to it this weekend.

  37. Ed says:

    sounds awesome reading it now ty XD

  38. Veedoo says:

    Hey, the tool is great. Can you point to the NEWEST version of the HELPER class? I can see modification from this discussions are not in the zip with the classes…

  39. Dom says:

    Just wanted to push this article again to show that there are still people who can’t wait to see part 3. :)

  40. dpk says:

    As I commented in part 3 I am going to try to make a sincere effort to work on part 3 this weekend. My first priority will be updating parts 1 and 2 to work with libgdx 0.9.1, however. I will probably end up putting the source up on github to make it easier to maintain the multiple different versions of JumperTutorial.java (versions as in “part 1″ “part 2″ “part 3″).

  41. Karim says:

    I’m getting an error when I run this using the emulator. I took the pack file and put it into a packer sub folder and renamed it to tiles packfile. I took the tiles png file and put it into the folder and made sure the file name is tiles.png and I renamed the level.tmx file to be tiles.tmx and left this in the data/world/level1/ folder. Do you know what’s I’m doing wrong?

    Here is the line that fails
    tiledMapRenderer.render((int) tmp.x,
    tiledMapRenderer.getMapHeightUnits() – (int) tmp.y,
    Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), layersList);

    Here is the error output on the console.

    Exception in thread “Thread-3″ javax.media.opengl.GLException: java.lang.IndexOutOfBoundsException: Required 1 remaining elements in buffer, only had 0
    at javax.media.opengl.Threading.invokeOnOpenGLThread(Threading.java:271)
    at javax.media.opengl.GLCanvas.maybeDoSingleThreadedWorkaround(GLCanvas.java:410)
    at javax.media.opengl.GLCanvas.display(GLCanvas.java:244)
    at com.badlogic.gdx.backends.jogl.JoglAnimator.display(JoglAnimator.java:137)
    at com.badlogic.gdx.backends.jogl.JoglAnimator$MainLoop.run(JoglAnimator.java:174)
    at java.lang.Thread.run(Unknown Source)
    Caused by: java.lang.IndexOutOfBoundsException: Required 1 remaining elements in buffer, only had 0
    at com.sun.gluegen.runtime.BufferFactory.rangeCheck(BufferFactory.java:247)
    at com.sun.opengl.impl.GLImpl.glVertexPointer(GLImpl.java:27937)
    at com.badlogic.gdx.backends.jogl.JoglGL10.glVertexPointer(JoglGL10.java:425)
    at com.badlogic.gdx.graphics.glutils.VertexArray.bind(VertexArray.java:114)
    at com.badlogic.gdx.graphics.Mesh.bind(Mesh.java:249)
    at com.badlogic.gdx.graphics.g2d.SpriteCache.begin(SpriteCache.java:846)
    at com.badlogic.gdx.graphics.g2d.tiled.TileMapRenderer.render(TileMapRenderer.java:336)
    at com.awesome.TiledMapHelper.render(TiledMapHelper.java:61)
    at com.awesome.JumperTutorial.render(JumperTutorial.java:250)
    at com.badlogic.gdx.backends.jogl.JoglGraphics.display(JoglGraphics.java:113)
    at com.sun.opengl.impl.GLDrawableHelper.display(GLDrawableHelper.java:78)
    at javax.media.opengl.GLCanvas$DisplayAction.run(GLCanvas.java:435)
    at com.sun.opengl.impl.GLDrawableHelper.invokeGL(GLDrawableHelper.java:194)
    at javax.media.opengl.GLCanvas$DisplayOnEventDispatchThreadAction.run(GLCanvas.java:452)
    at java.awt.event.InvocationEvent.dispatch(Unknown Source)
    at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
    at java.awt.EventQueue.access$000(Unknown Source)
    at java.awt.EventQueue$1.run(Unknown Source)
    at java.awt.EventQueue$1.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue.dispatchEvent(Unknown Source)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.run(Unknown Source)
    Exception in thread “AWT-EventQueue-0″ java.lang.IndexOutOfBoundsException: Required 1 remaining elements in buffer, only had 0
    at com.sun.gluegen.runtime.BufferFactory.rangeCheck(BufferFactory.java:247)
    at com.sun.opengl.impl.GLImpl.glVertexPointer(GLImpl.java:27937)
    at com.badlogic.gdx.backends.jogl.JoglGL10.glVertexPointer(JoglGL10.java:425)
    at com.badlogic.gdx.graphics.glutils.VertexArray.bind(VertexArray.java:114)
    at com.badlogic.gdx.graphics.Mesh.bind(Mesh.java:249)
    at com.badlogic.gdx.graphics.g2d.SpriteCache.begin(SpriteCache.java:846)
    at com.badlogic.gdx.graphics.g2d.tiled.TileMapRenderer.render(TileMapRenderer.java:336)
    at com.awesome.TiledMapHelper.render(TiledMapHelper.java:61)
    at com.awesome.JumperTutorial.render(JumperTutorial.java:250)
    at com.badlogic.gdx.backends.jogl.JoglGraphics.display(JoglGraphics.java:113)
    at com.sun.opengl.impl.GLDrawableHelper.display(GLDrawableHelper.java:78)
    at javax.media.opengl.GLCanvas$DisplayAction.run(GLCanvas.java:435)
    at com.sun.opengl.impl.GLDrawableHelper.invokeGL(GLDrawableHelper.java:194)
    at javax.media.opengl.GLCanvas.maybeDoSingleThreadedWorkaround(GLCanvas.java:412)
    at javax.media.opengl.GLCanvas.display(GLCanvas.java:244)
    at javax.media.opengl.GLCanvas.paint(GLCanvas.java:277)
    at sun.awt.RepaintArea.paintComponent(Unknown Source)
    at sun.awt.RepaintArea.paint(Unknown Source)
    at sun.awt.windows.WComponentPeer.handleEvent(Unknown Source)
    at java.awt.Component.dispatchEventImpl(Unknown Source)
    at java.awt.Component.dispatchEvent(Unknown Source)
    at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
    at java.awt.EventQueue.access$000(Unknown Source)
    at java.awt.EventQueue$1.run(Unknown Source)
    at java.awt.EventQueue$1.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
    at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue$2.run(Unknown Source)
    at java.awt.EventQueue$2.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.AccessControlContext$1.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue.dispatchEvent(Unknown Source)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.run(Unknown Source)

    • genson says:

      I also get this error, please tell me how can I do?

      • dpk says:

        Sorry benson, I’m not sure. Karim was able to fix the problem but I’m not sure what the cause was.

        • esch says:

          I had this problem and found that the texture packer hadn’t been able to generate a pack file for my tile map (it had a single entry in it with index -1). I was probably doing something wrong, anyway my map was simple so I just made the pack file manually and it fixed the problem.

  42. Jon says:

    Is there a way to hide the bounding rectangle of the box so that only the texture is shown. I changed the texture to a different image but now I see a box around the texture.

  43. dpk says:

    Jon, can you take a screenshot and post it on imgur.com? I am not sure I understand. If this is on your phone, you can take screenshots through Eclipse -> DDMS.

  44. Lat233 says:

    Hi Dpk,

    Thanks for your article. I would like to implement a game like The Battle City (tank game of Nintendo) by applying your solution for collision detection between tanks and the bricks/stones in the map (tiles). A problem occurs: the sprite (tank) cannot stop when I release the key (for example LEFT_KEY to move to the left). The sprite continues to move freely until it collides with an obstacle. The question is how can I remove the inertia of the sprite?

    Thanks so much!

  45. Lat233 says:

    Hi Dpk,
    Thanks so much about your reply. I will try it. Beside, I’ve tried to use set linear velocity of the character to zero after each one render (for each key release event) and it works. I think it’s not very like in physic world where an object in movement needs to have an inertia before it stops, but it’s acceptable for a game like tank.

    One more question: How can we synchronize coordinates of a body (character) with a real sprite when they are moved? What is the formula for converting the coordinates of a sprite to its body and vice versa? I need it because a sprite (tank) fires bullet from its center, that’s why I need to update the coordinates of the sprite according to its body’s coordinates.

    Many thanks.

    • dpk says:

      The way I’d approach that problem is with a single “drawSpriteAtCenter(Body body, Sprite sprite)” method. This method would use the body.getWorldCenter() method, .mul()’d by the PIXELS_PER_METER constant. Then I’d have it subtract half the sprite width from x and half the sprite height from y. (Actually, you might need to *add* half the sprite height. I don’t remember exactly.)

      Just be careful not to allocate Vector2s when doing this and it should work just fine. You can monitor allocations with Eclipse and DDMS.

  46. Francesco says:

    how i “write! a text on screen?

  47. What’s the other way to know if the character is on the floor?

  48. What’s the other way to know if the character is on the floor?
    The way used for you isn´t efficient. The character climb walls…

    • dpk says:

      I’m not sure what you mean about the floor.

      Agreed on the efficiency. The code I wrote is not great. I think that part of the problem is the line combining code. It needs another phase: removing lines that are fully contained within other lines. I don’t know if it’d help, but what if it’s possible to disable friction entirely on vertical lines?

  49. steve says:

    Nice article,

    I would although recommend using SVG files for the collision boundaries – Inkscape outputs this for you, so load
    your map into inkscape (png from Tiled) and then draw the collisions, save as an Inkscape SVG file and create a simple
    SVG parser to then create the Box2d bodies – this is what I do on iOS.

    Thanks again for some great stuff!

    Steve

Leave a Reply