How to build a game like Flappy Bird with Xcode and SpriteKit

How to build a game like Flappy Bird with Xcode and SpriteKit

When I heard about Flappy Bird for the first time, the hype was already in its final phase: developer Dong Nguyen had decided to pull the app from the stores in “22 hours from now“. Apart from overreacting users and an allegedly daily advertising revenue of up to 50,000 US$, the simple game mechanics were something that caught my attention. The concept of clicking/tapping to keep the player on track wasn’t really an innovation in itself, but given the hype it caused, it was certainly worth a closer look. As a Sunday evening challenge, I tried to reproduce the game mechanics and ended up with less than 200 lines of code.

Update Feb. 20th: I added the score counting part and included suggestions by readers Sascha and Nico. Updated code is pushed to GitHub.

Update Feb. 27th: Fixed a small glitch in the tutorial that DylanYasen pointed out, thanks! (No code change.)

Update Apr. 9th: Sorry, I currently don’t have time to update the tutorial. In the meantime, take a look what reader Daniel Bocksteger built with the learnings from this tutorial: Coin Catcher.

Graphic assets

Since the app was already removed from the stores when I started working on the clone, I had to rely on screenshots and videos of the original game to reproduce the individual sprites. I used The Gimp to produce the bird (two simple frames for animation), the pipes (upper and lower version), the distant background and the ground.

Art creation with The Gimp

Note that the created files are a little too small for an iPhone display. Therefore, in the course of this tutorial, you’ll see that I simply doubled the size by scaling every sprite by a factor of 2. In order to maintain the 8 bit retro look I changed the filtering mode of the textures from SKTextureFilteringLinear (standard) to SKTextureFilteringNearest.

You can download the created files with the final project.

Xcode project setup

iOS 7 comes with a great new framework called Sprite Kit. It hides lots of the iOS UI development details game programmers are not necessarily familiar with (outlets anyone?) and builds upon a basic game infrastructure they are are more used to (e.g. rendering loop and scene graph). For all iOS developers, focussing on the game mechanics becomes a breeze.

When Xcode starts, simply select “Create a new Xcode project” from the splash screen.

XCode start

From the available project templates, select “SpriteKit Game”.

Select SpriteKit Game

Now enter some project details. You can change them later if you need to.

Enter Project Options

Select a place to store the project files. I usually create a new subfolder in my Documents folder.

Finally, the project is created and we’re ready for development.

04-project-created

The bird

The template project comes with some sample code and assets that need to be removed. Delete the Spaceship.png graphic from the project and add the Bird1.png and Bird2.png files instead. Also remove the existing code in the initWithSize and touchesBegan methods of MyScene.m.

In initWithSize, create the bird sprite and add it to the scene. To access the bird easily from other class methods, we declare it as an ivar.

#import "MyScene.h"

@interface MyScene () {
    SKSpriteNode* _bird;
}
@end

@implementation MyScene

-(id)initWithSize:(CGSize)size {    
    if (self = [super initWithSize:size]) {
        /* Setup your scene here */
        
        SKTexture* birdTexture1 = [SKTexture textureWithImageNamed:@"Bird1"];
        birdTexture1.filteringMode = SKTextureFilteringNearest;
        
        _bird = [SKSpriteNode spriteNodeWithTexture:birdTexture1];
        [_bird setScale:2.0];
        _bird.position = CGPointMake(self.frame.size.width / 4, CGRectGetMidY(self.frame));
        
        [self addChild:_bird];
    }
    return self;
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    /* Called when a touch begins */
}

-(void)update:(CFTimeInterval)currentTime {
    /* Called before each frame is rendered */
}

@end

One important thing to remember is that the SpriteKit’s scene coordinate system has it’s origin at the bottom left point and the positive y- and x-axes extend to the top and right of the screen. A sprite’s default anchor point is always the center. Using the code above, the bird is placed on the vertical center and end of the horizontal first quarter of the screen.

Bird added to the scene

Hooray, we’re nearly there! It won’t get much harder than that, believe me.

Animating the bird

Animating is pretty easy thanks to SpriteKit Actions. We simply create a sequence of texture-changes and loop them endlessly with a little delay in order to produce the illusion of flapping wings. I believe that the original game used some more frames, but we’ll go with two for the sake of simplicity.

We’ll have to load the second bird sprite, too, produce an SKAction to animate them, and attach this animation to the bird.

SKTexture* birdTexture1 = [SKTexture textureWithImageNamed:@"Bird1"];
birdTexture1.filteringMode = SKTextureFilteringNearest;
SKTexture* birdTexture2 = [SKTexture textureWithImageNamed:@"Bird2"];
birdTexture2.filteringMode = SKTextureFilteringNearest;
        
SKAction* flap = [SKAction repeatActionForever:[SKAction animateWithTextures:@[birdTexture1, birdTexture2] timePerFrame:0.2]];
        
_bird = [SKSpriteNode spriteNodeWithTexture:birdTexture1];
[_bird setScale:2.0];
_bird.position = CGPointMake(self.frame.size.width / 4, CGRectGetMidY(self.frame));
[_bird runAction:flap];

This results in a simple animation like this:

Bird flapping its wings

Adding ground and background

Most of the screen background is a bright blue sky. Let’s fill the screen accordingly. We store the sky color in an ivar for later…

@interface MyScene () {
    SKSpriteNode* _bird;
    SKColor* _skyColor;
}
@end

@implementation MyScene

-(id)initWithSize:(CGSize)size {    
    if (self = [super initWithSize:size]) {
        /* Setup your scene here */
        
        _skyColor = [SKColor colorWithRed:113.0/255.0 green:197.0/255.0 blue:207.0/255.0 alpha:1.0];
        [self setBackgroundColor:_skyColor];

The background and ground sprites are repeating patterns. After adding Skyline.png and Ground.png to the project, we setup the textures first and then fill the full screen width with the sprites.

// Create ground
        
SKTexture* groundTexture = [SKTexture textureWithImageNamed:@"Ground"];
groundTexture.filteringMode = SKTextureFilteringNearest;

for( int i = 0; i < 2 + self.frame.size.width / ( groundTexture.size.width * 2 ); ++i ) {
    SKSpriteNode* sprite = [SKSpriteNode spriteNodeWithTexture:groundTexture];
    [sprite setScale:2.0];
    sprite.position = CGPointMake(i * sprite.size.width, sprite.size.height / 2);
    [self addChild:sprite];
}
        
// Create skyline
        
SKTexture* skylineTexture = [SKTexture textureWithImageNamed:@"Skyline"];
skylineTexture.filteringMode = SKTextureFilteringNearest;
        
for( int i = 0; i < 2 + self.frame.size.width / ( skylineTexture.size.width * 2 ); ++i ) {
    SKSpriteNode* sprite = [SKSpriteNode spriteNodeWithTexture:skylineTexture];
    [sprite setScale:2.0];
    sprite.zPosition = -20;
    sprite.position = CGPointMake(i * sprite.size.width, sprite.size.height / 2 + groundTexture.size.height * 2);
    [self addChild:sprite];
}

This leads to the following static scene.

Background, skyline and ground updated

You may have noticed that I created one more tile than necessary. This is required for the animation: We can create the illusion of movement by simply moving each sprite to the left for a full width and then resetting it to the original position. When the tiles wander to the left, a gap opens at the right edge of the screen. We use the extra tile to fill that gap.

// Create ground
        
SKTexture* groundTexture = [SKTexture textureWithImageNamed:@"Ground"];
groundTexture.filteringMode = SKTextureFilteringNearest;
        
SKAction* moveGroundSprite = [SKAction moveByX:-groundTexture.size.width*2 y:0 duration:0.02 * groundTexture.size.width*2];
SKAction* resetGroundSprite = [SKAction moveByX:groundTexture.size.width*2 y:0 duration:0];
SKAction* moveGroundSpritesForever = [SKAction repeatActionForever:[SKAction sequence:@[moveGroundSprite, resetGroundSprite]]];

for( int i = 0; i < 2 + self.frame.size.width / ( groundTexture.size.width * 2 ); ++i ) {
    // Create the sprite
    SKSpriteNode* sprite = [SKSpriteNode spriteNodeWithTexture:groundTexture];
    [sprite setScale:2.0];
    sprite.position = CGPointMake(i * sprite.size.width, sprite.size.height / 2);
    [sprite runAction:moveGroundSpritesForever];
    [self addChild:sprite];
}
        
// Create skyline
        
SKTexture* skylineTexture = [SKTexture textureWithImageNamed:@"Skyline"];
skylineTexture.filteringMode = SKTextureFilteringNearest;
        
SKAction* moveSkylineSprite = [SKAction moveByX:-skylineTexture.size.width*2 y:0 duration:0.1 * skylineTexture.size.width*2];
SKAction* resetSkylineSprite = [SKAction moveByX:skylineTexture.size.width*2 y:0 duration:0];
SKAction* moveSkylineSpritesForever = [SKAction repeatActionForever:[SKAction sequence:@[moveSkylineSprite, resetSkylineSprite]]];
        
for( int i = 0; i < 2 + self.frame.size.width / ( skylineTexture.size.width * 2 ); ++i ) {
    SKSpriteNode* sprite = [SKSpriteNode spriteNodeWithTexture:skylineTexture];
    [sprite setScale:2.0];
    sprite.zPosition = -20;
    sprite.position = CGPointMake(i * sprite.size.width, sprite.size.height / 2 + groundTexture.size.height * 2);
    [sprite runAction:moveSkylineSpritesForever];
    [self addChild:sprite];
}

Two layers of the scene are scrolling with different pace (speed scale factors 0.1 and 0.02 above), leading to an illusion of depth. This effect has been used in 2D video games for ages and is called parallax scrolling.

Parallax scrolling

Let’s add some physics

Until now, everything in the scene is just fake. The bird doesn’t even move. But we can easily enable the SpriteKit physics engine in order to add some realistic movement to the scene.

SpriteKit allows to attach a physics body to every scene node. The physics body has certain properties like mass, density, friction,… and it can interact with other physics-enabled entities in the scene. Once a physics body is created, the owner node is controlled by physics.

Let’s start with the bird. We will represent it’s physical shape by a circle.

_bird = [SKSpriteNode spriteNodeWithTexture:birdTexture1];
[_bird setScale:2.0];
_bird.position = CGPointMake(self.frame.size.width / 4, CGRectGetMidY(self.frame));
[_bird runAction:flap];

_bird.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:_bird.size.height / 2];
_bird.physicsBody.dynamic = YES;
_bird.physicsBody.allowsRotation = NO;

[self addChild:_bird];

When we start our clone now, we’ll see the bird falling straight down – and out of the screen. In order to avoid that, we will have to create a physics body for the ground as well. We could attach it to each of the ground tiles, but remember that they’re continuously moving left and then resetting their position. This could lead to some strange effects once the bird collides with such a ground tile. So instead, we can simply create an empty node with a rectangular physics body which spans the full ground.

Add this below the creation of the ground sprites:

// Create ground physics container

SKNode* dummy = [SKNode node];
dummy.position = CGPointMake(0, groundTexture.size.height);
dummy.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(self.frame.size.width, groundTexture.size.height * 2)];
dummy.physicsBody.dynamic = NO;
[self addChild:dummy];

