Build Your Own Flappy Bird Clone Using iOS7’s SpriteKit

FlappyBird
I’ve always been a big fan of simple tap tap game mechanics, so with all of the hype surrounding the rise and fall of Flappy Bird I thought I’d jump on the band wagon and show you how to build your very own Flappy Bird clone: Bouncy Brick™. It’s an addictive game mechanic, it’s also relatively easy to replicate. To keep it even simpler I haven’t created any art work, all assets are built programmatically from square sprites. Probably the hardest part of building your own Flappy Bird clone will be coming up with the name.

There are two main parts to the Flappy Bird game mechanic, the bird interaction and the obstacle hit test. The bird is a sprite with physics turned on so it has a constant gravitational pull on it. When we tap the screen it applies a linear impulse to the bird in the opposite direction. The obstacles are added to the scene and scrolled along horizontally on update. Then we run a simple hit test to check for collisions between the bird and obstacles. If a collision is registered we restart the game.

The purpose of this tutorial is to strip back the game as much as possible so as to expose the simplicity at the core of the Flappy Bird mechanic. There are a lot of things you could do to finesse, but essentially this is the main mechanic of Flappy Bird in less than 150 lines of code. Download the finished version of the project from GitHub here

-(id)initWithSize:(CGSize)size {    
    if (self = [super initWithSize:size]) {
        
        self.gameStarted = NO;
        self.isGameOver  = NO;
        self.currentDistanceBetweenObstacles = 0;
        
        self.backgroundColor = [SKColor blackColor];
        self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
        
        self.bird = [Bird spriteNodeWithColor:[UIColor whiteColor] size:CGSizeMake(30, 25)];
        self.bird.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:self.bird.size.width/2];
        self.bird.physicsBody.dynamic = NO;
        self.bird.physicsBody.density = 1.5;
        self.bird.physicsBody.linearDamping = 1.;
        self.bird.position = CGPointMake(160, 300);
        [self addChild:self.bird];
       
        self.obstacles = [NSMutableArray array];
        
        [self addNewObstacle];
    }
    return self;
}

I started the project from the default Xcode SpriteKit template. The first thing to do is set up the bird in the initWithSize method. We switch on the physics here and wrap the screen in bodyWithEdgeLoopFromRect so the bird cannot leave the screen. You should play around with the physicsBody parameters of the bird, it could do with a bit more tweaking to get the gravitational movement right. We also instantiate the array of obstacles.

- (void)addNewObstacle
{
    SKSpriteNode *obstacleTop = [SKSpriteNode spriteNodeWithColor:[UIColor greenColor] size:CGSizeMake(kObstacleWidth, 568.)];
    obstacleTop.anchorPoint = CGPointMake(0, 0);
    CGPoint topObstacleBasePoint = CGPointMake(320. + kObstacleWidth, [self randomValueBetween:kMinHeight andValue:kMaxHeight]);
    obstacleTop.position = topObstacleBasePoint;
    
    SKSpriteNode *obstacleBottom = [SKSpriteNode spriteNodeWithColor:[UIColor greenColor] size:CGSizeMake(kObstacleWidth, 568.)];
    obstacleBottom.anchorPoint = CGPointMake(0, 1);
    obstacleBottom.position = CGPointMake(obstacleTop.position.x, obstacleTop.position.y - kObstacleVertSpace);
    
    [self addChild:obstacleTop];
    [self addChild:obstacleBottom];
    
    [self.obstacles addObject:obstacleTop];
    [self.obstacles addObject:obstacleBottom];
}

The addNewObstacle method creates two obstacle sprites. The base of the top obstacle is positioned randomly on the y axis within a range of 250 and 450 pixels. The bottom obstacle is positioned about 55 pixels below the top one to create the gap, I’m also doing some trickery here with the anchor points to simplify the positioning. Adjust this distance between the obstacles to change the difficulty of the game.

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    for (UITouch *touch in touches) {
        if (!self.gameStarted) {
            self.gameStarted = YES;
            self.bird.physicsBody.dynamic = YES;
        }
        [self.bird bounce];
    }
}

On touchesBegan we start the game, or if the game is in progress we call an exposed method in the Bird that applies the linear impulse.

