Building Glitch Run in iOS

Glitch Run

In previous blog posts, we’ve seen how to perform authentication with Glitch in iOS and how to animate Glitch Avatars. With just those two things under our belt, we can starting doing some really interesting things – and what better place to start than building a game. In this post, we’ll see how we can take a basic game concept, turn it into a working app, and then imbue it with a little Glitch magic to give it that little extra something.

The Concept

The first step in building a game – and, it must be said, one of the trickier parts – is coming up with a great idea. No doubt you’ve got many great ideas of your own, but to keep this post to a manageable size, we’re going to keep things pretty simple. You’ve probably seen a number of games along the lines of Canabalt or the The Impossible Game, where you play a character running through a world populated by obstacles that you have to jump over. Controls are minimal – in fact there’s only one action to remember – you tap on the screen to jump. The gameplay is very simple, and is a perfect choice for our example app.

The prospect of building an iOS game can be quite daunting, there are so many things to have to get to grips with before you can really get anywhere: OpenGL, animating sprites, moving objects around, collision detection, and much, much more. That’s quite a lot of stuff to master just to put together a simple game. Luckily, you don’t have to do all this from scratch. There are a number of open source libraries around that can speed things up enormously here.

The first library that we’ll be using is cocos2d. Cocos2d is a popular game engine for iOS. It’s available under the MIT license and has been used in hundreds of successful app store games. The other library we will be taking advantage of is Box2D, a physics engine written by Erin Catto. A physics engine allows you to model your game world, and then takes care of figuring out how all the objects within it move and interact with each other. Again, this library has been used in many popular App Store games, including Angry Birds and Tiny Wings.

A Basic Game

So, we’ll start off with a very simple running game. Now, luckily, we just happen to have one of those ready-prepared, check it out here. It’s a very simple game, with fairly uninspired graphics, but it suits our purpose rather nicely. I recommend you download the source from github, so you can follow along as we go. If you build and run the game, you’ll see that your character is a round circle, which ‘runs’ through an endless landscape, encountering obstacles as you go which you must jump over by tapping the screen. You keep playing by continuing to move forward, if you hit an obstacle and fall backwards, the game ends.


OK, so it’s probably not the most exciting game you’ve ever played, but we’ll be seeing how we can make it a bit more interesting shortly. First, let’s just take a brief look at how the game is structured. Note that we’re not going to dwell too long on the details of basic game code here. If you are new to iOS game development and want to learn more, there are plenty of game-writing tutorials out there to help get you going. A good start would be to look at Ray Wenderlich’s set of iOS tutorials, where he’ll take you step-by-step through many game writing basics.

Building the World

The first thing you might notice is that some of the files have a .mm extension rather than the usual .m that is used by Objective-C. The Box2d library is written in C++, so any of our files that include Box2d headers must be compiled as Objective-C++ rather than Objective-C. The .mm extension is used to flag those files as Objective-C++. Cocos2d uses objects called ‘scenes’ to represent the view of the game seen by the player.

Our game is split into three ‘scenes’. There’s the intro scene, which just consists of the ‘Play’ button to start the game, there’s the game scene itself, and finally there’s the results scene which simply tells you how far you managed to run, and what the high score currently is. Naturally, all the interesting stuff is in the game scene. The main game scene is found in the RunScene.m file. Cocos2d scenes can contain a number of layers, stacked on top of each other – rather like you might find in a Photoshop file. Our RunScene contains two layers, a background layer and a game layer. The background layer consists of an single image which tiles horizontally. As our character runs, we simple scroll the background image to the left, giving the impression of movement. The game layer, found in RunGameLayer.mm is where we deal with the game world itself. You probably spotted that it has a .mm extension – that’s because it’s inside here that interact with the Box2d code.

The Box2d physics engine is used to model our ‘world’. Our run game’s world is a pretty simple one, but Box2d will make it easy to figure out where our character is, when he’s jumping or landing, and, most importantly, when he collides with an object. The basic world model is very simple to set up:

// Define the gravity vector.
b2Vec2 gravity;
gravity.Set(0.0f, -10.0f);
bool doSleep = true;
world = new b2World(gravity, doSleep);
world->SetContinuousPhysics(true);

