Floaty Balloon: How to Build a Flappy Bird Clone in 2 Hours

Unless you’ve been living under a rock, you’ll have probably heard of a little iOS game called Flappy Bird.

Whilst users went nuts over it, the iOS developer community’s response to Flappy Bird was a bit less enthusiastic; many criticised it for its poor implementation and relative crudeness. But though several developers declared that they could have built Flappy Bird in an hour, others quietly confessed that, although they could see it was simplistic, they actually wouldn’t have the first clue where to begin with writing a game for iOS – even one as simple as Flappy Bird.

Games are a hugely popular genre of app, but making games is a somewhat different process to traditional application development. There are dozens of ways to build games on iOS, from cross-platform tools such as Unity, to 3rd party frameworks such Cocos2D, to built-in APIs such as SpriteKit and GLKit. Most app developers have heard of these, but many have never have tried using them.

In this tutorial, we’ll demystify gaming on iOS by building Flappy Bird Floaty Balloon in a couple of hours, using ordinary UIKit classes that any app developer should already be familiar with. UIKit is not designed for gaming, but as we will demonstrate, it’s perfectly suitable for building a simple game such as this.

Graphics

This is the first hurdle to writing a game, and is often the point at which an aspiring programmer gives up on their dream of game development. Creating graphics is a PITA if you are not creatively inclined. To save you the from the horror of launching Photoshop and staring at a blank canvas, here are some sprites I created earlier:

Floaty-Balloon-Graphics

Download the Floaty Balloon Images

You may notice that these sprites are very small; I’m using a technique called pixel art, which makes creating graphics a lot easier for the artistically challenged (myself included) by reducing the number of pixels you need to draw down to a manageable number. It also looks kinda neat, in a retro sort of way.

To keep the scaling math simple, the graphics for Floaty Balloon have all been created at 1 tenth scale, so one pixel in the image will be scaled up to 10×10 points on the iPhone screen (which equates to 20×20 pixels on a Retina display). A full-screen background image at this scale is therefore a mere 32×57 pixels (320×568 divided by 10, then rounded up the nearest whole pixel).

Setting Up the Project

This is a very simple game, so we are going to put all the logic in our view controller. In general, this is not a good idea, and you should separate different game entities into their own classes, but for the purposes of our example this will keep things simpler.

Our game feature the following entities, each of which will require slightly different treatment:

  • The background – this doesn’t move.
  • The ground – this scrolls constantly.
  • The balloon – this moves up and down, but not sideways.
  • The buildings and clouds – these scroll from right to left and need to be freshly created with different positions each time they appear.

When you hear “scrolling”, as an app developer, your mind probably jumps to UIScrollView, but we won’t be using one of those. UIScrollView is used to model user-scrollable content, with scrollbars and bounce physics and all sorts of other goodies, but in our game the scrolling is programmatically controlled, and moves at a constant pace. UIScrollView would give us nothing useful here; everything we will see on screen can be implemented with simple UIViews and UIImageViews.

We’ll add some properties to the view controller to represent our game objects. These are all included in the private class extension in our ViewController.m file. Because our entire game takes place inside the view controller, there’s no reason for any properties to be public.

@interface ViewController ()
 
@property (nonatomic, strong) UIView *balloon;
@property (nonatomic, strong) UIView *ground;
@property (nonatomic, strong) NSMutableArray *scenery;
 
@property (nonatomic, strong) UIPushBehavior *floatUp;
@property (nonatomic, strong) UIDynamicAnimator *animator;
@property (nonatomic, assign) NSTimeInterval lastTime;
 
@end

The first group of properties above are pretty self-explanatory. There will only ever be one copy of the balloon and ground views on screen at once, so we have a property for each of these. There will be multiple clouds and buildings, so we have a scenery array to contain these. We don’t have a property for our background image because it doesn’t move, so we don’t need to retain a reference to it once it has been added to the view.

