As explained in the last post, ( How to make Flappy Bird like game using Cocos2D ) today, we learn how to make a Flappy Bird game using Objectivec SKSpriteKit for iOS7 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.
Prepare game graphics
~ background
~ scrollable floor
~ scrollable sky
~ the player
~ the pipes (up / down)
Making main menu scene
In your new file, MyScene.m, edit initWithSize method adding some SKSpriteNode, as the background image, the fish, and a SKLabelNode, that is the title.
Very easy to draw:
[code lang=”java” autolinks=”false” collapse=”false” firstline=”1″ gutter=”true” htmlscript=”false” light=”false” padlinenumbers=”false” smarttabs=”true” tabsize=”4″ toolbar=”false”]// 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];[/code]
You should see something like this:
Making game scene
Make a new file, MyWorld.m of type Sprite.
Next in MyScene.h in touchesBegan method, replace contents with
[code lang=”java” autolinks=”false” collapse=”false” firstline=”1″ gutter=”true” htmlscript=”false” light=”false” padlinenumbers=”false” smarttabs=”true” tabsize=”4″ toolbar=”false”]- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// choose you animation
SKTransition *transition = [SKTransition doorsCloseHorizontalWithDuration:.4];// present new scene
MyWorld *main = [[MyWorld alloc] initWithSize:self.size];
[self.scene.view presentScene:main transition:transition];
}[/code]
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
[code lang=”java” autolinks=”false” collapse=”false” firstline=”1″ gutter=”true” htmlscript=”false” light=”false” padlinenumbers=”false” smarttabs=”true” tabsize=”4″ toolbar=”false”]// customize your gravity here
[self.physicsWorld setGravity:CGVectorMake(0, -10)];// detect collision enabled
[self.physicsWorld setContactDelegate:self];[/code]
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:
[code lang=”java” autolinks=”false” collapse=”false” firstline=”1″ gutter=”true” htmlscript=”false” light=”false” padlinenumbers=”false” smarttabs=”true” tabsize=”4″ toolbar=”false”]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];[/code]
or to scroll background:
[code lang=”java” autolinks=”false” collapse=”false” firstline=”1″ gutter=”true” htmlscript=”false” light=”false” padlinenumbers=”false” smarttabs=”true” tabsize=”4″ toolbar=”false”]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];[/code]
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.
[code lang=”java” autolinks=”false” collapse=”false” firstline=”1″ gutter=”true” htmlscript=”false” light=”false” padlinenumbers=”false” smarttabs=”true” tabsize=”4″ toolbar=”false”]// 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];[/code]
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.
Make thing moving!
Add or edit, touchBegan and update methods to play with animations.
[code lang=”java” autolinks=”false” collapse=”false” firstline=”1″ gutter=”true” htmlscript=”false” light=”false” padlinenumbers=”false” smarttabs=”true” tabsize=”4″ toolbar=”false”]// 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];[/code]
Well done, now you’re able to jump!
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.
[code lang=”java” autolinks=”false” collapse=”false” firstline=”1″ gutter=”true” htmlscript=”false” light=”false” padlinenumbers=”false” smarttabs=”true” tabsize=”4″ toolbar=”false”][ NSTimer scheduledTimerWithTimeInterval:1.5 target:self selector:@selector(startScoreTimer) userInfo:nil repeats:YES ];[/code]
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.
[code lang=”java” autolinks=”false” collapse=”false” firstline=”1″ gutter=”true” htmlscript=”false” light=”false” padlinenumbers=”false” smarttabs=”true” tabsize=”4″ toolbar=”false”]pipeTop.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:pipeTop.size];
[pipeTop.physicsBody setAffectedByGravity:NO];
[pipeTop.physicsBody setDynamic:NO];
[pipeTop.physicsBody setCategoryBitMask: 0x2];
[pipeTop.physicsBody setCollisionBitMask:0x1];[/code]
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.
[code lang=”java” autolinks=”false” collapse=”false” firstline=”1″ gutter=”true” htmlscript=”false” light=”false” padlinenumbers=”false” smarttabs=”true” tabsize=”4″ toolbar=”false”]/ 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]];[/code]
Collision detection
At the beginning we setted physics world collision delegate.
This make didBeginContact being called on collision.
And now we add this method!
[code lang=”java” autolinks=”false” collapse=”false” firstline=”1″ gutter=”true” htmlscript=”false” light=”false” padlinenumbers=”false” smarttabs=”true” tabsize=”4″ toolbar=”false”]- (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];
}
}[/code]
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.