// Define the ground body.
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(0, 0); // bottom-left corner
groundBodyDef.type = b2_staticBody;
self.ground = world->CreateBody(&groundBodyDef);

// Define the ground box shape.
b2PolygonShape groundBox;		

// Define the ground as being a few screen widths wide, and we'll move it to follow us
// That way, we'll never run out of space
CGFloat w = screenSize.width * 5/PTM_RATIO;
groundBox.SetAsEdge(b2Vec2(-w/2, 0.5), b2Vec2(w/2, 0.5));
self.ground->CreateFixture(&groundBox, 0);

Here we’ve defined a world object which contains a single object – the ground. Now, we don’t know how far our character is going to run, so rather than try to create an infinitely long ground object, we’ve just defined it to be a few screen widths in length. As the player runs, we’ll move the ground along with him so he always has a solid surface to stand on.

Next we create an object to represent our character and place him in the world:

self.jumper = [Box2DSprite spriteWithSpriteFrameName:@"Circle.png"];

CGSize spriteSize = self.jumper.contentSize;
b2Vec2 jumperPos = b2Vec2(0, 0.6); 

[self createBodyAtLocation:jumperPos
                      type:b2_dynamicBody
                 forSprite:self.jumper
                 friction:0.02
              restitution:0.2
                  density:10
                    isBox:NO];

b2Body *jumperBody = self.jumper.body;
jumperBody->SetFixedRotation(YES);

CGPoint screenPos = [self convertWorldToScreen:jumperPos];
self.jumper.position = ccp(screenPos.x + spriteSize.width/2, screenPos.y + spriteSize.height/2);
[self addChild:self.jumper z:20];

Finally, we need some obstacles. Rather than create an endless number of obstacles, we just keep track of a small number. As the obstacles disappear off the left hand side of the screen, we remove them from the world, and create a new one to the right of the currently visible area. That way, Box2D only has to keep track of a small number of objects in world at any one time. When creating an obstacle, we simple choose one at random from the types available, and position it a random distance away.

NSString *obstacleName = @"Circle.png";
switch (arc4random() % 4)
{
    case 0:
        obstacleName = @"Circle.png";
        break;

    case 1:
        obstacleName = @"Square.png";
        break;

    case 2:
        obstacleName = @"SmallSquare.png";
        break;

    case 3:
        obstacleName = @"Triangle.png";
        break;
}

Box2DSprite *obstacle = [Box2DSprite spriteWithSpriteFrameName:obstacleName];
CGSize spriteSize = obstacle.contentSize;

float32 newX = self.lastObstacleLocation + self.minDistanceToNextObstacle + ((float32)(arc4random() % (int)(self.variantDistanceToNextObstacle * 100)))/100;
b2Vec2 pos = b2Vec2(newX, 0.5 + (spriteSize.height/2)/PTM_RATIO);
CGPoint screenPos = [self convertWorldToScreen:pos];

obstacle.position = ccp(screenPos.x + spriteSize.width/2, screenPos.y + spriteSize.height/2);
[self addChild:obstacle z:10];
[self.obstacles addObject:obstacle];

NSString *shapeName = [obstacleName stringByDeletingPathExtension];

[self createBodyAtLocation:pos
                      type:b2_staticBody
                 forSprite:obstacle
             fromShapeName:shapeName];

As the game progresses, the obstacles appear closer and closer together until a collision is inevitable.

So, that’s our game world build, now we need to put things in motion. At the heart of most games is the game loop. This is called repeatedly by the game engine, and on each iteration, the positions and interactions of all the game objects is recalculated and the scene redrawn to shown the new state of the world. Our game loop is very simple. We tell Box2d to update it’s model of the world:

world->Step(deltaTime, velocityIterations, positionIterations);
world->ClearForces();

and then iterate through each object in the world, drawing it in it’s new location:

