Collisions and Collectables: How To Make a Tile-Based Game with Cocos2D 2.X Part 2

In the this part of the tutorial, we’ll cover how to make collidable areas in the map, how to use tile properties, how to make collectable items and modify the map dynamically, and how to make sure your ninja doesn’t overeat. By Charlie Fulton.

Leave a rating/review
Save for later
Share

Contents

Hide contents

Update 1/17/2013 Fully updated for Cocos2D 2.1 rc0, Tiled 0.9.0 (released Feb 2013), Xcode 4.6, and ARC. (original post by Ray Wenderlich, update by Charlie Fulton).

Mmm, that was tasty!

Mmm, that was tasty!

Mmm, that was tasty!

This is the second part of a 2-part tutorial where we cover how to make a tile-based game with Cocos2D and the Tiled map editor. We are creating a simple tile-based game where a ninja explores a desert in search of tasty watermelon-things!

In the first part of the tutorial, we covered how to create a map with Tiled, how to add the map to the game, how to scroll the map to follow the player, and how to use object layers.

In the this part of the tutorial, we’ll cover how to make collidable areas in the map, how to use tile properties, how to make collectable items and modify the map dynamically, and how to make sure your ninja doesn’t overeat.

So let’s pick up where we left off last time and make our map a bit more game-like!

Tiled Maps and Collisions

You may have noticed that currently the ninja can move right through walls and obstacles with no problem at all. He is a ninja, but even ninjas aren’t that good!

So we need to figure out a way to mark some tiles as “collidable” so we can prevent the player from moving into those positions. There are many possible solutions to this (including using object layers), but I’m going to show you a new technique that I think is effective and also a good learning exercise – using a meta layer and layer properties.

Let’s dive right in! Load up Tiled again, click “Layer\Add Tile Layer…”, name the Layer “Meta”, and click OK. This is the layer we will be putting a few fake tiles in to indicate “special tiles”.

So now we need to add our special tiles. Click “Map\New Tileset…”, browse to meta_tiles.jpg in your TileGame\TileGameResources folder, and click Open. Set the Margin and Spacing to 1 and click OK.

With the layers window selected, click on meta_tiles in the Tilesets window. You will see two tiles: red and green.

Tiled and Meta Tiles

There is nothing at all special about these tiles – I just made a simple image with two red and green tiles with partial transparency. However we’re going to decide that red means “collidable” (we’ll use green later on), and paint our scene appropriately.

So make sure the Meta layer is selected, choose the stamp tool, choose the red tile, and paint over any object that you want the ninja to collide with. When you’re done it might look like the following:

Tiled and Painting Tiles for Collision Detection

Next, we need to set a property on the tile to flag it so we can recognize in code that this is the tile that is “collidable.” Right click on the red tile in the Tilesets section, and click “Tile Properties…”. Add a new property for “Collidable” set to “True” like the following:

Tile Properties in Tiled

Save the map and return to Xcode. Make the following changes to HelloWorldLayer.m:

// Inside the HelloWorldLayer interface with the other private properties
@property (strong) CCTMXLayer *meta;

// In init, right after loading background
self.meta = [_tileMap layerNamed:@"Meta"];
_meta.visible = NO;

// Add new method
- (CGPoint)tileCoordForPosition:(CGPoint)position {
    int x = position.x / _tileMap.tileSize.width;
    int y = ((_tileMap.mapSize.height * _tileMap.tileSize.height) - position.y) / _tileMap.tileSize.height;
    return ccp(x, y);
}

Ok let’s stop here a second. We declare a member/variable for the meta layer as usual, and load a reference from the tile map. Note that we mark the layer as invisible since we don’t want to see these objects, they are for annotating what is collidable only.

Next we add a new helper method that helps us convert x,y coordinates to “tile coordinates”. Each of the tiles has a coordinate, starting with (0,0) for the upper left and (49,49) for the bottom right (in our case).

Tile Coordinates in Tiled

The above screenshot is from the Java version of Tiled, btw. Showing the coordinates for tiles is a feature I don’t think they’ve ported to the Qt version yet.

Anyway, some of the functions we’re about to use require tile coordinates rather than x,y coordintes, so we need a way to convert the x,y coordinates into tile coordinates. This is exactly what this function does!

Getting the x coordinate is easy – we just divide it by the width of a tile. To get the y coordinate, we have to flip things around because in Cocos2D (0,0) is at the bottom left, not the top left.

Next replace the contents of setPlayerPosition with the following:

CGPoint tileCoord = [self tileCoordForPosition:position];
int tileGid = [_meta tileGIDAt:tileCoord];
if (tileGid) {    
    NSDictionary *properties = [_tileMap propertiesForGID:tileGid];
    if (properties) {        
        NSString *collision = properties[@"Collidable"];
        if (collision && [collision isEqualToString:@"True"]) {            
            return;
        }
    }
}
_player.position = position;

Here we convert the x,y coordinates for the player to tile coordinates. Then we use the tileGIDAt function in the meta layer to get the GID at the specified tile coordinate.

Huh, what’s a GID? GID stands for “globally unique identifier” (I think). But in this case I like to think of it as the id for the tile we’re using, which would be the red square if that’s where we’re trying to move.

We then use the GID to look up properties for that tile. It returns a dictionary of properties, so we look through to see if “Collidable” is set to true, and if it is we return immediately, hence not setting the player position and making the move invalid.

And that’s it! Compile and run the project, and you should now no longer be able to walk through any tiles you painted red:

Screenshot demonstrating you cannot move through walls

Modifying the Tiled Map Dynamically

So far our ninja is having a fine time exploring, but this world is a little dull. There’s simply nothing to do!

Plus our ninja looks a bit hungry. So let’s spice things up by giving our ninja something to eat.

For this to work, we’re going to have to create a foreground layer for any objects we want the user to collect. That way, we can simply delete the tile from the foreground layer when the ninja picks it up, and the background will show through.

So open up Tiled, go to “Layer\Add Tile Layer…”, name the layer “Foreground”, and click OK. Make sure the Foreground layer is selected, and add a couple collectibles to your map. I liked to use the tile that looks like a Watermelon or something to me.

Adding collectibles to the Tile Map

Now, we need to mark those tiles as collectible, similarly to how we marked some of the tiles as collidable. Select the meta layer, switch over to the meta_tiles, and paint a green tile over each of your collectables. You’ll have to click “Layer\Raise Layer” to make sure the meta layer is on top so that the green is visible.

Painting collectables with meta tiles

Next, we need to add the property to the tile to mark it as collectable. Right click on the green tile in the Tilesets section, click “Tile Properties…” and add a new property with name “Collectable”, value “True”.

Save the map and go back to Xcode. Make the following changes to HelloWorldWorldLayer.m:

// in HelloWorldLayer interface declaration with the other private properties
@property (strong) CCTMXLayer *foreground;

// In init, right after loading background
self.foreground = [_tileMap layerNamed:@"Foreground"];

// Add to setPlayerPosition, right after the if clause with the return in it
NSString *collectible = properties[@"Collectable"];
if (collectible && [collectible isEqualToString:@"True"]) {
    [_meta removeTileAt:tileCoord];
    [_foreground removeTileAt:tileCoord];    
}

Here is standard stuff to keep a reference to the foreground layer. The new thing is we check to see if the tile the player is moving to has the “Collectable” property. If it does, we use the removeTileAt method to remove the tile from both the meta layer and the foreground layer.

Compile and run the project, and now your ninja will be able to dine on tasty-melon-thingie!

Screenshot of Ninja about to eat a melon

Charlie Fulton

Contributors

Charlie Fulton

Author

Over 300 content creators. Join our team.