[Tutorial] Collision objects from TMX map

  ... tutorials on how to use AndEngine.

[Tutorial] Collision objects from TMX map

Postby Crundy » Mon Jul 04, 2011 4:03 pm

Thought I'd share my work on the above to avoid anyone else going nuts trying to get this working.

The idea is to have as many tile layers as you want, and then have an object layer with a property called "wall" with a value of "true" and draw shapes you don't want your character to walk on it. The objects you create will be created as Box2D objects in code and the collision is done automatically by the physics engine.

Note: The camera is a bit stuttery at the moment, I'll update the code if and when I find a fix for it. If you discover I'm doing something else stupid then let me know and I'll fix it up.
EDIT: Fixed now. Thanks to Nicolas.

EDIT 2: Changed to use release version of AndEngine, and I've included the entire project for people now.

Note 1: As someone has pointed out, if you want to use this example with your own maps, you need to add a property to the collision layer itself, not the individual objects in the layer.

Note 2: This example is perfect for when you want your character(s) to be able to move in increments within tiles, but if you are happy with your character(s) to move from the center of one tile to the next then I would recommend using RionEye's tutorial instead, which also uses A* pathing.

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. package your.namespace.tiletest;
  2.  
  3. import javax.microedition.khronos.opengles.GL10;
  4.  
  5. import org.anddev.andengine.engine.Engine;
  6. import org.anddev.andengine.engine.camera.BoundCamera;
  7. import org.anddev.andengine.engine.camera.hud.controls.BaseOnScreenControl;
  8. import org.anddev.andengine.engine.camera.hud.controls.BaseOnScreenControl.IOnScreenControlListener;
  9. import org.anddev.andengine.engine.camera.hud.controls.DigitalOnScreenControl;
  10. import org.anddev.andengine.engine.options.EngineOptions;
  11. import org.anddev.andengine.engine.options.EngineOptions.ScreenOrientation;
  12. import org.anddev.andengine.engine.options.resolutionpolicy.RatioResolutionPolicy;
  13. import org.anddev.andengine.entity.layer.tiled.tmx.TMXLayer;
  14. import org.anddev.andengine.entity.layer.tiled.tmx.TMXLoader;
  15. import org.anddev.andengine.entity.layer.tiled.tmx.TMXObject;
  16. import org.anddev.andengine.entity.layer.tiled.tmx.TMXObjectGroup;
  17. import org.anddev.andengine.entity.layer.tiled.tmx.TMXTiledMap;
  18. import org.anddev.andengine.entity.layer.tiled.tmx.util.exception.TMXLoadException;
  19. import org.anddev.andengine.entity.primitive.Rectangle;
  20. import org.anddev.andengine.entity.scene.Scene;
  21. import org.anddev.andengine.entity.shape.Shape;
  22. import org.anddev.andengine.entity.sprite.AnimatedSprite;
  23. import org.anddev.andengine.entity.util.FPSLogger;
  24. import org.anddev.andengine.extension.physics.box2d.FixedStepPhysicsWorld;
  25. import org.anddev.andengine.extension.physics.box2d.PhysicsConnector;
  26. import org.anddev.andengine.extension.physics.box2d.PhysicsFactory;
  27. import org.anddev.andengine.extension.physics.box2d.PhysicsWorld;
  28. import org.anddev.andengine.opengl.texture.TextureOptions;
  29. import org.anddev.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlas;
  30. import org.anddev.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlasTextureRegionFactory;
  31. import org.anddev.andengine.opengl.texture.region.TextureRegion;
  32. import org.anddev.andengine.opengl.texture.region.TiledTextureRegion;
  33. import org.anddev.andengine.ui.activity.BaseGameActivity;
  34. import org.anddev.andengine.util.Debug;
  35.  
  36. import com.badlogic.gdx.math.Vector2;
  37. import com.badlogic.gdx.physics.box2d.Body;
  38. import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
  39. import com.badlogic.gdx.physics.box2d.FixtureDef;
  40.  
  41. public class TileActivity extends BaseGameActivity {
  42.  
  43.         private TMXTiledMap mTMXTiledMap;
  44.         private BoundCamera mBoundChaseCamera;
  45.  
  46.         private static final int CAMERA_WIDTH = 480;
  47.     private static final int CAMERA_HEIGHT = 320;
  48.     private Scene mScene;
  49.    
  50.         private static final long[] ANIMATE_DURATION = new long[]{200, 200, 200};
  51.         private static final int PLAYER_VELOCITY = 2;
  52.    
  53.         private BitmapTextureAtlas mTexturePlayer;
  54.         private Body mPlayerBody;
  55.         private TiledTextureRegion mPlayerTextureRegion;
  56.         private BitmapTextureAtlas mOnScreenControlTexture;
  57.         private TextureRegion mOnScreenControlBaseTextureRegion;
  58.         private TextureRegion mOnScreenControlKnobTextureRegion;
  59.         private DigitalOnScreenControl mDigitalOnScreenControl;
  60.         private PhysicsWorld mPhysicsWorld;
  61.  
  62.         private enum PlayerDirection{
  63.                 NONE,
  64.                 UP,
  65.                 DOWN,
  66.                 LEFT,
  67.                 RIGHT
  68.         }
  69.         private PlayerDirection playerDirection = PlayerDirection.NONE;
  70.  
  71.  
  72.         @Override
  73.         public Engine onLoadEngine() {
  74.                 this.mBoundChaseCamera = new BoundCamera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
  75.                 return new Engine(new EngineOptions(true, ScreenOrientation.LANDSCAPE, new RatioResolutionPolicy(CAMERA_WIDTH, CAMERA_HEIGHT), this.mBoundChaseCamera));
  76.         }
  77.  
  78.         @Override
  79.         public void onLoadResources() {
  80.                 BitmapTextureAtlasTextureRegionFactory.setAssetBasePath("gfx/");
  81.                 // Control texture
  82.                 this.mOnScreenControlTexture = new BitmapTextureAtlas(256, 128, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
  83.                 this.mOnScreenControlBaseTextureRegion = BitmapTextureAtlasTextureRegionFactory.createFromAsset(this.mOnScreenControlTexture, this, "onscreen_control_base.png", 0, 0);
  84.                 this.mOnScreenControlKnobTextureRegion = BitmapTextureAtlasTextureRegionFactory.createFromAsset(this.mOnScreenControlTexture, this, "onscreen_control_knob.png", 128, 0);
  85.  
  86.                 // Player sprite texture
  87.                 this.mTexturePlayer = new BitmapTextureAtlas(128, 128, TextureOptions.DEFAULT);
  88.                 this.mPlayerTextureRegion = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.mTexturePlayer, this, "hero.png", 0, 0, 3, 4);
  89.  
  90.                 // Load the textures
  91.                 this.mEngine.getTextureManager().loadTextures(this.mTexturePlayer, this.mOnScreenControlTexture);
  92.         }
  93.  
  94.         @Override
  95.         public Scene onLoadScene() {
  96.                 this.mEngine.registerUpdateHandler(new FPSLogger());
  97.                
  98.                 // Create physics world
  99.                 this.mPhysicsWorld = new FixedStepPhysicsWorld(30, new Vector2(0, 0), false, 8, 1);
  100.                
  101.                 // Create the scene and register the physics world
  102.                 mScene = new Scene();
  103.                 mScene.registerUpdateHandler(this.mPhysicsWorld);
  104.  
  105.                 // Load the TMX map
  106.                 try {
  107.                         final TMXLoader tmxLoader = new TMXLoader(this, this.mEngine.getTextureManager(), TextureOptions.NEAREST, null);
  108.                         this.mTMXTiledMap = tmxLoader.loadFromAsset(this, "test.tmx");
  109.                 } catch (final TMXLoadException tmxle) {
  110.                         Debug.e(tmxle);
  111.                 }
  112.  
  113.                 // Add the non-object layers to the scene
  114.                 for (int i = 0; i < this.mTMXTiledMap.getTMXLayers().size(); i++){
  115.                         TMXLayer layer = this.mTMXTiledMap.getTMXLayers().get(i);
  116.                         if (!layer.getTMXLayerProperties().containsTMXProperty("wall", "true"))
  117.                         mScene.attachChild(layer);
  118.                 }
  119.  
  120.                 // Read in the unwalkable blocks from the object layer and create boxes for each
  121.                 this.createUnwalkableObjects(mTMXTiledMap);
  122.                
  123.                 // Make the camera not exceed the bounds of the TMXEntity.
  124.                 final TMXLayer tmxLayer = this.mTMXTiledMap.getTMXLayers().get(0);
  125.                 this.mBoundChaseCamera.setBounds(0, tmxLayer.getWidth(), 0, tmxLayer.getHeight());
  126.                 this.mBoundChaseCamera.setBoundsEnabled(true);
  127.                 // Add outer walls
  128.                 this.addBounds(tmxLayer.getWidth(), tmxLayer.getHeight());
  129.  
  130.                 // Calculate the coordinates for the player, so it's centred on the camera.
  131.                 final int centerX = (CAMERA_WIDTH - this.mPlayerTextureRegion.getTileWidth()) / 2;
  132.                 final int centerY = (CAMERA_HEIGHT - this.mPlayerTextureRegion.getTileHeight()) / 2;
  133.  
  134.                 // Create the player sprite and add it to the scene.
  135.                 final AnimatedSprite player = new AnimatedSprite(centerX, centerY, this.mPlayerTextureRegion);
  136.                 this.mBoundChaseCamera.setChaseEntity(player);
  137.                 final FixtureDef playerFixtureDef = PhysicsFactory.createFixtureDef(0, 0, 0.5f);
  138.                 mPlayerBody = PhysicsFactory.createBoxBody(this.mPhysicsWorld, player, BodyType.DynamicBody, playerFixtureDef);
  139.                 this.mPhysicsWorld.registerPhysicsConnector(new PhysicsConnector(player, mPlayerBody, true, false){
  140.                         @Override
  141.                         public void onUpdate(float pSecondsElapsed){
  142.                                 super.onUpdate(pSecondsElapsed);
  143.                                 mBoundChaseCamera.updateChaseEntity();
  144.                         }
  145.                 });
  146.                 mScene.attachChild(player);
  147.                
  148.                 // Add the control
  149.                 this.mDigitalOnScreenControl = new DigitalOnScreenControl(0, CAMERA_HEIGHT - this.mOnScreenControlBaseTextureRegion.getHeight(), this.mBoundChaseCamera, this.mOnScreenControlBaseTextureRegion, this.mOnScreenControlKnobTextureRegion, 0.1f, new IOnScreenControlListener() {
  150.                         @Override
  151.                         public void onControlChange(final BaseOnScreenControl pBaseOnScreenControl, final float pValueX, final float pValueY) {
  152.                                 // Set the correct walking animation
  153.                                 if (pValueY == 1){
  154.                                         // Up
  155.                                         if (playerDirection != PlayerDirection.UP){
  156.                                                 player.animate(ANIMATE_DURATION, 0, 2, true);
  157.                                                 playerDirection = PlayerDirection.UP;
  158.                                         }
  159.                                 }else if (pValueY == -1){
  160.                                         // Down
  161.                                         if (playerDirection != PlayerDirection.DOWN){
  162.                                                 player.animate(ANIMATE_DURATION, 9, 11, true);
  163.                                                 playerDirection = PlayerDirection.DOWN;
  164.                                         }
  165.                                 }else if (pValueX == -1){
  166.                                         // Left
  167.                                         if (playerDirection != PlayerDirection.LEFT){
  168.                                                 player.animate(ANIMATE_DURATION, 3, 5, true);
  169.                                                 playerDirection = PlayerDirection.LEFT;
  170.                                         }
  171.                                 }else if (pValueX == 1){
  172.                                         // Right
  173.                                         if (playerDirection != PlayerDirection.RIGHT){
  174.                                                 player.animate(ANIMATE_DURATION, 6, 8, true);
  175.                                                 playerDirection = PlayerDirection.RIGHT;
  176.                                         }
  177.                                 }else{
  178.                                         if (player.isAnimationRunning()){
  179.                                                 player.stopAnimation();
  180.                                                 playerDirection = PlayerDirection.NONE;
  181.                                         }
  182.                                 }
  183.                                 // Set the player's velocity
  184.                                 mPlayerBody.setLinearVelocity(pValueX * PLAYER_VELOCITY, pValueY * PLAYER_VELOCITY);
  185.                         }
  186.                 });
  187.                 this.mDigitalOnScreenControl.getControlBase().setBlendFunction(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
  188.                 this.mDigitalOnScreenControl.getControlBase().setAlpha(0.5f);
  189.                 this.mDigitalOnScreenControl.getControlBase().setScaleCenter(0, 128);
  190.                 this.mDigitalOnScreenControl.getControlBase().setScale(1.25f);
  191.                 this.mDigitalOnScreenControl.getControlKnob().setScale(1.25f);
  192.                 this.mDigitalOnScreenControl.getControlKnob().setAlpha(0.5f);
  193.                 this.mDigitalOnScreenControl.refreshControlKnobPosition();
  194.  
  195.                 mScene.setChildScene(this.mDigitalOnScreenControl);
  196.                
  197.                 return mScene;
  198.         }
  199.  
  200.         @Override
  201.         public void onLoadComplete() {
  202.                 // TODO Auto-generated method stub
  203.  
  204.         }
  205.        
  206.         private void createUnwalkableObjects(TMXTiledMap map){
  207.                 // Loop through the object groups
  208.                  for(final TMXObjectGroup group: this.mTMXTiledMap.getTMXObjectGroups()) {
  209.                          if(group.getTMXObjectGroupProperties().containsTMXProperty("wall", "true")){
  210.                                  // This is our "wall" layer. Create the boxes from it
  211.                                  for(final TMXObject object : group.getTMXObjects()) {
  212.                                         final Rectangle rect = new Rectangle(object.getX(), object.getY(),object.getWidth(), object.getHeight());
  213.                                         final FixtureDef boxFixtureDef = PhysicsFactory.createFixtureDef(0, 0, 1f);
  214.                                         PhysicsFactory.createBoxBody(this.mPhysicsWorld, rect, BodyType.StaticBody, boxFixtureDef);
  215.                                         rect.setVisible(false);
  216.                                         mScene.attachChild(rect);
  217.                                  }
  218.                          }
  219.                  }
  220.         }
  221.        
  222.         private void addBounds(float width, float height){
  223.                 final Shape bottom = new Rectangle(0, height - 2, width, 2);
  224.                 bottom.setVisible(false);
  225.                 final Shape top = new Rectangle(0, 0, width, 2);
  226.                 top.setVisible(false);
  227.                 final Shape left = new Rectangle(0, 0, 2, height);
  228.                 left.setVisible(false);
  229.                 final Shape right = new Rectangle(width - 2, 0, 2, height);
  230.                 right.setVisible(false);
  231.  
  232.                 final FixtureDef wallFixtureDef = PhysicsFactory.createFixtureDef(0, 0, 1f);
  233.                 PhysicsFactory.createBoxBody(this.mPhysicsWorld, bottom, BodyType.StaticBody, wallFixtureDef);
  234.                 PhysicsFactory.createBoxBody(this.mPhysicsWorld, top, BodyType.StaticBody, wallFixtureDef);
  235.                 PhysicsFactory.createBoxBody(this.mPhysicsWorld, left, BodyType.StaticBody, wallFixtureDef);
  236.                 PhysicsFactory.createBoxBody(this.mPhysicsWorld, right, BodyType.StaticBody, wallFixtureDef);
  237.  
  238.                 this.mScene.attachChild(bottom);
  239.                 this.mScene.attachChild(top);
  240.                 this.mScene.attachChild(left);
  241.                 this.mScene.attachChild(right);
  242.         }
  243.  
  244.  
  245. }
  246.  
Parsed in 0.069 seconds, using GeSHi 1.0.8.4


Entire test project attached.
Attachments
TileCollision.zip
Entire test project
(1.97 MiB) Downloaded 2808 times
Last edited by Crundy on Thu Aug 11, 2011 10:35 pm, edited 2 times in total.
Beer Freeze - Never leave your beer in the freezer again
Crundy
 
Posts: 96
Joined: Mon Jun 20, 2011 4:07 pm

Re: [Tutorial] Collision objects from TMX map

Postby rioneye » Mon Jul 04, 2011 7:05 pm

Wow, thats impressive Crundy. Although I am having a problem compiling your code (Line 144). I am getting an error for updateChaseEntity, which says "The method updateChaseEntity() is undefined for the type BoundCamera." I'm not sure what could cause that, since I have everything imported.
User avatar
rioneye
 
Posts: 277
Joined: Tue Jun 28, 2011 7:08 pm
Location: USA

Re: [Tutorial] Collision objects from TMX map

Postby Crundy » Mon Jul 04, 2011 8:38 pm

Ah, you need a change that Nicholas made today. get the latest andengine source, export it to a jar file and use that in your project.
Beer Freeze - Never leave your beer in the freezer again
Crundy
 
Posts: 96
Joined: Mon Jun 20, 2011 4:07 pm

Re: [Tutorial] Collision objects from TMX map

Postby sscape » Mon Jul 04, 2011 8:39 pm

rioneye wrote:Wow, thats impressive Crundy. Although I am having a problem compiling your code (Line 144). I am getting an error for updateChaseEntity, which says "The method updateChaseEntity() is undefined for the type BoundCamera." I'm not sure what could cause that, since I have everything imported.


Nicholas submitted some updates about 4 hours ago: "Camera.updateChaseEntity and made it publicly accessible, because it is often used to prevent the Camera from 'lagging' one frame behind the Entity it is chasing." so grab a new version of the code.
sscape
 
Posts: 15
Joined: Tue Sep 21, 2010 9:26 am

Re: [Tutorial] Collision objects from TMX map

Postby rioneye » Tue Jul 05, 2011 7:53 pm

So I'm relatively new to andEngine. Right now I'm using mercuil to import the andengine. Then I export the whole thing as a jar and use that, but I am getting errors about having an invalid apk. What exactly are we supposed to export to a jar file from andEngine?

These are the errors I'm getting
[2011-07-05 12:29:56 - GameTestAndEngine] Error generating final archive: Found duplicate file for APK: AndroidManifest.xml
Origin 1: C:\workspace\GameTestAndEngine\bin\resources.ap_
Origin 2: C:\workspace\GameTestAndEngine\lib\andEngine.jar
User avatar
rioneye
 
Posts: 277
Joined: Tue Jun 28, 2011 7:08 pm
Location: USA

Re: [Tutorial] Collision objects from TMX map

Postby AlexNunn » Tue Jul 05, 2011 8:47 pm

rioneye wrote:So I'm relatively new to andEngine. Right now I'm using mercuil to import the andengine. Then I export the whole thing as a jar and use that, but I am getting errors about having an invalid apk. What exactly are we supposed to export to a jar file from andEngine?

These are the errors I'm getting
[2011-07-05 12:29:56 - GameTestAndEngine] Error generating final archive: Found duplicate file for APK: AndroidManifest.xml
Origin 1: C:\workspace\GameTestAndEngine\bin\resources.ap_
Origin 2: C:\workspace\GameTestAndEngine\lib\andEngine.jar

You only need the generated/compiled class files in your andengine.jar. Everything else in your andengine project can be excluded.
AlexNunn
 
Posts: 604
Joined: Thu Oct 07, 2010 6:43 pm
Location: Kentucky

Re: [Tutorial] Collision objects from TMX map

Postby rioneye » Tue Jul 05, 2011 9:12 pm

AlexNunn wrote:You only need the generated/compiled class files in your andengine.jar. Everything else in your andengine project can be excluded.


Thanks I realized my mistake. I was exporting the entire andEngine Project, not the files in the src folder.
User avatar
rioneye
 
Posts: 277
Joined: Tue Jun 28, 2011 7:08 pm
Location: USA

Re: [Tutorial] Collision objects from TMX map

Postby Crundy » Tue Jul 05, 2011 10:17 pm

Working OK now?
Beer Freeze - Never leave your beer in the freezer again
Crundy
 
Posts: 96
Joined: Mon Jun 20, 2011 4:07 pm

Re: [Tutorial] Collision objects from TMX map

Postby rioneye » Wed Jul 06, 2011 3:06 am

Crundy wrote:Working OK now?

Yes thank you
User avatar
rioneye
 
Posts: 277
Joined: Tue Jun 28, 2011 7:08 pm
Location: USA

Re: [Tutorial] Collision objects from TMX map

Postby rioneye » Wed Jul 06, 2011 4:58 am

Hey Crundy,

I don't know if you've tried this or not, but wouldn't it have been easier to change the properties(to collidable or something equivalent) of a layer in the TMX file, then check the tile the character will walk on to see if it has that property?
User avatar
rioneye
 
Posts: 277
Joined: Tue Jun 28, 2011 7:08 pm
Location: USA

Next

Return to Tutorials

Who is online

Users browsing this forum: Google Feedfetcher and 23 guests