for (b2Body* b = world->GetBodyList(); b; b = b->GetNext())
{
    if (b->GetUserData() != NULL)
    {
        CCSprite *ccs = (CCSprite*)b->GetUserData();
        // We're moving our frame of reference to keep the jumper in the same position on screen
        ccs.position = CGPointMake( (b->GetPosition().x - jumperX) * PTM_RATIO + screenSize.width/2 - self.jumperOffset, b->GetPosition().y * PTM_RATIO);
        ccs.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle());
    }
}

And we simulate the effect of our player running, by applying a horizontal impulse to propel him along a little further:

b2Vec2 impulse = b2Vec2(0.6, 0);

b2Vec2 bodyCenter = jumperBody->GetWorldCenter();

jumperBody->ApplyLinearImpulse(impulse, bodyCenter);

To make the character jump whenever we tap the screen, we similarly apply a vertical component to the impulse:

b2Vec2 impulse = b2Vec2(0.1, 40.0);
b2Vec2 bodyCenter = jumperBody->GetWorldCenter();
jumperBody->ApplyLinearImpulse(impulse, bodyCenter);

And that’s pretty much all we need to do – Box2d does all the rest of the work for us. It will monitor the velocity of the player, based on the impulses we apply, and work out just where he will be at any point in time, taking account gravity, friction and the position of other objects that might get in the way. If we ever spot the player moving backwards, we know they’ve hit an obstacle and bounced back, so that’s game over:

if (v.x <= 0.0)
{
    // We've stopped or are going backwards
    [self crash];
}

Glitchification

Now we have our basic game, we can look at sprucing it up a little. That circle isn’t very exciting now, is it? The game would be much, much better if we could use our Glitch avatar here instead. That checkered background? It needs to go – a run through Groddle Meadow would be way more fun. And as for those obstacles – we don’t want to jump over geometric blocks – we should be jumping over shrines and rocks and pigs! It’s time for Glitchification!

You can see all the code for the Glitchified version of the game here. The basic game is exactly the same as the one we’ve just discussed, but if you build and run it, you’ll see that the addition of a little Glitch magic make a world of difference!

So, let’s take a look at what we did to transform the game so dramatically.

Sprites

We discussed how to build avatar animations in iOS in this previous post – if you didn’t read that one, then this would be a really good time to go check it out. The avatar animation code used in Glitch run is essentially the same as in our animations tutorial, although there are a few significant differences. The animations tutorial showed how to present an animated avatar within a UIView. Now that we’re using cocos2d, we need to build a CCSprite object instead, and create a set of CCAnimation objects to hold the animation sequences:

-(CCAnimation *)animationForName:(NSString *)name
{
    NSString *animationName = [NSString stringWithFormat:@"avatar-%@", name];
    CCAnimation *animation = [[CCAnimationCache sharedAnimationCache] animationByName:animationName];
    if (animation == nil)
    {
        NSArray *frames = [self framesForAnimationName:name];
        animation = [CCAnimation animation];
        for (GlitchAvatarAnimationFrame *f in frames)
        {
            NSString *frameName = [NSString stringWithFormat:@"avatar-frame-%d", [f->frameNumber integerValue]];
            CCTexture2D *texture = [[CCTextureCache sharedTextureCache] textureForKey:frameName];
            CGRect frameRect = f->frame;
            if (texture == nil)
            {
                CGImageRef spritesheet = [self spritesheetForName:f->sheetName];
                CGImageRef frameRef = CGImageCreateWithImageInRect(spritesheet, frameRect);
                texture = [[CCTextureCache sharedTextureCache] addCGImage:frameRef forKey:frameName];
                CGImageRelease(frameRef);
            }
            CGRect rect = CGRectMake(0, 0, frameRect.size.width, frameRect.size.height);
            [animation addFrameWithTexture:texture rect:rect];
        }
        [[CCAnimationCache sharedAnimationCache] addAnimation:animation name:animationName];
    }
    return animation;
}

We first of all check cocos2d’s shared animation cache to see if we’ve already created the animation. If not, we use Core Graphics to slice up the spritesheet into individual frames and create CCTexture2D objects from them. These textures are added to the animation, which is then cached for later use.