Note that the bird has property dynamic set to YES, while the ground (and all other “static” entities) have dynamic set to NO. This is because that only the bird is affected by interactions with the physic world. The remaining entities are under our control and won’t change their position or rotation e.g. due to a collision.

With these changes in place, you’ll see our little bird falling to the ground.

Physics enabled

The bird is falling a little too fast for my taste. We have multiple options to control this behavior, let’s try the easiest and most obvious one first: we’ll change the world’s gravity!

At the beginning of the scene setup, simply add:

self.physicsWorld.gravity = CGVectorMake( 0.0, -5.0 );

This will reduce the gravity from an authentic -9.8m/s^2 to only -5m/s^2, making the bird falling slower.

In order to let the bird raise on a click/touch, we will apply a small directed impulse to the bird’s physics body. You wouldn’t believe how easy this is with SpriteKit. Just add one line to the touchesBegan method:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    /* Called when a touch begins */
    [_bird.physicsBody applyImpulse:CGVectorMake(0, 8)];
}

The result should look like that:

Touches producing an upward directed impulse

Blog reader Sascha suggests to reset the bird’s velocity before the new impulse is applied. This avoids that the impulses accumulate per touch/click. Blog reader Nico suggests to reduce the impulse a little in that case, so the touchesBegan method now looks like that:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    /* Called when a touch begins */
    _bird.physicsBody.velocity = CGVectorMake(0, 0);
    [_bird.physicsBody applyImpulse:CGVectorMake(0, 4)];
}

I strongly encourage you to experiment with these values.

While we’re at it, we can also change the bird’s rotation depending on it’s velocity. If it’s flying upward the beak should point to the top right; if downward, to the bottom right. This can easily happen on a per-frame basis in the update:currentTime method, which was unused up to now.

CGFloat clamp(CGFloat min, CGFloat max, CGFloat value) {
    if( value > max ) {
        return max;
    } else if( value < min ) {
        return min;
    } else {
        return value;
    }
}

-(void)update:(CFTimeInterval)currentTime {
    /* Called before each frame is rendered */
    _bird.zRotation = clamp( -1, 0.5, _bird.physicsBody.velocity.dy * ( _bird.physicsBody.velocity.dy < 0 ? 0.003 : 0.001 ) );
}

As you can see, I use the bird’s physics body velocity property which is maintained by the physics simulation. I added a little helper function clamp which avoids too large values in either direction. Note that the minimum and maximum values (-1, 0.5) are radians.

Pipes

The last game entity are the pipes which are moving in to the scene from the right and need to be avoided. As before, the pipes are textured SKSpriteNodes, they will be animated using SKActions and they’ll get a physics body attached for collision.

Add the two pipe graphics Pipe1.png and Pipe2.png to the project. From a graphical perspective, the pipes are a special entity. While the ground and skyline are always attached to the bottom edge of the screen and the bird always has the same dimensions, the top pipe has to extend from the top edge and the bottom pipe has to extend from the bottom edge. While this could be solved programmatically with the benefit of saving some texture space, I decided to use very high textures instead and not worry about display size anymore.

Just make the pipes long enough

While pipes consist of two SKSpriteNodes, I wrap both nodes into one empty SKNode as a parent. This has the advantage that it is sufficient to position the parent node in the world, while the two childs are positioned relatively to their parent node, so I only need to worry about their vertical position.

The distance between the upper and lower pipe is arbitrary, I chose 100 points.

@implementation MyScene

static NSInteger const kVerticalPipeGap = 100;

-(id)initWithSize:(CGSize)size {

A pair of pipes is created outside of the right screen edge. The lower pipe’s position is chosen randomly somewhere in the lower third of the screen. The upper pipe is added accordingly, considering the defined gap size. The zPosition for the pipes is chosen so that they always are rendered behind the ground.

// Create pipes

SKTexture* _pipeTexture1 = [SKTexture textureWithImageNamed:@"Pipe1"];
_pipeTexture1.filteringMode = SKTextureFilteringNearest;
SKTexture* _pipeTexture2 = [SKTexture textureWithImageNamed:@"Pipe2"];
_pipeTexture2.filteringMode = SKTextureFilteringNearest;

SKNode* pipePair = [SKNode node];
pipePair.position = CGPointMake( self.frame.size.width + _pipeTexture1.size.width * 2, 0 );
pipePair.zPosition = -10;

CGFloat y = arc4random() % (NSInteger)( self.frame.size.height / 3 );

SKSpriteNode* pipe1 = [SKSpriteNode spriteNodeWithTexture:_pipeTexture1];
[pipe1 setScale:2];
pipe1.position = CGPointMake( 0, y );
pipe1.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:pipe1.size];
pipe1.physicsBody.dynamic = NO;
[pipePair addChild:pipe1];

SKSpriteNode* pipe2 = [SKSpriteNode spriteNodeWithTexture:_pipeTexture2];
[pipe2 setScale:2];
pipe2.position = CGPointMake( 0, y + pipe1.size.height + kVerticalPipeGap );
pipe2.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:pipe2.size];
pipe2.physicsBody.dynamic = NO;
[pipePair addChild:pipe2];

SKAction* movePipes = [SKAction repeatActionForever:[SKAction moveByX:-1 y:0 duration:0.02]];
[pipePair runAction:movePipes];

[self addChild:pipePair];

The bird collides with the moving pipe and is pushed out of the screen.

Collision detection

We want these pipes to appear regularly. We will use SKActions again, but this time, in a little different way. Instead of moving the pipes and resetting their position, we will spawn new pipes regularly. We will move the spawn logic into a separate method. For that purpose, we need to store textures and the SKAction to move the pipes in new ivars:

@interface MyScene () {
    SKSpriteNode* _bird;
    SKColor* _skyColor;
    SKTexture* _pipeTexture1;
    SKTexture* _pipeTexture2;
    SKAction* _moveAndRemovePipes;
}
@end

The _moveAndRemovePipes action is created right after the pipe texture loading like so:

CGFloat distanceToMove = self.frame.size.width + 2 * _pipeTexture1.size.width;
SKAction* movePipes = [SKAction moveByX:-distanceToMove y:0 duration:0.01 * distanceToMove];
SKAction* removePipes = [SKAction removeFromParent];
_movePipesAndRemove = [SKAction sequence:@[movePipes, removePipes]];

This will move the pipes by a full screen width plus two times their texture width to make sure that the pipes are out of sight. Then, they are removed from the scene.

The spawn method is responsible for creating new pipes:

-(void)spawnPipes {
    SKNode* pipePair = [SKNode node];
    pipePair.position = CGPointMake( self.frame.size.width + _pipeTexture1.size.width, 0 );
    pipePair.zPosition = -10;
    
    CGFloat y = arc4random() % (NSInteger)( self.frame.size.height / 3 );
    
    SKSpriteNode* pipe1 = [SKSpriteNode spriteNodeWithTexture:_pipeTexture1];
    [pipe1 setScale:2];
    pipe1.position = CGPointMake( 0, y );
    pipe1.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:pipe1.size];
    pipe1.physicsBody.dynamic = NO;
    [pipePair addChild:pipe1];
    
    SKSpriteNode* pipe2 = [SKSpriteNode spriteNodeWithTexture:_pipeTexture2];
    [pipe2 setScale:2];
    pipe2.position = CGPointMake( 0, y + pipe1.size.height + kVerticalPipeGap );
    pipe2.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:pipe2.size];
    pipe2.physicsBody.dynamic = NO;
    [pipePair addChild:pipe2];
    
    [pipePair runAction:_movePipesAndRemove];
    
    [self addChild:pipePair];
}

Finally, we need to ensure that the spawnPipes method is called regularly to produce new pipes and attach the animation to it. This happens with an SKAction which is directly attached to the SKScene.

SKAction* spawn = [SKAction performSelector:@selector(spawnPipes) onTarget:self];
SKAction* delay = [SKAction waitForDuration:2.0];
SKAction* spawnThenDelay = [SKAction sequence:@[spawn, delay]];
SKAction* spawnThenDelayForever = [SKAction repeatActionForever:spawnThenDelay];
[self runAction:spawnThenDelayForever];

This action calls the spawnPipes method, then pauses for two seconds, then repeats.

Now we’ve got beautiful repeating pipes.

Repeating pipes

Collision detection and response

The game is over when the bird hits a pipe, so detecting a hit is our next challenge. SpriteKit provides a dedicated delegate for collision detection, SKPhysicsContactDelegate, which allows to respond whenever two physics entities are in contact. We will implement the delegate in our scene and set the physics world delegate to the scene.

Moreover, we need to setup some categories for the different types of entities in the scene. Right now, all entities collide with each other and we would receive notifications for all these contacts because of the default setup: all relevant properties – discussed below – have value 0. We will use the categories to configure the contact testing in more detail.

@interface MyScene () <SKPhysicsContactDelegate> {
    SKSpriteNode* _bird;
    SKColor* _skyColor;
    SKTexture* _pipeTexture1;
    SKTexture* _pipeTexture2;
    SKAction* _movePipesAndRemove;
}
@end

@implementation MyScene

static const uint32_t birdCategory = 1 << 0;
static const uint32_t worldCategory = 1 << 1;
static const uint32_t pipeCategory = 1 << 2;

static NSInteger const kVerticalPipeGap = 100;