The types specified in the second group of properties may be less familiar to you. Don’t worry about these; they will all be explained as we go along.

We’ll begin by assembling our views in the viewDidLoad event, just as we would with any ordinary app. I would normally recommend that you do this in a Storyboard or nib file, as doing so will eliminate around a dozen lines of magic-number-laden layout code. It’s difficult to represent Interface Builder layouts in a screenshot however, and there are some subtleties to how we’ve framed our views, so for the purposes of this article, we’ll lay all our views out programmatically.

We’ll start with the background image view. Add the following to viewDidLoad:

- (void)viewDidLoad
{
  [super viewDidLoad];
 
  //add background
  UIImage *backgroundImage = [UIImage imageNamed:@"Background"];
  UIImageView *backgroundView = [[UIImageView alloc] initWithImage:backgroundImage];
  backgroundView.layer.magnificationFilter = kCAFilterNearest;
  backgroundView.frame = CGRectMake(0, self.view.bounds.size.height - 570, 320, 570);
  [self.view addSubview:backgroundView];
 
}

Our background image is very tiny (32×57 pixels), but we are displaying it at 10× scale, so we have hard-coded the frame rather than relying on the UIImageView initWithImage: method to set the size automatically. Our upscaled image is big enough to fill a 4″ screen, but we want it to work on an older 3.5″ iPhone as well, so we’ve bottom-aligned the image so that only the empty sky gets cut off on a smaller screen.

By default, when upscaling small images, UIImageView will apply an effect called Bilinear Filtering to smooth out the rough pixels. This is a good idea most of the time, but it makes pixel art look horrible. To fix that, we’ve made use of the view.layer.magnificationFilter property to override the scaling behavior. The constant kCAFilterNearest forces the view to use Nearest Neighbour scaling instead, which preserves the hard edges of our pixels.

Next up, we’ll add the ground view. Append the following code to the viewDidLoad method:

- (void)viewDidLoad
{
  …
 
  //add ground
  UIImage *groundImage = [UIImage imageNamed:@"Ground"];
  self.ground = [[UIImageView alloc] initWithImage:groundImage];
  self.ground.layer.magnificationFilter = kCAFilterNearest;
  self.ground.frame = CGRectMake(0, self.view.bounds.size.height - 60, 640, 60);
  [self.view addSubview:self.ground];
 
  //set up scenery array
  self.scenery = [NSMutableArray arrayWithObject:self.ground];
 
}

Note that we’ve added the ground view to the scenery array. Later this array will include our buildings and clouds as well. All these elements will scroll and potentially collide with the player, so keeping them all in the same array will simplify our game logic.

Our upscaled ground image is 640 points wide – the equivalent of two screen’s width on an iPhone. That’s because of how we are going to implement continuous scrolling; basically, we’ll scroll a full screen’s width, and then flip the ground view back to its original position, then continue scrolling. Because the ground pattern repeats, this switchover will be seamless to the user, giving the impression of an infinite ground plane.

Finally, we add the balloon view:

- (void)viewDidLoad
{
  …
 
  //add balloon
  self.balloon = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 40, 50)];
  UIImage *balloonImage = [UIImage imageNamed:@"Balloon"];
  UIImageView *balloonView = [[UIImageView alloc] initWithImage:balloonImage];
  balloonView.layer.magnificationFilter = kCAFilterNearest;
  balloonView.frame = CGRectMake(-5, -5, 50, 140);
  [self.balloon addSubview:balloonView];
  self.balloon.center = CGPointMake(50, self.view.bounds.size.height / 2);
  [self.view addSubview:self.balloon];
 
}

With the balloon view, we’ve done something a bit curious: Rather than add the balloon’s UIImageView directly to the screen as we did with the background and ground views, we’ve wrapped it in a smaller UIView. This is to do with collision detection, which we’ll come back to later. For now all you need to know is that because views do not clip to their bounds by default, our balloon view’s frame will have the dimensions of 40×50 points, but on screen it will be drawn at its full size of 50×140 points. The following image illustrates the relationship between the containing view and the UIImageView inside it:

Balloon-Frame

If we run the app now, we’ll see this:

Floaty-Balloon-1

OK, that’s the background and the balloon, but what about the rest of the scenery? In our game the player must avoid crashing into buildings and clouds, so let’s add those now. Unlike the views we’ve added so far, the buildings and clouds will need to be created and destroyed repeatedly as the level scrolls, so we’ll create separate constructor methods for them that we can call multiple times:

- (void)addBuildingWithOffset:(CGPoint)offset
{
  UIImage *buildingImage = [UIImage imageNamed:@"Building"];
  UIImageView *buildingView = [[UIImageView alloc] initWithImage:buildingImage];
  buildingView.layer.magnificationFilter = kCAFilterNearest;
  buildingView.frame = CGRectMake(offset.x - 40, offset.y, 80, 570);
  [self.view insertSubview:buildingView belowSubview:self.ground];
  [self.scenery addObject:buildingView];
}
 
- (void)addCloudWithOffset:(CGPoint)offset
{
  UIView *cloud = [[UIView alloc] initWithFrame:CGRectMake(offset.x - 50, offset.y - 560, 100, 560)];
  UIImage *cloudImage = [UIImage imageNamed:@"Cloud"];
  UIImageView *cloudView = [[UIImageView alloc] initWithImage:cloudImage];
  cloudView.layer.magnificationFilter = kCAFilterNearest;
  cloudView.frame = CGRectMake((cloud.frame.size.width - 240) / 2, 0, 240, 570);
  [cloud addSubview:cloudView];
  [self.view addSubview:cloud];
  [self.scenery addObject:cloud];
}

The building constructor method is fairly unsurprising – the only thing worth noting is that we are inserting the buildings behind the ground view, so that the layering of our scene always looks correct. The cloud constructor uses the same trick as we used earlier with the balloon – we wrap the UIImageView inside a smaller UIView so that its apparent size is smaller than the actual dimensions of the image (see image below). This will be useful later when we want to handle collisions.

Cloud-Frame

We now need to place some buildings and clouds on screen. We will position the buildings and clouds at a randomly selected height, based on some constant parameters that we can tweak later to tune the gameplay. We want the cloud cover to appear continuous, so we’ll actually add two cloud views over every building. The following method implements that logic.

Add the following const definitions at the top of the ViewController.m file (just after the #import):

const CGFloat minHeight = 150; //the minimum building height
const CGFloat maxHeight = 300; //the maximum building height
const CGFloat gap = 150; //the gap between buildings and clouds

Add the method below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (void)addBuildingAndClouds
{
  //calculate horizontal position
  CGFloat x = self.view.frame.size.width / 2;
 
  //get random y position
  CGFloat y = self.view.frame.size.height - minHeight - arc4random_uniform(maxHeight - minHeight);
 
  //add building
  [self addBuildingWithOffset:CGPointMake(x, y)];
 
  //add clouds
  [self addCloudWithOffset:CGPointMake(x, y - gap)];
  [self addCloudWithOffset:CGPointMake(x + 160, y - gap - 50)];
}

And the result looks like this:

Floaty-Balloon-2

The game isn’t playable yet, but at least it’s starting to take shape. Although the building and clouds look nice in this still frame, we actually want them to start off-screen so that the player isn’t immediately killed when the game begins, before they’ve had time to react. We’ll tweak the x position value so that the starting position is 1.5 screens over to the right, instead of 0.5 as it is now.

Replace the code in lines 3-4 with the following:

//calculate horizontal position
CGFloat x = self.view.frame.size.width * 1.5;

Next, we need to add some movement.

Setting Things in Motion

In a typical app, animations are triggered by user interactions such as pressing a button. One of the mental hurdles that app developers struggle with when writing a game is the idea that the game runs by itself, without events needing to be triggered by the user.

But this isn’t such a strange concept really; if you asked an app developer how they would make a method execute over and over again without any user input, they’d say that you should use an NSTimer – and prior to iOS7 that would exactly the approach we’d have taken. We’d have set a timer to repeatedly call an update method to move our game objects and run our game logic.

We aren’t going to do that this time however, because the UIDynamics APIs give us a simpler and more powerful alternative. UIDynamics is a new set of classes in iOS 7 that let us add physical behaviours to our views. Effects such as gravity, acceleration and collisions, which we’d previously have needed to use a third-party library to simulate, are now all built-into UIKit. UIDynamics works as follows:

  • We add a UIDynamicAnimator to our view controller – this manages our animations.
  • We add one or more UIDynamicBehaviors to our animator – these simulate specific physical effects.
  • We add one or more views to each behavior – that lets the UIDynamicAnimator take control of animating those views.

Let’s set up an animator and use UIGravityBehavior (a subclass of UIDynamicBehavior) to make our balloon fall. Append the following to the viewDidLoad method:

- (void)viewDidLoad
{
  …
 
  //set up animator
  self.animator = [[UIDynamicAnimator alloc] init];
 
  //add gravity
  UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:@[self.balloon]];
  gravity.magnitude = 0.5;
  [self.animator addBehavior:gravity];
 
}

