[Objc] How to make Flappy Bird like game using UIKit

As explained in the last post, ( How to make Flappy Bird like game using Cocos2D ) today, we learn how to make a Bird using Objectivec for 7 in 20 rows of code!

What you need?

XCode and iOS 7 SDK.
Open XCode, make new project and select SpriteKit Game for iOS or OSX, it’s equal.
Screenshot 2014-02-22 01.19.57

Prepare game graphics

~ background
MarioBackground-static
~ scrollable floor
MarioBackground-scrolling
~ scrollable sky
MarioBackground-scrolling-top
~ the player
Bird@2x
~ the pipes (up / down)
PipeBottom@2x
PipeTop@2x

Making main menu scene

In your new file, MyScene.m, edit initWithSize method adding some , as the background image, the fish, and a SKLabelNode, that is the title.

Very easy to draw:

// add the backgound
SKSpriteNode *background = [SKSpriteNode spriteNodeWithImageNamed:@"MarioBackground-static"];
[background setPosition:CGPointMake(self.frame.size.width/2, self.frame.size.height/2 - 12)];
[self addChild:background];// add the title label

SKLabelNode *titleLabel = [[SKLabelNode alloc] initWithFontNamed:@"Helvetica"];
[titleLabel setPosition:CGPointMake(self.size.width/2, self.size.height-150)];
[titleLabel setText:@"Touch to start"];
[self titleLabel];

// add the player prite
SKSpriteNode *player = [SKSpriteNode spriteNodeWithImageNamed:@"Bird"];
[player setPosition:CGPointMake(self.size.width/2, self.size.height/2)];
[self addChild:player];

You should see something like this:
Screenshot 2014-02-22 13.02.40

Making game scene

Make a new file, MyWorld.m of type Sprite.
Next in MyScene.h in touchesBegan method, replace contents with

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
  // choose you 
  SKTransition *transition = [SKTransition doorsCloseHorizontalWithDuration:.4];// present new scene
  MyWorld *main = [[MyWorld alloc] initWithSize:self.size];
  [self.scene.view presentScene:main transition:transition];
}

Now, in your new file, draw the layer like the main menu, as you want in the initWithSize method.
Before drawing the elements, you need to apply the gravity on your world

// customize your gravity here
[self.physicsWorld setGravity:CGVectorMake(0, -10)];// detect collision enabled
[self.physicsWorld setContactDelegate:self];

For this project I’ve used an extension of SKSpriteNode, called PBParallaxScrolling. You can find it on github.
It’s an extension used to easily make parallax scrolling. Download and add.

PBParallaxScrolling is used for example to scroll sky:

PBParallaxScrolling * parallax = [[PBParallaxScrolling alloc]
    initWithBackgrounds:@[@"MarioBackground-scrolling-top"]
    size:CGSizeMake(320, self.frame.size.height) direction:kPBParallaxBackgroundDirectionLeft
    fastestSpeed:1.0
    andSpeedDecrease:kPBParallaxBackgroundDefaultSpeedDifferential];

_parallaxBackgroundSky = parallax;
[self addChild:parallax];

or to scroll background:

PBParallaxScrolling * parallax = [[PBParallaxScrolling alloc]
    initWithBackgrounds:@[@"MarioBackground-scrolling"]
    size:CGSizeMake(320, self.view.frame.size.height + 70) direction:kPBParallaxBackgroundDirectionLeft
    fastestSpeed:2.1
    andSpeedDecrease:kPBParallaxBackgroundDefaultSpeedDifferential];

_parallaxBackground = parallax;
[self addChild:parallax];// 1

_parallaxBackground.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(320, 5)];
[_parallaxBackground.physicsBody setAffectedByGravity:NO];
[_parallaxBackground.physicsBody setDynamic:NO];
[_parallaxBackground.physicsBody setCategoryBitMask:0x3 ];
[_parallaxBackground.physicsBody setCollisionBitMask: 0x1];

the background layer, recognize collision and gravity.
1) we created the body to don’t fall outside screen and to bounce on the floor.

Also the player should recognize gravity and collision.