-(id)initWithSize:(CGSize)size {    
    if (self = [super initWithSize:size]) {
        /* Setup your scene here */
        
        self.physicsWorld.gravity = CGVectorMake( 0.0, -5.0 );
        self.physicsWorld.contactDelegate = self;

We can use the categories to configure which entities can collide with each other and for which types of collisions we want to get notified. This is useful for two reasons: 1. the physics engine doesn’t need to test for collisions between e.g. the pipes and the ground (which saves some performance) and 2. we don’t require notifications for all possible types of collisions.

SpriteKit provides three physics body properties for that purpose: categoryBitMask says to which categories an entity belongs, collisionBitMask says which categories can collide with the entity and contactTestBitMask says which contacts lead to a notification. Note the separation of collision (actual interaction with the physics world) and contact (sole collision testing without any reaction in the physics world)! The latter two fields allow to produce three different scenarios: 1. contacts between entities are ignored, 2. contacts are notified but nothing happens in the physics simulation, 3. contacts are notified and applied to the physics world.

We will setup the bird as follows:

_bird.physicsBody.categoryBitMask = birdCategory;
_bird.physicsBody.collisionBitMask = worldCategory | pipeCategory;
_bird.physicsBody.contactTestBitMask = worldCategory | pipeCategory;

This means that the bird can collide with entities of the worldCategory and pipeCategory and that we would get notified for both of them. (You may wonder why we separate the categories for world and pipes – we could use one category for both of them. I’ll clarify later in this tutorial.)

The definition for the ground entity is a little easier. We only need to assign the category but do not request any collision notification/reaction.

dummy.physicsBody.categoryBitMask = worldCategory;
pipe1.physicsBody.categoryBitMask = pipeCategory;
pipe1.physicsBody.contactTestBitMask = birdCategory;
pipe2.physicsBody.categoryBitMask = pipeCategory;
pipe2.physicsBody.contactTestBitMask = birdCategory;

For the player nothing has changed: the bird still collides with the world. But the internals are now configured in a more sensible way.

Now we need to implement the contact notification method as required by the delegate. In order to visualize the collision, we will flash the background a little. That’s why we stored the sky color at the beginning of the tutorial!

- (void)didBeginContact:(SKPhysicsContact *)contact {
    // Flash background if contact is detected
    [self removeActionForKey:@"flash"];
    [self runAction:[SKAction sequence:@[[SKAction repeatAction:[SKAction sequence:@[[SKAction runBlock:^{
        self.backgroundColor = [SKColor redColor];
    }], [SKAction waitForDuration:0.05], [SKAction runBlock:^{
        self.backgroundColor = _skyColor;
    }], [SKAction waitForDuration:0.05]]] count:4]]] withKey:@"flash"];
}

We’re using a symbolic identifier for the SKAction here for the first time. That way, we can remove any previously created SKAction with the same name.

When you start the game now, you’ll see the background flashing red in case of a collision.

Animation stop

In order to stop the world movement and disable player control of the bird in case of a collision, we can make use of the speed property of the SKNodes. Setting the speed to 0 means that all running SKActions are paused. In order to avoid setting the speed property on every individual moving entity in the scene, we create a dummy parent node called _moving which holds all moving entities: the pipes, the background, the ground.

Once again we use an ivar to make it accessible from all methods.

@interface MyScene () <SKPhysicsContactDelegate> {
    SKSpriteNode* _bird;
    SKColor* _skyColor;
    SKTexture* _pipeTexture1;
    SKTexture* _pipeTexture2;
    SKAction* _movePipesAndRemove;
    SKNode* _moving;
}
@end

The entity is created at the beginning and added to the scene. All moving entities are added to this parent node.

        _skyColor = [SKColor colorWithRed:113.0/255.0 green:197.0/255.0 blue:207.0/255.0 alpha:1.0];
        [self setBackgroundColor:_skyColor];
        
        _moving = [SKNode node];
        [self addChild:_moving];
        
        SKTexture* birdTexture1 = [SKTexture textureWithImageNamed:@"Bird1"];
        birdTexture1.filteringMode = SKTextureFilteringNearest;

        ...
        
        // Create ground
        
        ...

        for( int i = 0; i < 2 + self.frame.size.width / ( groundTexture.size.width * 2 ); ++i ) {
            ...
            [_moving addChild:sprite];
        }
        
        // Create skyline
        
        ...

        for( int i = 0; i < 2 + self.frame.size.width / ( skylineTexture.size.width * 2 ); ++i ) {
            ...
            [_moving addChild:sprite];
        }

This affects also the pipe creation:

-(void)spawnPipes {
    ...    
    [pipePair runAction:_movePipesAndRemove];
    
    [_moving addChild:pipePair];
}

Now that we have all moving parts except the bird under one parent node. In order to stop animations, we can set the speed of _moving to 0 in case of contact. The parent’s speed is applied to all child nodes. We also want to happen this exactly once. We’re not interested in any further contacts once the animation has halted, so we wrap the whole method in a corresponding if.

- (void)didBeginContact:(SKPhysicsContact *)contact {
    if( _moving.speed > 0 ) {
        _moving.speed = 0;
    
        // Flash background if contact is detected
        ...
    }

You might have noticed that we did not add the bird to the _moving node. This is because we still want it to move and animate. We just need to retain player control from the bird by allowing touches only with _moving.speed > 0.

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    /* Called when a touch begins */
    if( _moving.speed > 0 ) {
        _bird.physicsBody.velocity = CGVectorMake(0, 0);
        [_bird.physicsBody applyImpulse:CGVectorMake(0, 4)];
    }
}

Animations are haltet on collision

Restart

Before we can restart the game, we need some way to reset the entire scene: the bird has to be moved to its initial position, its physics properties have to be reset, existing pipes have to be removed and the animation has to be restarted. Most importantly, this shouldn’t start immediately after a collision but after a little delay and an explicit user click/touch.

Let’s keep track when the user is allowed to restart the game in a new ivar _canRestart.

@interface MyScene () <SKPhysicsContactDelegate> {
    SKSpriteNode* _bird;
    SKColor* _skyColor;
    SKTexture* _pipeTexture1;
    SKTexture* _pipeTexture2;
    SKAction* _movePipesAndRemove;
    SKNode* _moving;
    BOOL _canRestart;
}
@end

We will initialize this flag to NO because it’s not relevant during a running game.

-(id)initWithSize:(CGSize)size {
    if (self = [super initWithSize:size]) {
        /* Setup your scene here */
        
        _canRestart = NO;
        
        self.physicsWorld.gravity = CGVectorMake( 0.0, -5.0 );

But we’ll change the flag to YES after a collision and after the background-flash-animation has finished (that should be enough game-over-drama for the sake of this tutorial). For that purpose, we can use another kind of SKAction, one that supports block execution. We simply append it to the flash-animation.

- (void)didBeginContact:(SKPhysicsContact *)contact {
    _moving.speed = 0;
    
    // Flash background if contact is detected
    [self removeActionForKey:@"flash"];
    [self runAction:[SKAction sequence:@[[SKAction repeatAction:[SKAction sequence:@[[SKAction runBlock:^{
        self.backgroundColor = [SKColor redColor];
    }], [SKAction waitForDuration:0.05], [SKAction runBlock:^{
        self.backgroundColor = _skyColor;
    }], [SKAction waitForDuration:0.05]]] count:4], [SKAction runBlock:^{
        _canRestart = YES;
    }]]] withKey:@"flash"];
}

Now, if the game finished due to a collision and the user is allowed to restart, we can simply make use of the flag in the touchesBegan method and reset the scene after click/touch.

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    /* Called when a touch begins */
    if( _moving.speed > 0 ) {
        _bird.physicsBody.velocity = CGVectorMake(0, 0);
        [_bird.physicsBody applyImpulse:CGVectorMake(0, 4)];
    } else if( _canRestart ) {
        [self resetScene];
    }
}

But wait, where’s that resetScene method coming from? Right, we still need to implement it.

How would we do that? We can easily reset the bird, restart the animation and reset the _canRestart flag, but how can we reset the pipes? Right now the existing pipes are direct children of the _moving container and we have no way to easily distinguish them from other nodes like the ground or skyline.

In order to easily access the pipes and remove them from the scene, we will restructure the scene hierarchy one more time: We will introduce another artificial parent node for all pipes, add this node to the existing _moving node and add all pipes to this new parent node.

The change is pretty straightforward. Once again a new ivar…

@interface MyScene () <SKPhysicsContactDelegate> {
    SKSpriteNode* _bird;
    SKColor* _skyColor;
    SKTexture* _pipeTexture1;
    SKTexture* _pipeTexture2;
    SKAction* _movePipesAndRemove;
    SKNode* _moving;
    SKNode* _pipes;
    BOOL _canRestart;
}
@end

…and a new SKNode as the parent for the pipes…

_moving = [SKNode node];
[self addChild:_moving];

_pipes = [SKNode node];
[_moving addChild:_pipes];
        
SKTexture* birdTexture1 = [SKTexture textureWithImageNamed:@"Bird1"];
birdTexture1.filteringMode = SKTextureFilteringNearest;

…and all created pipes added to this node instead of the _moving node.

    [pipePair runAction:_movePipesAndRemove];
    
    [_pipes addChild:pipePair];
}

Now we have everything we need to implement the resetScene method:

-(void)resetScene {
    // Move bird to original position and reset velocity
    _bird.position = CGPointMake(self.frame.size.width / 4, CGRectGetMidY(self.frame));
    _bird.physicsBody.velocity = CGVectorMake( 0, 0 );
    
    // Remove all existing pipes
    [_pipes removeAllChildren];
    
    // Reset _canRestart
    _canRestart = NO;
    
    // Restart animation
    _moving.speed = 1;
}

A little more drama

Right now, a crashed bird would simply fall as far as the physics world permits. If it crashes right in a pipe gap, it would possibly land directly on the lower pipe. I thought a little more drama was due! I wanted to ensure that the bird falls down to the ground and I wanted it to look a little more spectacular. So I changed the bird’s collision bitmask after collision so that it only would collide with the ground, and I added a little rotation animation:

- (void)didBeginContact:(SKPhysicsContact *)contact {
    _moving.speed = 0;
    
    _bird.physicsBody.collisionBitMask = worldCategory;
    
    [_bird runAction:[SKAction rotateByAngle:M_PI * _bird.position.y * 0.01 duration:_bird.position.y * 0.003] completion:^{
        _bird.speed = 0;
    }];

The angle and duration calculations are attempts to adjust the amount of rotation to the bird’s altitude. If the bird collides at a very low position, it will hit the ground sooner than if it collides at a high position, so the angle and durations are smaller if the bird flew lower. Right after the animation we change the bird’s speed to 0, too, to halt the wing flap animation. To avoid overriding the animation, we need to restrict the velocity-dependent rotation to when the game is still running:

-(void)update:(CFTimeInterval)currentTime {
    /* Called before each frame is rendered */
    if( _moving.speed > 0 ) {
        _bird.zRotation = clamp( -1, 0.5, _bird.physicsBody.velocity.dy * ( _bird.physicsBody.velocity.dy < 0 ? 0.003 : 0.001 ) );
    }
}

Now we need to reset the collisionBitMask, speed and zRotation properties in the resetScene method as well:

-(void)resetScene {
    // Reset bird properties
    _bird.position = CGPointMake(self.frame.size.width / 4, CGRectGetMidY(self.frame));
    _bird.physicsBody.velocity = CGVectorMake( 0, 0 );
    _bird.physicsBody.collisionBitMask = worldCategory | pipeCategory;
    _bird.speed = 1.0;
    _bird.zRotation = 0.0;

An idea to make the crash even more spectacular would be to add a little feather particle system. But maybe that’s something for another tutorial.

Score counting

The first thing to do for score counting is to actually display the score. SpriteKit provides SKLabelNodes, which are meant to display text in the scene. iOS 7 comes with plenty of fonts to choose from, I decided to go for “MarkerFelt-Wide”. Eventually we should switch to a more fitting bitmap font, but this should do for now.

To keep track of the score and the label, we’ll first add two new ivars.

@interface MyScene () <SKPhysicsContactDelegate> {
    SKSpriteNode* _bird;
    SKColor* _skyColor;
    SKTexture* _pipeTexture1;
    SKTexture* _pipeTexture2;
    SKAction* _movePipesAndRemove;
    SKNode* _moving;
    SKNode* _pipes;
    BOOL _canRestart;
    SKLabelNode* _scoreLabelNode;
    NSInteger _score;
}
@end

Now we’ll initialize the ivars and add the label to the scene. Add this at the end of the /* Setup your scene here */ block.

// Initialize label and create a label which holds the score
_score = 0;
_scoreLabelNode = [SKLabelNode labelNodeWithFontNamed:@"MarkerFelt-Wide"];
_scoreLabelNode.position = CGPointMake( CGRectGetMidX( self.frame ), 3 * self.frame.size.height / 4 );
_scoreLabelNode.zPosition = 100;
_scoreLabelNode.text = [NSString stringWithFormat:@"%d", _score];
[self addChild:_scoreLabelNode];

The score should increment whenever the bird has passed a pair of pipes. The easy way, as suggested in the comments, would be to simply increment the score when the pipes have left the screen and are removed from the scene graph. However, this would also mean a little delay until the score is counted. Instead, we will create an SKNode right “after” (meaning: to the right of) the pipes with a physics body with contact detection enabled.

Add this to -(void)spawnPipes:

[pipePair addChild:pipe2];
    
SKNode* contactNode = [SKNode node];
contactNode.position = CGPointMake( pipe1.size.width + _bird.size.width / 2, CGRectGetMidY( self.frame ) );
contactNode.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake( pipe2.size.width, self.frame.size.height )];
contactNode.physicsBody.dynamic = NO;
[pipePair addChild:contactNode];

[pipePair runAction:_movePipesAndRemove];

If you run the game now, you’ll see that the bird crashes shortly after passing a pair of pipes. This happens because the contact of the bird’s physics body with the contactNode’s physics body is detected and - (void)didBeginContact:(SKPhysicsContact*) is called, but we currently do not distinguish whether the bird is in contact with the world or something else. We can make use of the collision categories again and add a dedicated one for the score area.

@implementation MyScene

static const uint32_t birdCategory = 1 << 0;
static const uint32_t worldCategory = 1 << 1;
static const uint32_t pipeCategory = 1 << 2;
static const uint32_t scoreCategory = 1 << 3;

static NSInteger const kVerticalPipeGap = 100;

We assign this category to the score node that we created right before.

SKNode* contactNode = [SKNode node];
contactNode.position = CGPointMake( pipe1.size.width + _bird.size.width / 2, CGRectGetMidY( self.frame ) );
contactNode.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(pipe2.size.width, self.frame.size.height)];
contactNode.physicsBody.dynamic = NO;
contactNode.physicsBody.categoryBitMask = scoreCategory;
contactNode.physicsBody.contactTestBitMask = birdCategory;
[pipePair addChild:contactNode];

Now we can handle the different types of contacts properly by investigating the SKPhysicsContact object passed to the didBeginContact method. It has two properties representing the two physics bodies in contact, bodyA and bodyB. If either of these two physics bodies is of category scoreCategory, we can increase the score. In all other cases, the bird collides with the world.

- (void)didBeginContact:(SKPhysicsContact *)contact {
    if( _moving.speed > 0 ) {
        if( ( contact.bodyA.categoryBitMask & scoreCategory ) == scoreCategory || ( contact.bodyB.categoryBitMask & scoreCategory ) == scoreCategory ) {
            // Bird has contact with score entity
            
            _score++;
            _scoreLabelNode.text = [NSString stringWithFormat:@"%d", _score];
        } else {
            // Bird has collided with world

            ...
        }
    }
}

The only thing left is to reset the score to 0 in the reset method.

-(void)resetScene {
    ...
    
    // Reset score
    _score = 0;
    _scoreLabelNode.text = [NSString stringWithFormat:@"%d", _score];
}

The result should look like that:

Score Counting

The score update is a little boring. You can easily add some visual feedback by using some SKActions to scale the score for a short moment.

- (void)didBeginContact:(SKPhysicsContact *)contact {
    if( _moving.speed > 0 ) {
        if( ( contact.bodyA.categoryBitMask & scoreCategory ) == scoreCategory || ( contact.bodyB.categoryBitMask & scoreCategory ) == scoreCategory ) {
            // Bird has contact with score entity
            
            _score++;
            _scoreLabelNode.text = [NSString stringWithFormat:@"%d", _score];

            // Add a little visual feedback for the score increment
            [_scoreLabelNode runAction:[SKAction sequence:@[[SKAction scaleTo:1.5 duration:0.1], [SKAction scaleTo:1.0 duration:0.1]]]];
        } else {
            ...

The action scales the score text up to 150% of its size of 1/10th of a second and then back to the original 100% in another 1/10th second.

Many roads lead to Rome

I need to emphasize that there are dozens of other possible ways to implement the game mechanics.

For example, the world movement and score counting could easily be done in the update method. The movement could be implemented depending on the passed time since last frame and the counting could be implemented depending on the pipe’s horizontal position. Even collision detection is so trivial that you don’t need the physics engine for that. On the other hand, look how easy it was to implement the bird’s behavior!

You would also normally not load all sprites into separate files but use texture atlasses instead (i.e. as many sprites as possible on one texture) because texture switches are an expensive operation on the graphics card.

From a software engineering perspective there is also plenty of improvement potential, like moving the score update functionality into a dedicated method, or even a subclass of SKLabelNode with dedicated increment/reset methods.

Conclusion

During the course of this tutorial, you’ve learned how to…

  • create a SpriteKit project with Xcode
  • load textures into SKTexture
  • create sprites with SKSpriteNode
  • create text with SKLabelNode
  • use different animation speeds to create the illusion of depth
  • use the SKSpriteNode‘s zPosition property to control rendering order
  • attach and configure physics bodies to nodes
  • configure the physics world
  • use SKAction to animate sprites, move the game world and control game states
  • control game flow using the SKNode.speed property
  • handle touches
  • detect and distinguish contacts between entities
  • structure the scene graph in a way that makes sense for the game world

If you think that something needs to be covered in more detail, feel free to leave a comment or contact me on Twitter (@digitalbreed). I will try to update the tutorial or respond to you directly.

If you liked this tutorial and you have some Bitcoins to spare, I would appreciate a tip at 1QByEN4aeEN1KKqikBFUGdgeg1ZCX6c1nr.

Download

I have pushed the tutorial code to GitHub.

Next Article

LeWeb 2010 startups revisited

95 Comments

Cancel

  1. Great Tutorial, i’ve learned allot about the ios sprite kit from this.
    Keep up the good work :)

  2. Is it difficult to add a main menu to this game?

  3. Great work and it’s very useful for me who is just starting to learn the sprite kit. But where i can download the source code? i can’t find out the link.

  4. I would like to download the image assets for this tutorial. It says at the top they can be downloaded with the rest of the project. Where can I find them?

    Thanks, it looks like a great tutorial.

  5. Great!
    I would like to download images. Please live link
    Thanks for your work

  6. I have pushed the tutorial code to GitHub, link is at the bottom of the tutorial.

  7. When are you putting the score counting part of the tutorial up?

  8. Absolutely love the tutorial! Though there were some various problems throughout the walk-through (e.g., differently named variables (I think you listed groundTexture as “bottomTexture” at one point), missing bits of code, etc.)., it was an excellent read, and I’m going to take a lot away from it.

    Thank you!

  9. @Ryyydizzle, not really. This tutorial is about the game mechanics but I may extend it with some menu code.

    @Mark, as soon as time permits. The basic concept will be: add an invisible rectangular physics object with it’s own category right of each pipe pair and increment the score every time a contact with this object is detected.

    @Syrinathos, thanks, fixed. I did some renaming before I wrote the tutorial and I missed that one. Any other issues you’ve encountered?

  10. I’d recommend adding ‘_bird.physicsBody.velocity = CGVectorMake(0, 0);’ to touchesBegan. Otherwise the impulse gets added to the current velocity of the bird node, leading to bigger jumps the more often you tap.

  11. I like your code, especially the _moving part and animation!

    One more question, if I tap more than once quickly, the bird seems run very high …
    Do you know how to adjust the gravity and impulse so that the bird running normally?

    thanks!

  12. @ET Worker, please see Sascha’s comment right above yours for a solution.

  13. Hey Matt, did you spend any thought about counting the pipes the bird had been reached? It isn’t going into my mind how to realize this…

    Thanks for this great Tutorial! :-)

  14. @Daniel Bocksteger, yes, I already sketched a solution above, see my response to Mark. I will possibly add it later tonight.

  15. @Matt: Not as far as I can recall, no. The only other one that stood out in my mind was the _movePipesAndRemove having been different between one block of code and the next, but you seem to have fixed that as well.

    In addition, another item that wasn’t covered was the lack of a distinct ceiling within the game world. As the project stands now, the player can simply fly over all the pipes and not have to worry about collisions of any kind. I made a crude work around by ensuring that they cannot apply the impulse if the Y position of the bird is within 100 points of the top of the screen, but if they tap quickly enough they can still fly up into the ceiling until gravity yanks them back down.

    I’m thinking another invisible node running along the top of the scene moving with the bird to count as another physics body would be in order.

    Regarding the score counting, wouldn’t it be easier to make an instance variable that gets incremented as each pipe is removed from the scene? Logically speaking, if the pipe has been removed, you’ve already passed it.

  16. how much longer until the score part is added?
    great tutorial BTW :)

  17. @Syrinathos, yes, I discussed the ceiling issue on Twitter, too, and I will add some information on how to avoid that. Score counting would be easier that way but it would cause a recognizable delay, too.

  18. @Syrinathos: Regarding the bird flying off the screen, simply restrict the bird to the self.frame or in other words restrict it to the frame of the phone.

    About the score counting, its very easy to implement, like @matt said, all you need to do add an SKNode to between the pipes and make a collision category for it. When the bird collides with the SKNode, just increment the score.

    @matt: Great tutorial! Love the way you have explained everything in detail with illustrations of the progress. Keep up the good work!

  19. Thanks for share,
    it is help me alot!

  20. Hey, i have added the line _bird.physicsBody.velocity = CGVectorMake(0, 0);
    in the method touchesBegan:

    -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
    /* Called when a touch begins */
    if( _moving.speed > 0 )
    {
    [_bird.physicsBody applyImpulse:CGVectorMake(0, 8)];
    _bird.physicsBody.velocity = CGVectorMake(0, 0);
    }
    else if( _canRestart )
    {
    [self resetScene];
    }
    }

    but it takes no effect.

    can someone help me?
    sry for my bad english, i am a german 😀

  21. @Nico, do it the other way around. First set the velocity to 0, then apply the impulse.

  22. thanks!! now it works
    but i have to change the impule from 8 to 4. now its perfect!

  23. i’ve created a Flappy Bird Clone a few weeks ago and our code is almost the same the only difference is the pipe generation, i generate the pipes with a pattern image of 2 pixels height instead of single images, nice tutorial

  24. Legend.

  25. This is unbelievable.

    Thank you

  26. Hey Matt,

    Thanks for this awesome tutorial. I always wanted to look at SpriteKit but never did. This is a cool example to dive in.

    I have one question though regarding the velocity. At some point you do this “* ( _bird.physicsBody.velocity.dy < 0 ? 0.003 : 0.001 )" to rotate the bird sprite in the correct direction. If I don't do this (which is what I first tried because that's how I'd have done it) the bird is constantly facing down and start moving forward on the x axis. Adding this part of code makes it correct but I don't understand why. Could you try to explain it to me ?

    Thanks for your help.

    Anthony

  27. @Anthony, I can only guess because my code works fine even without the rotation code. But your scenario sounds as if your scene is rotated by 90 degrees :) Also check that your bird’s physics body has allowsRotation = NO set and that you’re calling applyImpulse, not applyAngularImpulse.

  28. Thanks Matt you were right. I had allowsRotation set to YES instead of NO. Changing it solved the problem of the bird going forward when hitting the ground. I guess an angular velocity was automatically added by the physics engine when birdy was hitting the ground.

  29. Hi, This is a fantastic tutorial. Can you add a bit on about how to do highscores etc.

    Thanks in advance,

  30. hey,
    i got a probem with the score system every time the bird hits the groud you get points for that …
    pls help me

  31. Hey Matt,
    I was wondering how you did the art work? I know you used gimp but could you quickly go through the steps of what you did?
    Thank, much appreciated!

  32. I am having an issue with the skyline and ground as well. I need the dimension sizes and I cannot use Gimp. My current images are not loading properly for the sizes. Help?

  33. @TimoP, nice enhancement! Hope that solves @Rob’s question as well.

    @Tim, check that bird and ground have proper categories set up and that the didBeginContact method properly distinguishes both.

    @Andrew, I loaded some screenshots into Gimp, created images with transparent background and started placing pixels similarly to the original game sprites using the Color Picker Tool for color selection and the Pencil tool with brush size 1. The bird was the easiest because I found some good images without JPEG artefacts etc., but I couldn’t find a good sample of the city background so I created that one with an own pattern but with similar colors.

    @juice, skyline is 80×42 pixels and ground is 18×48 pixels, but remember that I scaled everything up ingame with factor 2. The code is written so that the image dimensions shouldn’t really matter, though. I guess I do not understand your problem yet.

  34. My images are not lining up properly on the simulator. My ground image is taking up so much space that there is only 20% of the background color visible. I am very new to this coding and I guess I don’t understand the sizing within the code. I resized my image to match yours and I am still getting the oversized images.

  35. when i build it all of the images on the test screen show as a red x!!
    error codes: 2014-02-23 14:50:11.248 Wacky B[13643:a0b] SKTexture: Error loading image resource: “Bird1”
    2014-02-23 14:50:11.250 Wacky B[13643:a0b] SKTexture: Error loading image resource: “Ground”
    2014-02-23 14:50:11.252 Wacky B[13643:a0b] SKTexture: Error loading image resource: “Skyline”
    2014-02-23 14:50:11.472 Wacky B[13643:a0b] SKTexture: Error loading image resource: “Bird2”

  36. Where do i place these actions?

    The _moveAndRemovePipes action is created right after the pipe texture loading like so:

    1
    2
    3
    4
    CGFloat distanceToMove = self.frame.size.width + 2 * _pipeTexture1.size.width;
    SKAction* movePipes = [SKAction moveByX:-distanceToMove y:0 duration:0.01 * distanceToMove];
    SKAction* removePipes = [SKAction removeFromParent];
    _movePipesAndRemove = [SKAction sequence:@[movePipes, removePipes]];
    This will move the pipes by a full screen width plus two times their texture width to make sure that the pipes are out of sight. Then, they are removed from the scene.

    The spawn method is responsible for creating new pipes:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    -(void)spawnPipes {
    SKNode* pipePair = [SKNode node];
    pipePair.position = CGPointMake( self.frame.size.width + _pipeTexture1.size.width, 0 );
    pipePair.zPosition = -10;

    CGFloat y = arc4random() % (NSInteger)( self.frame.size.height / 3 );

    SKSpriteNode* pipe1 = [SKSpriteNode spriteNodeWithTexture:_pipeTexture1];
    [pipe1 setScale:2];
    pipe1.position = CGPointMake( 0, y );
    pipe1.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:pipe1.size];
    pipe1.physicsBody.dynamic = NO;
    [pipePair addChild:pipe1];

    SKSpriteNode* pipe2 = [SKSpriteNode spriteNodeWithTexture:_pipeTexture2];
    [pipe2 setScale:2];
    pipe2.position = CGPointMake( 0, y + pipe1.size.height + kVerticalPipeGap );
    pipe2.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:pipe2.size];
    pipe2.physicsBody.dynamic = NO;
    [pipePair addChild:pipe2];

    [pipePair runAction:_movePipesAndRemove];

    [self addChild:pipePair];
    }
    Finally, we need to ensure that the spawnPipes method is called regularly to produce new pipes and attach the animation to it. This happens with an SKAction which is directly attached to the SKScene.

    1
    2
    3
    4
    5
    SKAction* spawn = [SKAction performSelector:@selector(spawnPipes) onTarget:self];
    SKAction* delay = [SKAction waitForDuration:2.0];
    SKAction* spawnThenDelay = [SKAction sequence:@[spawn, delay]];
    SKAction* spawnThenDelayForever = [SKAction repeatActionForever:spawnThenDelay];
    [self runAction:spawnThenDelayForever];
    This action calls the spawnPipes method, then pauses for two seconds, then repeats.

    Now we’ve got beautiful repeating pipes.

  37. @chse, best is to download the project (see GitHub link at the end) and compare the final Scene.m code with your progress.

  38. is it main.m, myscene.h/m ?

  39. @chse, it’s MyScene.m

  40. how can i add a menu? and a score list ?

  41. @chse, I may add another chapter on that, if time permits.

    @bosss, look into ViewController.m, there are two lines setting showsFPS and showsNodeCount to YES. Change that to NO.
    Here are some good answers regarding how to deal with the MIT license: http://programmers.stackexchange.com/a/178493 http://stackoverflow.com/a/3194704/74890

  42. i get these error when i run it on my phone 2014-02-24 17:42:33.852 test[1323:60b] SKTexture: Error loading image resource: “Bird1”
    2014-02-24 17:42:33.864 test[1323:60b] SKTexture: Error loading image resource: “ground”

  43. 2/24/2014 at 23:43 ·
    i get these error when i run it on my phone 2014-02-24 17:42:33.852 test[1323:60b] SKTexture: Error loading image resource: “Bird1″
    2014-02-24 17:42:33.864 test[1323:60b] SKTexture: Error loading image resource: “ground”

  44. Could you add in a menu/ start page and a page after the bird had died in the game to reset the game? Thanks

  45. Great tut!

    As a newbie, i spent a whole afternoon plus a morning to follow through this great tutorial.
    Summary of problems i have encountered:
    1. That collision bit mask and bitwise shift stuff are just too much for me. I could’t understand it.
    2. rotation animation looks hard to understand so i just passed it.
    3. That flash screen actions are just… so long

    There is a minor problem at the part when we add the moving objects to a parent node so we can pause the game.
    you said :
    “You might have noticed that we did not add the bird to the _moving node. ”

    but actually you did add it (in line 12):
    [_moving addChild:_bird];

    :)

    so wrap up,
    thank you for the great tut!

  46. i got one more question
    (contact.bodyA.categoryBitMask & scoreCategory ) == scoreCategory

    this line of code. i was confused when i saw that & symbol. so i looked it up then realized it returns the address of the following object.
    and things got even more confusing

    i tired to change the code to
    (contact.bodyA.categoryBitMask) == scoreCategory
    as this looks more understandable to me. and i found out it worked as well. nothing changed in the game.

    In the sprite kit sample project “adventure”, i found this line of code was represented in another way
    *if (contact.bodyA.categoryBitMask & APAColliderTypeProjectile ||contact.bodyB.categoryBitMask & APAColliderTypeProjectile)

    it just used & instead of == or other alternate ways.

    so i assume that, functionally speaking, & and == are same here ??

    am i right? maybe i still don’t fully understand the & symbol
    please correct me if so.

    thank you!

  47. :(
    got another problem.

    pass through invis score block , will cause score add up by 4 instead of 1.

    so score goes like 0,4,8,12““

    this is so weird. i couldn’t find out where the problem is.

    there is nothing wrong with score++; (i did not write score+=4“`)

    can you help me :)

    thank you very much

  48. @ DylanYasen, good catch with _moving, I’ll change it. (It’s correct in the uploaded source code, though.) Some comments regarding your problems and confusion with collision detection:

    1. Once you got it, bitmasks are really easy. We create them by shifting a bit x times “to the left” with the “shift left” operator “< <", like 1<<0 = 1b, 1<<1 = 10b, 1<<2 = 100b, 1<<3 = 1000b, etc. (b for binary). Now we test if the bit is set with the binary "and" operator (a & b) --- it is NOT the "address of" operator in that case. "Binary and" compares all bits and leaves only those which are set in both operands, so 100b & 111b would become 100b. So if we're testing if the object's collision bitmask is e.g. scoreCategory, we "binary and" it and check if the result is equal to scoreCategory. Alternatively you can simply check if the result is != 0 and do a shortcut by making use of the fact that in C, for an "if" condition, any numeric value != 0 evaluates to "true", which is basically what you saw in another tutorial (i.e. if (1) -> true, block will be executed, if (0) -> false, block won’t be executed).

    2. Rotation angle calculation takes the current vertical velocity (i.e. the amount of movement up or down), multiplies it with a certain factor to scale it (factors were figured out by experimenting) and clamps it… One needs to note that rotation is in Radians, i.e. 0..2*PI for a full circle. Does this make it clearer?

    3. The animation is built out of several individual SKActions. You can try to split them up to get a better understanding.

    Regarding the score counting: it seems that the contact is detected four times instead of one time, which is weird, since didBeginContact should be called exactly once per contact. Reason could be multiple overlapping “score count objects”. You could log the collision entities for a single “score contact” and I guess you would see that it’s always a different object. Check your “score object” creation.

  49. @Bennett Taylor, @chse, I may add it if time permits. You can watch the GitHub repo to get notified if I make updates.

  50. @chse, @McGee, seems like you haven’t added the required graphic files to the project. Also check the casing (ground vs Ground).

  51. firstly thanks for your reply.

    After log through many kinds of possibilities, i found nothing.

    i got lots of warnings tho. and there was one just about _score.
    “Format specifies type “int” but the argument has type ‘NSInteger *’ (aka ‘int *’) ”

    Then i checked my header file. I found that:
    NSinteger *_score .
    i just removed that * and warnings’ gone.
    score is working normal.

    can you explain that for me ?
    what’s the difference between ‘*’ and ‘no *’. i thought i had to use * in any situation.
    really curious about why I was getting 4.
    and this * thing!
    lol
    i asked too many questions

    thank you

  52. Okay, that explains it! “NSInteger*” is a “pointer to an NSInteger”. A pointer is basically a location in memory and you need to use it only for class instances, not for primitive values. Messing around with pointers leads to crashes most of the time, but since you’re initializing it with 0 and then only increment it, it doesn’t hurt. Since you’re working on a 64 bit system, a pointer increment with ++ points to the next position in memory, which means moving the pointer 4 bytes. This explains your row of 0, 4, 8,…! Go get a C / Objective C tutorial and work through that, too 😉

  53. Hi Matt, Solved the highscore problem! Please do a title screen/page this w/e?

  54. Ok.
    I learnt a lot from this tut as my first project on ios.

    what i have learnt:
    1. fancy logical operators that i have never used before
    “? :”
    when i first saw this, that just looked intimidating. But i calm myself down later, and look it up. hey that was just a shortcut for “if” !

    2. i think i know how to use sprit kit major functionality now
    1) set texture for sprites (even set the filter incase of scaling issue)
    2) control depth of objects by setting zposition
    3) basic physics system
    4) repeating pattern and movement for simple tiles (that movement trick just feels like screwing over game players )
    ……
    most important:
    x) adequate confidence to keep going

    appreciate it!

  55. I’ve got 2 Errors :

    error: could not read data from ‘/Users/Luki/Desktop/how-to-build-a-game-like-flappy-bird-with-xcode-and-sprite-kit-master/FlappyBirdClone/FlappyBirdClone-Info.plist’: The file “FlappyBirdClone-Info.plist” couldn’t be opened because there is no such file.

    Command /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang failed with exit code 1

    pls help me :(

  56. I need some help… When i start the game is there anyway i could make it so you have to hit start instead of it starting automatically? Thanks if you can help!!

  57. Luki redownload the zip file sounds like you deleted an important file

  58. Nice tutorial, you may add as a scene change or level once you reach the end of a level no ..

  59. Hello can i make the game faster ? and how about a start menu and a end menu with high score thanks

  60. A start menu and an end menu would be great!! Thanks!!

  61. anybody know how to add a start and game over menu if so please comment i have no idea how to thanks!!

  62. I have 6 Error ,Error is Local declaration of ‘ _pipeTexture1′ hides instance variable’ in // Create pipes . every time i add the SKTexture* _pipeTexture1; and SKTexture* _pipeTexture2; its not work . i don’t Know why have Error. Please Help me.Thank you

  63. Hey I was wondering if say a person was to put this game on the app store but with their own graphics added would that be ok or be copyright infringement

  64. Mail from you:

    “Hi Simon,
    if you’re asking whether you can use the tutorial code to release your own app, no worries, you can do whatever you want with it. The art should be replaced, though.

    And just mind you that the tutorial is far from a complete game, there are lots of bits and pieces missing.

    Best regards
    Matt”

    What do you meant with “And just mind you that the tutorial is far from a complete game, there are lots of bits and pieces missing.”?

    Can it not be released on app store yet?
    Is it not compatibility with iOS 7?

    And can you please help me to add start menu and a end menu with high score.

    Sorry for bad english (norwegian)
    Thank you in advance!

  65. How to take off the numbers of the corner right down ( fraps i think) and make bird faster

  66. Hi Matt,
    This is really helpful and great tutorial. I am kinda new to iOS game programming and have done little with the cocos2d but now loving the SpriteKit more than cocos2d.
    Well, I have a quick question that I could not get in this tutorial. For creating the values like

    static const uint32_t birdCategory = 1 << 0;
    static const uint32_t worldCategory = 1 << 1;
    static const uint32_t pipeCategory = 1 << 2;

    why do we need to do 1 << 0, 1 << 1 and 1 << 2 ? and why do we need these variables?

    Can not we just do like

    static const uint32_t birdCategory = 1;
    static const uint32_t worldCategory = 2;
    static const uint32_t pipeCategory = 4;

    May be I was missing something but could not get this part.

    Thanks
    Jeewan

  67. hello does anyone know how to play a sound when the bird goes threw the pipe and when you click on the screen?

  68. I sometimes experience that the bird stop flapping. Only static “flapping” with no animation. Anybody experience the same thing?

  69. @chse:
    1. Add #import in the MyScene.h

    2. make sure you have sound like…hurt…score and flap flap…. or email me: geir-ove1970@getmail.no (for sound)

    3. Insert (replace old part):
    -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    /* Called when a touch begins */
    if( _moving.speed > 0 && _bird.position.y max ) {
    return max;
    } else if( value 0 ) {
    _bird.zRotation = clamp( -1, 0.5, _bird.physicsBody.velocity.dy * ( _bird.physicsBody.velocity.dy 0 ) {
    if( ( contact.bodyA.categoryBitMask & scoreCategory ) == scoreCategory || ( contact.bodyB.categoryBitMask & scoreCategory ) == scoreCategory ) {
    // Bird has contact with score entity

    _score+=10;

    // Sound effect – Score
    [self runAction:[SKAction playSoundFileNamed:@”score.wav” waitForCompletion:NO]];

    _scoreLabelNode.text = [NSString stringWithFormat:@”%ld”, (long)_score];

    // Add a little visual feedback for the score increment
    [_scoreLabelNode runAction:[SKAction sequence:@[[SKAction scaleTo:1.5 duration:0.1], [SKAction scaleTo:1.0 duration:0.1]]]];
    } else {
    // Bird has collided with world

    _moving.speed = 0;

    _bird.physicsBody.collisionBitMask = worldCategory;

    [_bird runAction:[SKAction rotateByAngle:M_PI * _bird.position.y * 0.01 duration:_bird.position.y * 0.003] completion:^{
    _bird.speed = 0;
    }];

    // Sound effect – Collision
    [self runAction:[SKAction playSoundFileNamed:@”hurt.wav” waitForCompletion:NO]];
    // Vibrate
    AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);

    // Flash background if contact is detected
    [self removeActionForKey:@”flash”];
    [self runAction:[SKAction sequence:@[[SKAction repeatAction:[SKAction sequence:@[[SKAction runBlock:^{
    self.backgroundColor = [SKColor whiteColor];
    }], [SKAction waitForDuration:0.02], [SKAction runBlock:^{
    self.backgroundColor = _skyColor;
    }], [SKAction waitForDuration:0.02]]] count:6], [SKAction runBlock:^{
    _canRestart = YES;
    }]]] withKey:@”flash”];
    }
    }
    }

    @end

  70. Sorry, step 1: #import

  71. Check Out Flappys Flight on App Store

  72. H I G H S C O R E

    1) add to the @interface the next lines as shown below:
    @interface
    ….
    NSInteger _heighscore;
    SKLabelNode* _heighscoreLabelNode;

    2) then add in the section “-(id)initWithSize:(CGSize)size” the following lines as shown below:
    -(id)initWithSize:(CGSize)size {
    if (self = [super initWithSize:size]) {
    …..
    _heighscore = [[NSUserDefaults standardUserDefaults] integerForKey:@”HighScoreSave”];
    _heighscoreLabelNode.text = [NSString stringWithFormat:@”BEST %li”, (long)_heighscore];

    also add:
    _heighscoreLabelNode = [SKLabelNode labelNodeWithFontNamed:@”MarkerFelt-Wide”]; //Type of title
    _heighscoreLabelNode.position = CGPointMake(self.size.width / 1.3, self.size.height/9.67); //location
    _heighscoreLabelNode.zPosition = 100; //font location
    [_heighscoreLabelNode setScale:0.8]; //size
    _heighscoreLabelNode.text = [NSString stringWithFormat:@”BEST %li”, (long)_heighscore]; //string
    [self addChild:_heighscoreLabelNode]; //add highscore

    3) add the next lines to the “-(void)resetScene” as shown below:
    -(void)resetScene {
    …..
    _heighscore = [[NSUserDefaults standardUserDefaults] integerForKey:@”HighScoreSave”];
    _heighscoreLabelNode.text = [NSString stringWithFormat:@”BEST %li”, (long)_heighscore];

    4) add the next lines to the “-(void)didBeginContact” as shown below:
    – (void)didBeginContact:(SKPhysicsContact *)contact {
    if (_score > _heighscore) {
    [[NSUserDefaults standardUserDefaults] setInteger:_score forKey:@”HighScoreSave”];
    }

    _______D O N E__________ NOW YOU HAVE HIGHSCORE_____________

  73. Can someone help me please in creation of “game interface sheet” which includes:
    – pause/resume button
    – sound on/off button
    – reset score button

    THANK YOU!

  74. Pleease help someone in creation of:
    -sounds on/off button
    -pause/resume the game
    -reset score

    loydspace@gmail.com

  75. Awesome tutorial…the only thing I notice is that you’re not using the typical best practices in creating different classes for the different aspects of the app, other than that, well done.

  76. Hi Matt!
    I am learning objective C from scratch and so far learnt how to write simple programs for Mac OSX. I just wanted to get some advice on what resources to use for learning iOS game development (like what books, guides etc) after I’ve learnt the language. Loved this tutorial by the way!
    Thanks!

  77. Hi Matt,
    Great tutorial! I’m still learning objective C, too.
    Please have a hint what I have to do to see the pipes in front of the ground. Is this possible?
    I am very pleased to get an answer,
    Stefan

    • @Stefan, the ordering of sprites is determined by two things: 1. the scene hierarchy (implicitly through the order of adding nodes to the same parent or explicitly through parent-/child-node-relations) and 2. the zPosition property of a SKNode.

      In the tutorial, I explicitly set the zPosition to -10 to ensure that the pipes appear behind the ground (due to 1., the implicit version). You can change this value to e.g. +10 and the pipes should appear in front of the ground.

  78. Hi there,

    for some reason when my game resets the bird no longer flaps. Have you any advice?

    Thanks

    • @Pure, the SKAction animation runs infinitely, so the only idea I have is that the speed attribute of the _bird is not properly reset to 1.

  79. Thanks Matt,

    I know you are a busy man. If you could kindly add a tut for adding a stargame/restart ame buttons for this it would be great. Alternatively just some code. I have tried so many different options and I am stuck.

    Thanks in Advance!.

    Btw all I did for the bird not flapping scenario was move the _bird.speed = xx in the //Bird Has collided with the world to above the [_bird runAction……. instead of below. like so:

    _bird.speed = xx
    [_bird runAction:[SKAction rotateByAngle:M_PI * _bird.position.y * 0.01 duration:_bird.position.y * 0.003] completion:^{

  80. For bird flapping issue / speed issue, add

    _bird.speed =1 into resetScene method

  81. For bird flapping issue / speed issue, add

    -(void)resetScene {

    _bird.speed =1 into resetScene method

    }

    Now it’s time to figure out how to make a start game / quit / restart game options!

  82. Just a suggestion maybe use a custom class for the bird and pipes…

  83. EXCELLENT tutorial!

    My first attempt at an app and to much success, thank you.

    I just have one error that remains unsolved: the addition of the ‘if…else’ conditional statement for the score increment works great, but seems to have over-ridden ‘death-on-pipe-collision’. The score also increases with collisions with the pipes, meaning you can fly on despite hitting them. While this may be great for those who like it easy, I was kinda going for the ‘red-flashing-death-on-pipe-collision’ effect…

    I have tried a few things, but alas, I am a total noob.

    Any recommendations greatly appreciated.

    Thanks again,
    M.

  84. @Mary, hard to say what’s wrong from the distance, I would suggest you take a look at the source code on GitHub and compare it to yours.

  85. Hey Matt!
    Thank you so much for the tutorial. I was wondering if maybe you or someone else could explain more in depth the logic behind the scrolling ground/sky code? Especially the for loop. I am just struggling to understand how you are going about doing it.

    Thank you!
    -Derek

  86. Solved! 😀

    Had a couple of crazy code placements, all fixed now.
    Guess I just needed someone to tell me to look twice 😉

    Thanks again,
    Mary.

  87. Hi there,

    Can anyone explain to me how the game restarts, in terms of the code used. I mean that you add a BOOL and then change the value to a YES when you collide, then if you click again then you run the [self resetScene]. But in resetScene function you remove all the pipes not but how do you create all the pipes again?

    Could someone specially point the code that is creating all the pipes again after you collide?

    I am new to sprite kit so any help would be great.

    Thanks,
    Tushar

  88. I did the tutorial, and all works fine. Now I have the latest SpriteBuilder (1.3.1) and it crashes on build after publishing. Then I updated Cocos with SpriteBuilder and it won’t build. Any ideas as to why?

  89. Hi! Good night thanks for all the help you post in your page!its just awesome!! Im making a game with your flappy basis, but i have 2 questions that i cant implement,how can i spawn pipes with the fisrt delay? I mean that i dont have to wait a little bit for the forst pipes to appear? And how can i set the bounds for the bird? Because when i touch the screen for the bird to flap too much it goes of the screen. THANKS A LOT BY THE WAY cheers from South America!! :) :)

  90. Hi Matt! AWESOME tutorial.

    I was wondering, and yes I checked EVERYTHING with the source code, but is the only file we modify the MyScene.m? Because when I checked the other files, they don’t seem to show anything when I build and run in the simulator. I am running Xcode 6,

  91. Classic comments! Good tutorial!

    Commenters: why ask for multiplayer and other features like menus? There are infinite possibilities. How about a click adjuster menu? Part of the fun is developing new features like that.

    So you want a menu? Ok, set everything to invisible and show some menu sprites. Give them touch events to run new functions like StartGame()

    This is a good starting point for any game, so maybe start from scratch?

  92. where does this code go
    CGFloat distanceToMove = self.frame.size.width + 2 * _pipeTexture1.size.width;
    SKAction* movePipes = [SKAction moveByX:-distanceToMove y:0 duration:0.01 * distanceToMove];
    SKAction* removePipes = [SKAction removeFromParent];
    _movePipesAndRemove = [SKAction sequence:@[movePipes, removePipes]];