And that’s it! Run the app and our balloon will now fall off the bottom of the screen. OK, so how do we stop it from falling off the screen? When the user taps the screen, we want the balloon to float upwards – how can we do that?

There’s another subclass of UIDynamicBehavior called UIPushBehaviour that can be used to apply a force to a view. If we apply an upward force to our balloon when the user taps the screen, it will counter the effect of gravity.

We already declared a UIPushBehavior property called “floatUp” in our ViewController interface. Let’s set that up in our viewDidLoad:

- (void)viewDidLoad
{
  …
 
  //add floating behavior
  self.floatUp = [[UIPushBehavior alloc] initWithItems:@[self.balloon] mode:UIPushBehaviorModeInstantaneous];
  self.floatUp.pushDirection = CGVectorMake(0, -0.5);
  self.floatUp.active = NO;
  [self.animator addBehavior:self.floatUp];
 
}

We’ve configured our UIPushBehavior with a mode of UIPushBehaviorModeInstantaneous, meaning the force is applied instantaneously, not continuously over a period of time. We’ve also set the active property to NO, because we don’t want to apply the force right away, we want to trigger it later when we tap the screen. To detect the tap, we’ll just use an ordinary UITapGestureRecognizer:

- (void)viewDidLoad
{
  …
 
  //add tap handler
  UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapped)];
  [self.view addGestureRecognizer:tapGesture];
 
}

Then in our tapped method, we activate the floatUp behavior:

- (void)tapped
{
  //apply vertical force
  self.floatUp.active = YES;
}

Now, if we tap the screen, our balloon will float upwards instead of falling. The floatUp UIPushBehavior will then deactivate itself automatically, ready for the next time we tap. All that’s left is to implement scrolling and collisions.

Scrolling

UIDynamics is designed to simulate physical interactions, and in real life things don’t move continuously – they accelerate to a certain speed, move for a finite time and then slow down again. But our game isn’t real life, and we want continuous scrolling. How can we simulate that?

The built-in behaviours of UIDynamics don’t make this easy, because the physics engine always expect objects to eventually come to rest. Even with zero friction, if we apply force or velocity to our views they will eventually slow down and stop.

In the pre-iOS 7 days we’d have set up an NSTimer (or CADisplayLink) to fire at 60 frames per second and move all of our views in tiny steps. UIDynamics gives us a better way to do this however: The UIDynamicBehavior class has a block property called “action” that is called repeatedly every frame while the physics engine is running. This can be used for implementing custom animation types, and is perfect for implementing our scrolling behaviour. We’ll add a new UIDynamicBehavior instance and implement the action block.

