Welcome to my Programming Blog...

I am currently working on a couple of projects: An original game called "Implosion" which I am porting from Flash to the iPad, and a remake of Q*bert in Python (pygame), as part of the class I am teaching. Please feel free to use the "Labels" (at right) to follow a specific project or theme. If you are one of my Python students, I recommend that you check out the Python thread.

Wednesday, January 12, 2011

Making cocos2d TileMaps, part 3

Continued from part 2
Still with substantial attribution to  Ray Wenderlich, Steffen Itterheim and the cocos2d-iphone tutorial!

So in parts 1 & 2, we created a tileset and made it appear on the screen and move so that the penguin stays centered in the screen and still moves through the world. The next thing is to interact with the things in that tiled world. In part 1, we created a tiled layer on top of the primary layer that represented things that were solid- things that we couldn't move a penguin through. Now to make it act that way!

In this program, the solidity of these objects only matters when the penguin is trying to move - if it is trying to move into a solid spot, we need to prevent that. This requires us to find out which tile the penguin is on (or moving into) and whether the "obstacle" tile there is a solid one.

Here's the math: Deciding on which tile the penguin is in is a matter of converting a set of pixel coordinates that might go as high as, say 2400 x 2400 into a row and column of tiles that are 24 x 24 pixels each (or another size of your choosing). So we might find the penguin at, say (1234, 567) and find that it refers to pixel coordinates (51, 77). This can be done by dividing the coordinates by the tile size and rounding down. (The coordinate on the y-axis gets subtracted from the max coordinate - the tiles get counted down from the top, but the pixels get counted from the bottom of the screen.) Try it!
So here's the code to make the conversion:

CGPoint penguinGrid = ccp((int)([thePenguin getPosition].x/theMap.tileSize.width),
                          (int)(theMap.mapSize.height - ([thePenguin getPosition].y/theMap.tileSize.height)));

So the next step, once we know which tile location we are on, is to find out what is there. In our example, I wrote a short function that asks the map for the information on the "obstacles" layer, gets the "global identifier" (GID) of the tile at that location, and looks up to see whether it is a tile that has a "solid" property set to "True."

// determines whether the tile at the given tile coordinates in the obstacles layer
//    has a property of "solid" set to "True." If there is no tile there, it doesn't
//  have any properties, it doesn't have a "solid" property, or the property isn't
//  "True", then the answer is "NO."
-(BOOL)tileIsAnObstacle:(CGPoint)loc
{
    CCTMXLayer* obstaclesLayer = [theMap layerNamed:@"obstacles"];
    NSAssert(nil!=obstaclesLayer, @"Can't find a tile in the obstacles layer - no such layer found.");
    int theGID = [obstaclesLayer tileGIDAt:loc];
    if (theGID!=0) {
        NSDictionary* properties = [theMap propertiesForGID:theGID];
        if(nil!=properties)
        {
            NSString* solidStatus = [properties valueForKey:@"solid"];
            if (nil!=solidStatus && [solidStatus compare:@"True"] == NSOrderedSame)
                return YES;
        }
    }
    return NO;
}


So what we can do is to check - if we want to move one cell to the right, is that square currently occupied by a "solid" tile? We need to consider which cell we are trying to move into:
CGPoint targetCell = cpp(penguinGrid.x+1,penguinGrid.y);
and then we can check whether that square is occupied:
if ([self tileIsAnObstacle: targetCell])
    return; // cancel the move!
else
    [thePenguin moveRight];

This should show the basic pieces that go into reading what is in the tile Map. But wait! There's more!

What if you want to change which tile is at a given location? It takes two steps: ask the map for the layer you want to change:
CCTMXLayer* backgroundLayer = [theMap layerNamed:@"background"];
and then we tell that layer to change the GID of a given tile:
[backgroundLayer setTileGID: newGID at: thePoint];
(where newGID is the new value and thePoint is the location in the grid)
How do you figure out what GID to put? This may take some experimentation - I suspect that you can just count across the row of tiles in your tilesheet, like a book, but I haven't tried it. (Can anyone confirm this?)

No comments:

Post a Comment