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
- Creating map tiles and the game map
- Integrating box2d
- Final touches: Scoring, lives, game over [coming soon]
- Add breaking blocks
- Add score and score display
- Add player death and a game over screen
- Hey, you’ve got a game
- 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.
“You don’t have permission to access /android/jt/part2/JumperTutorialProjects.zip on this server.” :(
Whoops. Fixed, thanks.
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.
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 :)
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.
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?
This is great! Will wait for the part 3!
Thanks,
/Renz
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.
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.
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 :)
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).
Works a treat. Great Job, tested on my Android HTC T-mobile G1 Running (Android 1.6)
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.
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?
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.
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)
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
theres the vid, Archos 101 running Android 2.2.1
http://www.youtube.com/watch?v=HDW6ws6ZKV4
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.
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.
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)).
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.
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 :)
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.
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.
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.
I think that’s because I didn’t implement the resize() method.
Hi!
Any chance that there will be an offline-version of the tile-collision tool?
Cheers,
Aydin
plus: the collision-tool is not designed to handle spaces between tiles or?
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.
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
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?
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
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);
Thanks a lot, this fixed it.
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
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…
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.
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).
Thanks, I’ll make sure to note that in the rewrite.
I’ll try to debug tomorrow (ddms) and let you know,
Don’t know Ed. I’ll try some stuff after finishing part 3. :)
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.
wow cant wait too see. good luck!!!!
any updates you can tell us about ?
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.
sounds awesome reading it now ty XD
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…
Just wanted to push this article again to show that there are still people who can’t wait to see part 3. :)
I will second that !!!
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″).
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)
I also get this error, please tell me how can I do?
Sorry benson, I’m not sure. Karim was able to fix the problem but I’m not sure what the cause was.
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.
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.
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.
image uploaded -> http://i.imgur.com/ijqNI.png
I’ve looked everywhere but I can’t figure out how to set the box to transparant. I think this is the collision border of the box that is shown. Completely new to this so I apologise if this is a stupid question :P
finally sussed it.
I removed the reference to debugRenderer. I didn’t realise this was what it did …. DOH!!
Ah ha. Good find.
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!
Hi Lat233,
If I understand right, you want the tank to stop when the player lets go of the key. There are a few things you can do here, and you may just need to play with it until it feels right. You can try increasing the tank’s friction, in its FixtureDef. You could add a call to setLinearDamping to some really high value, which will cause the tank to halt after some time:
http://libgdx.l33tlabs.org/docs/api/com/badlogic/gdx/physics/box2d/Body.html#setLinearDamping(float)
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.
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.
how i “write! a text on screen?
Check out http://code.google.com/p/libgdx-users/wiki/addingText2D
What’s the other way to know if the character is on the floor?
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…
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?
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