Tag Archives: tilemap

How to create Box2D collision from a Tiled Map

Initially I just implemented plain bounding box collision detection between my main game player and Tiled Map objects (obstacles on the map), but realized it is not perfect solution since obstacles are not always tailored by tile size. So, I decided to do it using Box2D. When I started working on it and searching for similar implementations or tutorials, I realized that there hard to find any explaining this very common requirement which motivated me to write this tutorial. Note that in my case collision means main game player hits the obstacles on the Map and explodes, not collision in sense of moving on some kind of platform. Also, Tiled Map is placed on top of Parallax, but should not be much of difference in case your Tiled Map is child of main scene layer whatsoever.

This tutorial builds upon basics of using Box2D for collision detection covered in great Ray Wenderlich tutorial, so I suggest reading it first. I will not talk here again about Box2D bodies and ContactListeners.

First, you need to define your collision objects on Tiled Map using Object Layer (I am calling it CollisionOL).

On Tiled Map initialization, we need to create Box2D bodies for collision objects on Tiled Map we have just created on our Object Layer. Note that in my case Tiled Map is not sized over complete scene, and I have more then 1 Tiled Map on it, so needed to do some more calculations (getWorldPosition). If you are not using it on such way, your _point should be defined with x, y as got from Object Layer.

  1. (void) drawCollisionTiles:(CCTMXTiledMap *)tiledMap
  2. {
  3.  CCTMXObjectGroup *collisionObjects = [tiledMap objectGroupNamed:@"CollisionOL"];
  4.  NSMutableDictionary * objPoint;
  5.  
  6.  int x, y, w, h;
  7.  for (objPoint in [collisionObjects objects]) {
  8.   x = [[objPoint valueForKey:@"x"] intValue];
  9.   y = [[objPoint valueForKey:@"y"] intValue];
  10.   w = [[objPoint valueForKey:@"width"] intValue];
  11.   h = [[objPoint valueForKey:@"height"] intValue];
  12.  
  13.   if (y < 0) y = 0;
  14.    
  15.   CGPoint _point = [self getWorldPosition:x withY:y withTiledMap:tiledMap];  
  16.   CGPoint _size  = ccp(w, h);  
  17.   _point = ccpAdd(_point, ccp(_size.x/2, _size.y/2));
  18.  
  19.   [self makeBox2dObjAt:_point withSize:_size dynamic:false];
  20.  }
  21. }
  22.  
  23. (void) makeBox2dObjAt:(CGPoint)p withSize:(CGPoint)size dynamic:(BOOL)d
  24. {
  25.  b2BodyDef bodyDef;
  26.  
  27.  if(d) bodyDef.type = b2_dynamicBody;
  28.  bodyDef.position.Set(p.x/PTM_RATIO, p.y/PTM_RATIO);
  29.    
  30.  CollisionGameObject *obstacle = [[CollisionGameObject alloc] init];
  31.  [obstacle setType:kGameObjectObstacle];
  32.  obstacle.position = p;
  33.  bodyDef.userData = obstacle;  
  34.  
  35.  b2Body *body = _world>CreateBody(&bodyDef);
  36.  
  37.  // Define another box shape for our dynamic body.
  38.  b2PolygonShape dynamicBox;
  39.  dynamicBox.SetAsBox(size.x/2/PTM_RATIO, size.y/2/PTM_RATIO);
  40.  
  41.  // Define the dynamic body fixture.
  42.  b2FixtureDef fixtureDef;
  43.  fixtureDef.shape = &dynamicBox;
  44.  fixtureDef.density = 0.0f;
  45.  fixtureDef.friction = 1.5f;
  46.  fixtureDef.restitution = 0;
  47.  fixtureDef.isSensor = true;
  48.  
  49.  body>CreateFixture(&fixtureDef);
  50. }

That’s it. Now, the tricky part. You need to assure that your collision objects (obstacles) are moving with your scene layer, or in my case Parallax Node. So in your tick method, together with scene movement, we should move Box2D objects accordingly.

  1. // parallax movement
  2. CGFloat velx =  backgroundXVelocity * aDelta;
  3. CGPoint newPos = ccp(currentBackgroundPos.x + velx, currentBackgroundPos.y);
  4. [parallaxBackground setPosition: newPos];
  5.  
  6. // moving collision objects as well
  7. [self updateBoxBodyPosition:aDelta vel:velx];