Begin by adding the following const to the top of ViewController.m alongside the other const definitions:

const CGFloat scrollSpeed = 100; //the speed at which the level scrolls

Then append the following to viewDidLoad:

- (void)viewDidLoad
{
  …
 
  //add custom behavior
  UIDynamicBehavior *scroll = [[UIDynamicBehavior alloc] init];
  scroll.action = ^{ [self update]; };
  [self.animator addBehavior:scroll];
}

We’ve set the action block to call a method called “update” on our view controller. That’s where we will put our scrolling logic:

- (void)update
{
  //get time since last update
  NSTimeInterval deltaTime = self.animator.elapsedTime - self.lastTime;
  self.lastTime = self.animator.elapsedTime;
 
  //calculate scroll distance
  CGFloat scrollStep = scrollSpeed * deltaTime;
 
  //scroll ground, buildings and clouds
  for (UIView *view in self.scenery)
  {
    view.center = CGPointMake(view.center.x - scrollStep, view.center.y);
  }
 
}

The first thing we do inside our update method is calculate the delta time – that’s the time that has passed since the last update. We do that by comparing the total elapsed time since the animator started running against the lastTime property that we have stored in our controller.

By multiplying the deltaTime by our scrollSpeed constant (measured in pixels-per-second) we can determine how far we need to scroll our views for this step of the animation. We then loop through the scenery array and update all the view positions.

I should point out that using UIDynamics in this way is technically cheating. What we ought to do is create a UIDynamicItemBehavior and add our ground, building and cloud views to it so that it can manipulate them using simulated forces, instead of simply moving their frames. In practice however, I found using UIDynamicItemBehavior to implement continuous scrolling of multiple elements is much more complicated than a step-based approach, especially when it comes to implementing the wraparound and collision behaviours we’ll discuss shortly.

If we run the app again, we’ll see that our ground and buildings scroll off the left of the screen and disappear. That’s not really ideal. What we want is for the views to wrap back around again. To do that, we’ll detect when the ground view has scrolled beyond one screen’s distance, and then move it back to its original position. We can also remove any offscreen clouds and buildings and add new ones on the other side of the screen at the same time.

Add the code below to the update method:

- (void)update
{
  …
 
  //wrap around if necessary
  if (self.ground.frame.origin.x < -self.view.frame.size.width)
  {
    //reset ground position
    self.ground.center = CGPointMake(self.ground.center.x + self.view.frame.size.width, self.ground.center.y);
 
    //remove offscreen clouds and buildings
    for (UIView *view in [self.scenery reverseObjectEnumerator])
    {
      if (view.frame.origin.x + view.frame.size.width < 0)
      {
        [self.scenery removeObject:view];
        [view removeFromSuperview];
      }
    }
 
    //add new buildings and clouds
    [self addBuildingAndClouds];
  }
 
}

If we run the app now, we have a nice continuous scrolling effect, with new buildings and clouds appearing at random heights from the right of the screen. All that remains is to detect collisions, so that the game ends if our balloon collides with the scenery.

Collision Detection

Normally, when using UIDynamics, we would add a UICollisionBehavior to our animator that would manage detecting and handling collisions between our game objects. For a more sophisticated game that would make collision handling much easier, but here it would actually make things more complicated.

As soon as you apply UIDynamics to a view, you lose the ability to manipulate its frame directly because the physics engine takes over control (a bit like AutoLayout constraints). If we add a collision behavior, it will interfere with the way that we have implemented scrolling. We’d also potentially have problems such as our ground colliding with our buildings, or the buildings, clouds and ground views being dislodged when they collide with the balloon, and floating off into space.

You can work around all of these issues by using multiple collision groups and setting the physical properties of the collision behaviors to restrict certain types of movement, but this would mean adding a bunch of extra code for the sake of turning off physics features that we don’t really want in the first place.