Now that we have Box2D keeping track of our avatar’s movement, we need some way of figuring out which animation we should be showing at any given time. The range of actions in our game are pretty limited (run, jump), as are the animations that get displayed (run, jump up, jump down). We can use the velocity and position of our avatar, as calculated by Box2D, to figure out just what animation is appropriate. A simple set of animation ‘rules’ would look something like this:

  • If we’re on a horizontal surface, and not moving, we should use the ‘idle’ animation.
  • If we’re on a horizontal surface, and have a positive horizontal component to our velocity, we should use the ‘running’ animation.
  • If we’re not in contact with a surface and have a positive vertical component to our velocity, we should show the ‘jumping up’ animation.
  • If we’re not in contact with a surface and have a negative vertical component to our velocity, we should show the ‘falling’ animation.

If you take a look at the GlitchAvatarSprite class, you’ll see these rules implemented. In practice, we also need to add a few extra tweaks to check for some edge cases, and help smooth the animation transitions, but the above rules form the core of the animation logic:

-(void)animateBasedOnVelocity
{
    b2Vec2 v = self.body->GetLinearVelocity();

    if (onGround && fabs(v.x) < kTinyAmount && fabs(v.y) < kTinyAmount)     {         [self idle];     }        else if (onGround && fabs(v.x) > kTinyAmount)
    {
        [self run];
    }
    else if (v.y > kModestAmount)
    {
        [self jumpUp];
    }
    else if (v.y < kModestAmount)
    {
        [self landAndWhenComplete:^(void)
        {
            [self run];
        }];
    }
    else
    {
        // Keep doing whatever we were doing until something changes.
    }
}

Some Useful Tools

Now we’ve got a rather snazzy looking avatar to display, we really need to update the rest of the graphics to suit. The dull checkered background can be simply swapped out for a nice Groddle scene, and our rather boring obstacles are replaced with much better looking shrines and pigs. If you dig around in the Resource directory, you’ll notice that these files aren’t stored individually, but instead are stored as in a single spritesheet.

There is a corresponding plist file which defines where the individual sprites can be found within the spritesheet:

<dict>
    <key>frames</key>
        <key>Piggy</key>
        <dict>
        <key>frame</key>
        <string>{{2,655},{156,101}}</string>
        <key>offset</key>
        <string>{0,0}</string>
        <key>rotated</key>
        <false/>
        <key>sourceColorRect</key>
        <string>{{0,0},{156,101}}</string>
        <key>sourceSize</key>
        <string>{156,101}</string>
    </dict>
</dict>

This is by far the most efficient way to add in-game graphics, and there are number of tools available to help generate these files. I used TexturePacker [http://www.texturepacker.com/ ] to build these files, and Zwoptex [http://zwoptexapp.com/ ] is another popular choice.

Now we’ve added obstacles with more complex shapes, we need to be sure that Box2D knows how to detect collisions with these objects. We do that by creating another plist file that defines the physics of the objects:

<dict>
    <key>bodies</key>
    <dict>
        <key>Shrine</key>
        <dict>
            <key>polygons</key>
            <array>
                <array>
                    <string>{ -21.650,48.808 }</string>
                    <string>{ -16.150,49.308 }</string>
                    <string>{ -23.150,56.308 }</string>
                    <string>{ -24.150,53.308 }</string>
                </array>
            ...

It’s rather tedious building these files by hand and once again, there are tools available to assist you. Physics Editor will automatically generate the outline of your objects and let you tweak to suit:

Here, for example, we’ve adjusted the shape of the shrine object to exclude the surrounding foliage, and to provide a flat top that can be run along.

One final tool worth mentioning is Glyph Designer – this app lets you create the font equivalent of a spritesheet to embed in your app, allowing you to use your own custom fonts, rather than just those that come with iOS:

So, now you’ve seen how Glitch Run was put together. Perhaps you’ve got some game ideas of your own that could benefit from the addition of a little Glitch goodness? If you have any questions about Glitch Run or how to integrate Glitch into your own apps and games, please feel free to post in the developer forums and we’ll help answer your questions as best we can.

David Wilkinson

This entry was posted in spritesheet, Animation, SDKs, API, iOS and tagged Glitch, animation, API, SDK, iOS, Platformer