Here I am distinguishing my main Game player (Manta) from obstacles. For obstacles, I am updating its world position according to scene movement and then updating the position of all Box2D objects based on the position of the Cocos2D sprites as is nicely explained in Ray tutorial. Note that my world has only X movement. You will need to take care about Y axe as well.

  1. (void) updateBoxBodyPosition:(float)dt vel:(float)v
  2. {
  3.  int32 velocityIterations = 10;
  4.  int32 positionIterations = 10;
  5.  
  6.  _world>Step(dt, velocityIterations, positionIterations);
  7.  
  8.  for(b2Body *b = _world>GetBodyList(); b; b=b>GetNext()) {
  9.   if (b>GetUserData() != NULL) {
  10.    CCSprite *sprite = (CCSprite *)b>GetUserData();  
  11.    if (sprite != nil) {
  12.        
  13.     // Handling obstacle position according to Parallax movement
  14.     if (sprite.tag != LevelLayerNodeTagManta) {    
  15.      sprite.position = ccpAdd(sprite.position, ccp(v, 0));      
  16.     }
  17.    
  18.     b2Vec2 b2Position = b2Vec2(sprite.position.x/PTM_RATIO, sprite.position.y/PTM_RATIO);
  19.     float32 b2Angle = -1 * CC_DEGREES_TO_RADIANS(sprite.rotation);    
  20.     b>SetTransform(b2Position, b2Angle);                    
  21.    }  
  22.   }
  23.  }  
  24. }

I am sure there are other, maybe better, ways of doing it, so if you have any suggestion of improvement, or this tutorial is not clear enough for you, just write the comment.

I will not include sample project including this code since you could get complete iUridium source from here šŸ˜‰
.


Read full storyComments { 0 }

How to animate spots on Tilemap

This issue gave me a lot of headache, so just want to share it with you. If may help you, I will be more then happy šŸ˜‰

I have a large parallax background and just wanted to animate one spot on my map. What I needed you can see in iUridium video at this link (blinking hatch on Dreadnought map for spawning Torpedos). What I’ve used are Tiled and Zwoptex tools, but I can assume using other tools should make not much difference. In this procedure I asume you are familiar with these tools. So, what I did is as follows:

1. First, I have used some graphical editor and created animated frames (images) for the blinking hatch (actually, just changed the hatch color)

2. Then, I have used Zwoptex to create Texture Atlas containing animated frames (saved it as “blinkframes.plist”)

3. Using Tiled I created new Tileset with newly made Texture Atlas

4. I have created new Tilemap layer (let us call it “Blink”)

5. I selected initial image from my new Tileset and put it on new Layer on position where blinking hatch should appear

6. The most important, I have used following code to load Texture Atlas:

  1. [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:
  2.                     @"blinkframes.plist" texture:[tileMap layerNamed:@"Blink"].texture];

7. For animation, I have used standard way to animate hatch on my map (of course, do not forget to use SpriteFrames from Texture Atlas):

  1. float animationSpeed = 0.25;
  2. NSMutableArray *animationArray = [NSMutableArray array];
  3.  
  4. [animationArray addObject: [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"blink1.png"]];
  5. [animationArray addObject: [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"blink2.png"]];
  6. [animationArray addObject: [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:@"blink3.png"]];
  7.  
  8. CCAnimation *animation = [CCAnimation animationWithName:@"animation" delay:animationSpeed frames:animationArray];
  9. CCAction *action = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:animation restoreOriginalFrame:NO]];
  10.  
  11. CCTMXLayer *layer = [tileMap layerNamed:@"Blink"];
  12. CGPoint position = CGPointMake(9.0f, 8.0f);
  13.  
  14. CCSprite *tile = (CCSprite*) [layer tileAt:position];
  15. [tile runAction:action];

And, that’s it. I hope I was clear enough in this post, if not feel free to comment it.

.


Read full storyComments { 9 }