The collision requirements of our game are actually very simple; you may recall from earlier that we deliberately made the balloon and cloud view frames smaller than the visible area of the sprites. The reason we did this was so that the frames would correspond to the collidable areas of our objects. Modelling a circular balloon or cloud with a rectangle is always going to be an approximation, but by using the old-school trick of making the collision rectangle slightly smaller than the bounding rectangle of the image, the approximation is good enough to use for collision detection (see diagram below).

Collisions

Since all of our game objects have rectangular collision areas, and we don’t want to model any realistic physical collision response (such as objects bouncing off of each other), we can handle collision detection between the balloon and scenery using simple CGRectIntersectsRect tests between their view frames. If we detect an overlap between the frame of the balloon view and any of the scenery views, it’s game over.

Append the following code to the update method:

- (void)update
{
  …
 
  //check for collision with scenery (bounding box check)
  for (UIView *view in self.scenery)
  {
    if (CGRectIntersectsRect(self.balloon.frame, view.frame))
    {
      //end simulation
      [self.animator removeAllBehaviors];
 
      //game over
      [[[UIAlertView alloc] initWithTitle:@"Game Over" message:nil delegate:self cancelButtonTitle:@"Play Again" otherButtonTitles:nil] show];
 
      break;
    }
  }
}

When we detect the game over scenario, we terminate the simulation by removing all behaviors from our animator, which halts the effects of gravity and our floatUp method, and stops the update block from firing. The game is then effectively paused while we display the “Game Over” alert.

Game Over

We now have a fully playable game – we just need a way to restart the game again when we (inevitably) die. To do that, let’s move some of the setup logic out of our viewDidLoad and into its own method that we can call it again to restart the game. In the process we’ll also need to add some cleanup code to remove leftover views and UIDynamicBehavior relationships from the previous game.

Replace the your viewDidLoad method with this modified version:

- (void)viewDidLoad
{
  [super viewDidLoad];
 
  //add background
  UIImage *backgroundImage = [UIImage imageNamed:@"Background"];
  UIImageView *backgroundView = [[UIImageView alloc] initWithImage:backgroundImage];
  backgroundView.layer.magnificationFilter = kCAFilterNearest;
  backgroundView.frame = CGRectMake(0, self.view.bounds.size.height - 570, 320, 570);
  [self.view addSubview:backgroundView];
 
  //add ground
  UIImage *groundImage = [UIImage imageNamed:@"Ground"];
  self.ground = [[UIImageView alloc] initWithImage:groundImage];
  self.ground.layer.magnificationFilter = kCAFilterNearest;
  self.ground.frame = CGRectMake(0, self.view.bounds.size.height - 60, 640, 60);
  [self.view addSubview:self.ground];
 
  //add balloon
  self.balloon = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 40, 50)];
  UIImage *balloonImage = [UIImage imageNamed:@"Balloon"];
  UIImageView *balloonView = [[UIImageView alloc] initWithImage:balloonImage];
  balloonView.layer.magnificationFilter = kCAFilterNearest;
  balloonView.frame = CGRectMake(-5, -5, 50, 140);
  [self.balloon addSubview:balloonView];
  self.balloon.center = CGPointMake(50, self.view.bounds.size.height / 2);
  [self.view addSubview:self.balloon];
 
  //set up animator
  self.animator = [[UIDynamicAnimator alloc] init];
 
  //add tap handler
  UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapped)];
  [self.view addGestureRecognizer:tapGesture];
 
  //show start screen
  [[[UIAlertView alloc] initWithTitle:@"Floaty Balloon" message:@"Tap to float upwards. Avoid the ground, buildings and clouds." delegate:self cancelButtonTitle:@"Start" otherButtonTitles:nil] show];
}

Then, add the following methods to your view controller:

- (void)reset
{
  //reset simulation
  [self.animator removeAllBehaviors];
 
  //clean up scenery views
  [self.scenery makeObjectsPerformSelector:@selector(removeFromSuperview)];
  self.scenery = [NSMutableArray array];
 
  //reset ground start position and add it to scenery array
  self.ground.center = CGPointMake(self.view.frame.size.width, self.ground.center.y);
  [self.scenery addObject:self.ground];
  [self.view insertSubview:self.ground belowSubview:self.balloon];
 
  //reset balloon start position
  self.balloon.center = CGPointMake(50, self.view.bounds.size.height / 2);
 
  //add floating behavior
  self.floatUp = [[UIPushBehavior alloc] initWithItems:@[self.balloon] mode:UIPushBehaviorModeInstantaneous];
  self.floatUp.pushDirection = CGVectorMake(0, -0.5);
  self.floatUp.active = NO;
  [self.animator addBehavior:self.floatUp];
 
  //add gravity
  UIGravityBehavior *gravity = [[UIGravityBehavior alloc] initWithItems:@[self.balloon]];
  gravity.magnitude = 0.5;
  [self.animator addBehavior:gravity];
 
  //add initial building and clouds
  [self addBuildingAndClouds];
 
  //add custom behavior
  UIDynamicBehavior *scroll = [[UIDynamicBehavior alloc] init];
  scroll.action = ^{ [self update]; };
  [self.animator addBehavior:scroll];
}
 
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
  //start the game, after short delay
  [self performSelector:@selector(reset) withObject:nil afterDelay:0.5];
}

We’ve added an instructional alert to start the game instead of starting immediately when the app is launched. Both this and the game over alert will call the alertView:clickedButtonAtIndex: delegate method when closed, which triggers the reset method (after a 0.5-second delay to prevent the game from starting while the alert view is still animating).

Further Development

You can download the artwork and Xcode project for Floaty Balloon from here: Flappy Bird Clone. Warning: It’s fiendishly difficult and rather addictive. There is a lot more you could do to make this into a fully-fledged game, however:

  • Add scoring – detect when the user passes a building and increment an on-screen score.
  • Add parallax background scrolling – the background is currently static, but you could make it scroll at a slower speed than the ground and buildings to add a greater sense of depth.
  • High scores table, achievements, etc – use Game Center to implement a global high scores table.
  • Improve the collision detection – try modelling the balloon as an oval and the clouds as circles (or circle + rectangle). The math for doing circle -> rectangle intersection detection is outside the scope of this tutorial, but if you are familiar with vectors and Pythagoras, it’s pretty straightforward.

Apple seems to be cracking down on Flappy Bird clones in the App Store now (so it’s probably too late to release your own version), but hopefully this will inspire you to create some unique games of your own.


Nick Lockwood is the author of iOS Core Animation: Advanced Techniques. Nick also wrote iCarousel, iRate and other Mac and iOS open source projects.
  1. Great tutorial. It was actually crashing for me right before it got to the second building. downloaded your code and I noticed that the tutorial has this for calculating the horizontal position:

    //calculate horizontal position
    CGFloat x = self.view.frame.size.width * 1.5;

    But in your project you have:

    //calculate horizontal position
    CGFloat x = self.ground.frame.origin.x + self.view.frame.size.width * 1.5;

    But I think the crash was caused by the update method above having:

    for (UIView *view in self.scenery)

    But in your code, you have

    for (UIView *view in [self.scenery reverseObjectEnumerator])

    Thanks again for posting this! Very cool!

    • Thanks for letting me know! I’ve fixed the offending line of code.

      The loop modifies the scenery array, so by using reverseObjectEnumerator instead of enumerating the array directly, we avoid an exception.

      The other issue you spotted shouldn’t make any difference to the behaviour; we only add new clouds and buildings when the ground position has been reset to 0,0, so self.ground.frame.origin.x should be zero, or close to it. Well spotted though!

  2. Very nice!
    I don’t suppose we could see a part-2 covering the “Further Development” items in more detail (particularly the first 3 items)

Comments are closed.

  • Related Content by Tag