-(void)update:(CFTimeInterval)currentTime
{
    if (!self.isGameOver && self.gameStarted) {
        
        NSMutableArray *objectsToRemove = [NSMutableArray array];
        self.currentDistanceBetweenObstacles += kSpeed;
        
        if (self.currentDistanceBetweenObstacles >= kObstacleHorizSpace) {
            self.currentDistanceBetweenObstacles = 0;
            [self addNewObstacle];
        }
        
        for (SKSpriteNode *obstacle in self.obstacles) {
            CGPoint currentPos = obstacle.position;
            obstacle.position = CGPointMake(currentPos.x - kSpeed , currentPos.y);
            
            // REMOVE WHEN OFF SCREEN
            if (obstacle.position.x + obstacle.size.width < 0) {
                [obstacle removeFromParent];
                [objectsToRemove addObject:obstacle];
            }
            
            // RUN A BASIC SPRITE HIT TEST
            if ([obstacle intersectsNode:self.bird]) {
                self.isGameOver = YES;
                [self restart];
                break;
            }
        }
        // remove outside of the for loop
        [self.obstacles removeObjectsInArray:objectsToRemove];
    }
}

Every frame in the update method, if the game is in progress, we see if we’ve passed enough distance to add a new obstacle (about 155 pixels) again you can adjust this to change the difficulty of the game. We then cycle through all the objects on the screen, move them over by kSpeed pixels, check to see if the obstacle has moved off the screen, if it has we remove it from the scene, then we run a basic hit test against the other obstacles and the bird using intersectsNode: method. This will return a Boolean value if the square border of the sprites is overlapping. If there’s a hit registered, we set the gameOver Boolean to YES, stopping the movement of the obstacles and we call restart and break from the for loop. It’s probably important to mention here that we could have used the more accurate physics collision detection by turning the obstacles into physical bodies, but in the interest of simplicity I went with Sprite collision. Outside of the for loop we remove any obstacles we’ve set up to be removed from the obstacles array, we must do this here so we don’t mutate the array while enumerating through it.

- (void)restart
{
    for (SKSpriteNode *obstacle in self.obstacles) {
        [obstacle removeFromParent];
    }
   [self.obstacles removeAllObjects];
    
    self.bird.position = CGPointMake(160, 300);
    self.bird.physicsBody.dynamic = NO;
    
    self.gameStarted = NO;
    self.isGameOver  = NO;
    self.currentDistanceBetweenObstacles = 0;
    
    [self addNewObstacle];
    
}

The restart method simple sets up the game to where it was at the beginning, removes all the obstacles, repositions the bird and turns off it’s dynamic property until the user taps again.

- (void)bounce
{
    CGFloat birdDirection = self.zRotation + M_PI_2;
    self.physicsBody.velocity = CGVectorMake(0, 0);
    [self.physicsBody applyImpulse: CGVectorMake(bounceImpulse*cosf(birdDirection),
                                                    bounceImpulse*sinf(birdDirection))];
}

Inside the Bird class there’s an exposed method called bounce. Here we’re applying an impulse the physicsBody of the sprite, but first I’m resetting the velocity to zero, this was a little tweak I added to mimic the physics in Flappy Bird, if we don’t reset it the velocity increases exponentially every time we tap and the bird feels too light and has the wrong motion. Because the zRotation is pointing up the impulse always shoots the body upward.

That’s my really basic version of the Flappy Bird game mechanic in under 150 lines of code. Go forth and take the app store by storm!

15 comments

    1. Hey Matt, thanks for reading the tutorial. I started out using the physics engine contact detection, but decided to run with basic shape sprites and a node intersection hit test just to keep it super simple. Great post BTW!

      Reply

  1. I’m looking at the code and I’m not sure which part of the code is responsible for getting the screen/pipes to move. Could you explain further?

    Reply

    1. Hey no problem,
      have a look inside the update method, there is a for loop that cycles through the obstacles that we’ve place on the screen:

       for (SKSpriteNode *obstacle in self.obstacles) {
                  CGPoint currentPos = obstacle.position;
                  obstacle.position = CGPointMake(currentPos.x - kSpeed , currentPos.y);
                   
      

      First we ge the current position of the obstacle, then subtract kSpeed from it’s current xPos, this moves it kSpeed pixels to the left.

      Reply

  2. Excellent breakdown of the game! I’d never saw the allure of it myself, and this definitely takes even more out of it. Still, can’t argue with the consensus, I suppose.

    Reply

    1. Hahaha, yes if you don’t like the game this definitely isn’t going to sell it to you.

      Reply

  3. We stumbled over here from a different website andd
    thought I mivht check things out. I like what I ssee so i am jujst following you.
    Loook forward to looking at your web page repeatedly.

    Reply

  4. Heya i am for the first tim here. I came across this board and I find It truly
    useful & it helped me out a lot. I hope to give somwthing back and help others like
    you aided me.

    Reply

Leave a Reply to jungle treasure 2 Cancel reply

Your email address will not be published. Required fields are marked *