// APSpritePlayer is a SKSpriteNode class, empty.
_player = [APSpritePlayer spriteNodeWithImageNamed:@"Bird"];
[_player setPosition:CGPointMake(self.size.width/2, self.size.height/2)];
[self addChild:_player];_player.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:_player.size];
[_player.physicsBody setDensity:kDensity];
[_player.physicsBody setAllowsRotation:NO];
[_player.physicsBody setCategoryBitMask:0x1 ];
// bitmask for collision groups
[_player.physicsBody setContactTestBitMask:0x2 | 0x3];
[_player.physicsBody setCollisionBitMask:0x3 | 0x2];

If it’s all correct, you should see your fish that fall down on the screen and stop on the floor. Parallax backgrounds and sky are currently not updated.

Senza titolo

Make thing moving!

Add or edit, touchBegan and update methods to play with animations.

// jump on touch
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    // make fish jump
    [_player.physicsBody setVelocity:CGVectorMake(_player.physicsBody.velocity.dx, 400)];
}

// update loop method
- (void)update:(NSTimeInterval)currentTime
{
    // scroll parallaxes
    [_parallaxBackground    update:currentTime];
    [_parallaxBackgroundSky update:currentTime];if (_player.physicsBody.velocity.dy > 400) {
    [_player.physicsBody setVelocity:CGVectorMake(_player.physicsBody.velocity.dx, 400)];
}

// rotate player on jump / fall down
CGFloat rotation = ((_player.physicsBody.velocity.dy + 400) / (2*400)) * M_2_PI;
[_player setZRotation:rotation-M_1_PI/2];

Well done, now you’re able to jump!

Senza titolo (1)

Add and count score

You’re should be able to draw a label. Add a label on top-center of screen with text @”0″.

In your initWithSize method, after all drawing, add a schedule, to update score.

[ NSTimer scheduledTimerWithTimeInterval:1.5 target:self selector:@selector(startScoreTimer) userInfo:nil repeats:YES ];

This call every 1.5 seconds, startScoreTimer method and make a +1 on the score label.
Save 1.5 value! This must be the same time of the pipes creation interval!

Add the random pipes

First draw the pipes yourself!
Pipes are SKSpriteNode objects. Place it on screen in a fixed distance one with one.
Make drawing in a method that you next schedule with 1.5 seconds of interval and drawing continuously.

Every pipe should detect collision, of course!
Like the ground, we need to set to the pipe the physical body and gravity.

pipeTop.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:pipeTop.size];
[pipeTop.physicsBody setAffectedByGravity:NO];
[pipeTop.physicsBody setDynamic:NO];
[pipeTop.physicsBody setCategoryBitMask: 0x2];
[pipeTop.physicsBody setCollisionBitMask:0x1];

Do the same things for the pipeDown that you will make.

Move pipes:

To make a continuous horizontal scolling, we use SKAction repeating forever the loop.

/ Move top pipe
SKAction *pipeTopAction = [SKAction moveToX:-(pipeTop.size.width/2) duration:1.5];
SKAction *pipeTopSequence = [SKAction sequence:@[pipeTopAction, [SKAction runBlock:^{
    [pipeTop removeFromParent];
}]]];
[pipeTop runAction:[SKAction repeatActionForever:pipeTopSequence]];

Senza titolo (2)

Collision detection

At the beginning we setted physics world collision delegate.
This make didBeginContact being called on collision.

And now we add this method!

- (void)didBeginContact:(SKPhysicsContact *)contact
{
  SKNode *node = contact.bodyA.node;// boom!
  if ([node isKindOfClass:[APSpritePlayer class]])
  {
    SKTransition *transition = [SKTransition doorsCloseHorizontalWithDuration:.4];// go back to main menu
    APMainMenu *newGame = [[APMainMenu alloc] initWithSize:self.size];
    [self.scene.view presentScene:newGame transition:transition];
  }
}

Now you’re able to detect collision, jump, move pipes, count score, move background and move sky!
That’s all.

As usual, it’s a starting point, and you can download here.
You can also improve, edit and make money using github version.