Flappy Bird is one of the most infamous games ever to hit the App Store. What was it’s key to success? It was a simple game that offered players a unique challenge.

This tutorial will show you how to create a game similar to Flappy Bird for iOS using only SpriteKit.

Create a new SpriteKit Game:

Open XCode and click File > New > Project

Below the iOS category , select Application and Game

category select and Click Next

Beside Product Name , enter HappyBird

, enter For the Language, select Swift

For the Game Technology, select SpriteKit

select For Devices, select iPhone and click Next

select and click Select a location to save the project and click Create

Configure the Environment:

Note: Download the asset/image files here

Highlight the HappyBird project

project Uncheck Landscape Left and Landscape Right

and Drag and Drop background. png , topPipe.png , bottomPipe,png and floor.png into the project

, , and into the project Ensure that Copy Items if Needed is selected

is selected In Finder , create a new folder called atlas

, create a new folder called Copy player1.png, player2.png, player3.png and player4.png into the player.altas folder

and into the folder Drag and Drop the atlas folder into the project. All of these files should now appear below the yellow HappyBird folder icon within XCode

folder into the project. All of these files should now appear below the yellow folder icon within XCode Open swift

Below the viewDidLoad() function, change the sibling order attribute from true to false :

skView.ignoresSiblingOrder = false 1 skView . ignoresSiblingOrder = false

Changing this setting allows our game to place our objects in the order we want, instead of letting the computer randomly decide.

function, change the sibling order attribute from to : Changing this setting allows our game to place our objects in the order we want, instead of letting the computer randomly decide. Open GameScene.swift

Remove all of the code within the didMoveToView and touchesBegan function

Add the SKPhysicsContactDelegate to the GameScene Class:

class GameScene: SKScene, SKPhysicsContactDelegate { 1 class GameScene : SKScene , SKPhysicsContactDelegate {

The scene is all set up, so now we’re ready to building our game!

Add the background to the Scene:

Open GameScene.swift

Within GameScene class, define a variable for the background node:

class GameScene: SKScene, SKPhysicsContactDelegate { var myBackground = SKSpriteNode() 1 2 class GameScene : SKScene , SKPhysicsContactDelegate { var myBackground = SKSpriteNode ( )

Within the didMoveToView function, enter the following code:

override func didMoveToView(view: SKView) { myBackground = SKSpriteNode(imageNamed: "background") myBackground.anchorPoint = CGPointZero; myBackground.position = CGPointMake(100, 0); self.backgroundColor = SKColor(red: 80.0/255.0, green: 192.0/255.0, blue: 203.0/255.0, alpha: 1.0) 1 2 3 4 5 6 7 override func didMoveToView ( view : SKView ) { myBackground = SKSpriteNode ( imageNamed : "background" ) myBackground . anchorPoint = CGPointZero ; myBackground . position = CGPointMake ( 100 , 0 ) ; self . backgroundColor = SKColor ( red : 80 . 0 / 255 . 0 , green : 192 . 0 / 255 . 0 , blue : 203 . 0 / 255 . 0 , alpha : 1.0 )

The preceding code assigns and image (background) to the scene. Note that you do not need to include the “.png” extension as SpriteKit will automatically pick up on it.

The . anchorPoint and . position routines define where the background will be placed on the screen

and . routines define where the background will be placed on the screen The backgroundColor property allows you to define the background color. In this case it will be the same color as our background, allowing it to seamlessly blend.

property allows you to define the background color. In this case it will be the same color as our background, allowing it to seamlessly blend. Finally, add the background to the screen by entering the following code in the didMoveToView section:addChild(self.myBackground)

section:addChild(self.myBackground) Run the project. You will see a simple screen with a background image of trees

Add the floor to the Scene:

Within GameScene class, define a variable for two floor nodes:

var myFloor1 = SKSpriteNode() var myFloor2 = SKSpriteNode() 1 2 var myFloor1 = SKSpriteNode ( ) var myFloor2 = SKSpriteNode ( )

Within the didMoveToView function, enter the following code:

myFloor1 = SKSpriteNode(imageNamed: "floor") myFloor2 = SKSpriteNode(imageNamed: "floor") myFloor1.anchorPoint = CGPointZero; myFloor1.position = CGPointMake(0, 0); myFloor2.anchorPoint = CGPointZero; myFloor2.position = CGPointMake(myFloor1.size.width-1, 0); 1 2 3 4 5 6 myFloor1 = SKSpriteNode ( imageNamed : "floor" ) myFloor2 = SKSpriteNode ( imageNamed : "floor" ) myFloor1 . anchorPoint = CGPointZero ; myFloor1 . position = CGPointMake ( 0 , 0 ) ; myFloor2 . anchorPoint = CGPointZero ; myFloor2 . position = CGPointMake ( myFloor1 . size . width - 1 , 0 ) ;

As you seen with the background image, this adds the floor images to the nodes and sets up the anchor points and positions. The position of “myFloor2” is the same distance as the length of the “myFloor1”. This allows the image to continuously repeat itself.

Finally, add the floor nodes to the Scene:

addChild(self.myFloor1) addChild(self.myFloor2) 1 2 addChild ( self . myFloor1 ) addChild ( self . myFloor2 )

Our project is coming together nicely! If you run the project you will now see a floor. Next, we’ll add the player sprite to the scene

Add the player (bird) to the scene:

Within the GameScene class, enter the following code:

let birdAtlas = SKTextureAtlas(named:"player.atlas") var birdSprites = Array<SKTexture>() var bird = SKSpriteNode() 1 2 3 let birdAtlas = SKTextureAtlas ( named : "player.atlas" ) var birdSprites = Array < SKTexture > ( ) var bird = SKSpriteNode ( )

The first line creates a texture atlas. A texture atlas is a series of images that make an animation. The second line creates an array of textures to pick from.

Within the didMoveToView function, define the array of images:

birdSprites.append(birdAtlas.textureNamed("player1")) birdSprites.append(birdAtlas.textureNamed("player2")) birdSprites.append(birdAtlas.textureNamed("player3")) birdSprites.append(birdAtlas.textureNamed("player4")) 1 2 3 4 birdSprites . append ( birdAtlas . textureNamed ( "player1" ) ) birdSprites . append ( birdAtlas . textureNamed ( "player2" ) ) birdSprites . append ( birdAtlas . textureNamed ( "player3" ) ) birdSprites . append ( birdAtlas . textureNamed ( "player4" ) )

Now, set up the bird’s initial position:

bird = SKSpriteNode(texture:birdSprites[0]) bird.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame)); bird.size.width = bird.size.width / 10 bird.size.height = bird.size.height / 10 1 2 3 4 bird = SKSpriteNode ( texture : birdSprites [ 0 ] ) bird . position = CGPoint ( x : CGRectGetMidX ( self . frame ) , y : CGRectGetMidY ( self . frame ) ) ; bird . size . width = bird . size . width / 10 bird . size . height = bird . size . height / 10

Define the bird’s animation, and repeat the animation forever:

let animateBird = SKAction.animateWithTextures(self.birdSprites, timePerFrame: 0.1) let repeatAction = SKAction.repeatActionForever(animateBird) self.bird.runAction(repeatAction) 1 2 3 let animateBird = SKAction . animateWithTextures ( self . birdSprites , timePerFrame : 0.1 ) let repeatAction = SKAction . repeatActionForever ( animateBird ) self . bird . runAction ( repeatAction )

Lastly, add the bird to the scene:

addChild(self.bird) 1 addChild ( self . bird )

Now, if all went well, we can see the bird flapping his wings in the Scene:

Add some pipes to the scene:

We’re going to add 4 pipes to our Scene.

Within the GameScene class, enter the following code:

var bottomPipe1 = SKSpriteNode() var bottomPipe2 = SKSpriteNode() var topPipe1 = SKSpriteNode() var topPipe2 = SKSpriteNode() var start = Bool(false) var birdIsActive = Bool(false) var pipeHeight = CGFloat(200) 1 2 3 4 5 6 7 var bottomPipe1 = SKSpriteNode ( ) var bottomPipe2 = SKSpriteNode ( ) var topPipe1 = SKSpriteNode ( ) var topPipe2 = SKSpriteNode ( ) var start = Bool ( false ) var birdIsActive = Bool ( false ) var pipeHeight = CGFloat ( 200 )

The second-last variable will be used to determine if our game has started.

The last variable will be used to set a random size for our pipe.

Set up the location and images of the pipes within the didMoveToView function:

bottomPipe1 = SKSpriteNode(imageNamed: "bottomPipe") bottomPipe2 = SKSpriteNode(imageNamed: "bottomPipe") topPipe1 = SKSpriteNode(imageNamed: "topPipe") topPipe2 = SKSpriteNode(imageNamed: "topPipe") bottomPipe1.position = CGPointMake(800, 200); bottomPipe1.size.height = bottomPipe1.size.height / 2 bottomPipe1.size.width = bottomPipe1.size.width / 2 bottomPipe2.position = CGPointMake(1600, 200); bottomPipe2.size.height = bottomPipe2.size.height / 2 bottomPipe2.size.width = bottomPipe2.size.width / 2 topPipe1.position = CGPointMake(800, 200 * 5); topPipe1.size.height = topPipe1.size.height / 2 topPipe1.size.width = topPipe1.size.width / 2 topPipe2.position = CGPointMake(1600, 200 * 5); topPipe2.size.height = topPipe2.size.height / 2 topPipe2.size.width = topPipe2.size.width / 2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 bottomPipe1 = SKSpriteNode ( imageNamed : "bottomPipe" ) bottomPipe2 = SKSpriteNode ( imageNamed : "bottomPipe" ) topPipe1 = SKSpriteNode ( imageNamed : "topPipe" ) topPipe2 = SKSpriteNode ( imageNamed : "topPipe" ) bottomPipe1 . position = CGPointMake ( 800 , 200 ) ; bottomPipe1 . size . height = bottomPipe1 . size . height / 2 bottomPipe1 . size . width = bottomPipe1 . size . width / 2 bottomPipe2 . position = CGPointMake ( 1600 , 200 ) ; bottomPipe2 . size . height = bottomPipe2 . size . height / 2 bottomPipe2 . size . width = bottomPipe2 . size . width / 2 topPipe1 . position = CGPointMake ( 800 , 200 * 5 ) ; topPipe1 . size . height = topPipe1 . size . height / 2 topPipe1 . size . width = topPipe1 . size . width / 2 topPipe2 . position = CGPointMake ( 1600 , 200 * 5 ) ; topPipe2 . size . height = topPipe2 . size . height / 2 topPipe2 . size . width = topPipe2 . size . width / 2

Lastly, add the pipes to the scene:

addChild(self.bottomPipe1) addChild(self.bottomPipe2) addChild(self.topPipe1) addChild(self.topPipe2) 1 2 3 4 addChild ( self . bottomPipe1 ) addChild ( self . bottomPipe2 ) addChild ( self . topPipe1 ) addChild ( self . topPipe2 )

We’ve now added pipes to the scene, however we won’t see them even though we’ve added the children nodes. This is because we haven’t started moving the pipes and they are theoretically on their way from the right-hand side of the screen.

Scroll the Scene

Let’s scroll the scene now. In the update function, add the following code:

override func update(currentTime: CFTimeInterval) { myFloor1.position = CGPointMake(myFloor1.position.x-4, myFloor1.position.y); myFloor2.position = CGPointMake(myFloor2.position.x-4, myFloor2.position.y); if (myFloor1.position.x < -myFloor1.size.width / 2){ myFloor1.position = CGPointMake(myFloor2.position.x + myFloor2.size.width, myFloor1.position.y); } if (myFloor2.position.x < -myFloor2.size.width / 2) { myFloor2.position = CGPointMake(myFloor1.position.x + myFloor1.size.width, myFloor2.position.y); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 override func update ( currentTime : CFTimeInterval ) { myFloor1 . position = CGPointMake ( myFloor1 . position . x - 4 , myFloor1 . position . y ) ; myFloor2 . position = CGPointMake ( myFloor2 . position . x - 4 , myFloor2 . position . y ) ; if ( myFloor1 . position . x < - myFloor1 . size . width / 2 ) { myFloor1 . position = CGPointMake ( myFloor2 . position . x + myFloor2 . size . width , myFloor1 . position . y ) ; } if ( myFloor2 . position . x < - myFloor2 . size . width / 2 ) { myFloor2 . position = CGPointMake ( myFloor1 . position . x + myFloor1 . size . width , myFloor2 . position . y ) ; } }

This will push the floor left continuously and re-draw the same floor on the right side to give the appearance of a seamless moving floor.

Now, to animate the pipes, enter the following code:

if (start) { bottomPipe1.position = CGPointMake(bottomPipe1.position.x-8, 200); bottomPipe2.position = CGPointMake(bottomPipe2.position.x-8, bottomPipe2.position.y); topPipe1.position = CGPointMake(topPipe1.position.x-8, 800); topPipe2.position = CGPointMake(topPipe2.position.x-8, 700); if (bottomPipe1.position.x < -bottomPipe1.size.width + 600 / 2){ bottomPipe1.position = CGPointMake(bottomPipe2.position.x + bottomPipe2.size.width * 4, pipeHeight); topPipe1.position = CGPointMake(topPipe2.position.x + topPipe2.size.width * 4, pipeHeight); } if (bottomPipe2.position.x < -bottomPipe2.size.width + 600 / 2) { bottomPipe2.position = CGPointMake(bottomPipe1.position.x + bottomPipe1.size.width * 4, pipeHeight); topPipe2.position = CGPointMake(topPipe1.position.x + topPipe1.size.width * 4, pipeHeight); } if (bottomPipe1.position.x < self.frame.width/2) { pipeHeight = randomBetweenNumbers(100, secondNum: 240) } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 if ( start ) { bottomPipe1 . position = CGPointMake ( bottomPipe1 . position . x - 8 , 200 ) ; bottomPipe2 . position = CGPointMake ( bottomPipe2 . position . x - 8 , bottomPipe2 . position . y ) ; topPipe1 . position = CGPointMake ( topPipe1 . position . x - 8 , 800 ) ; topPipe2 . position = CGPointMake ( topPipe2 . position . x - 8 , 700 ) ; if ( bottomPipe1 . position . x < - bottomPipe1 . size . width + 600 / 2 ) { bottomPipe1 . position = CGPointMake ( bottomPipe2 . position . x + bottomPipe2 . size . width * 4 , pipeHeight ) ; topPipe1 . position = CGPointMake ( topPipe2 . position . x + topPipe2 . size . width * 4 , pipeHeight ) ; } if ( bottomPipe2 . position . x < - bottomPipe2 . size . width + 600 / 2 ) { bottomPipe2 . position = CGPointMake ( bottomPipe1 . position . x + bottomPipe1 . size . width * 4 , pipeHeight ) ; topPipe2 . position = CGPointMake ( topPipe1 . position . x + topPipe1 . size . width * 4 , pipeHeight ) ; } if ( bottomPipe1 . position . x < self . frame . width / 2 ) { pipeHeight = randomBetweenNumbers ( 100 , secondNum : 240 ) } }

Create a new function, a random number generator, to make the pipes random sizes:

func randomBetweenNumbers(firstNum: CGFloat, secondNum: CGFloat) -> CGFloat{ return CGFloat(arc4random()) / CGFloat(UINT32_MAX) * abs(firstNum - secondNum) + min(firstNum, secondNum) } 1 2 3 4 5 func randomBetweenNumbers ( firstNum : CGFloat , secondNum : CGFloat ) -> CGFloat { return CGFloat ( arc4random ( ) ) / CGFloat ( UINT32_MAX ) * abs ( firstNum - secondNum ) + min ( firstNum , secondNum ) }

Now, in the touchesBegan function, tell the computer that the game has started. This will occur when you touch (or click) the screen.

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) { start = true } 1 2 3 override func touchesBegan ( touches : Set < UITouch > , withEvent event : UIEvent ? ) { start = true }

We now have a bird flying across the screen between pipes. It looks great, but now we need to add some challenge to the game. We’ll do this by applying physics to our nodes.

Apply Physics to the Scene:

In the GameScene class, we’re going to start by adding a category for our bitmask. This will allow us to differentiate between pipes and other objects in the scene:

let birdCategory:UInt32 = 0x1 << 0 let pipeCategory:UInt32 = 0x1 << 1 1 2 let birdCategory : UInt32 = 0x1 << 0 let pipeCategory : UInt32 = 0x1 << 1

In the didMoveToView class, create a physics body around the entire screen using the edgeLoopFromRect Also, create a contactDelegate for the world:

self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame) self.physicsWorld.contactDelegate = self 1 2 self . physicsBody = SKPhysicsBody ( edgeLoopFromRect : self . frame ) self . physicsWorld . contactDelegate = self

Now, add some bitmask objects to each pipe:

bottomPipe1.physicsBody?.categoryBitMask = pipeCategory bottomPipe1.physicsBody?.contactTestBitMask = birdCategory bottomPipe2.physicsBody?.categoryBitMask = pipeCategory bottomPipe2.physicsBody?.contactTestBitMask = birdCategory topPipe1.physicsBody?.categoryBitMask = pipeCategory topPipe1.physicsBody?.contactTestBitMask = birdCategory topPipe2.physicsBody?.categoryBitMask = pipeCategory topPipe2.physicsBody?.contactTestBitMask = birdCategory 1 2 3 4 5 6 7 8 9 10 11 bottomPipe1 . physicsBody ? . categoryBitMask = pipeCategory bottomPipe1 . physicsBody ? . contactTestBitMask = birdCategory bottomPipe2 . physicsBody ? . categoryBitMask = pipeCategory bottomPipe2 . physicsBody ? . contactTestBitMask = birdCategory topPipe1 . physicsBody ? . categoryBitMask = pipeCategory topPipe1 . physicsBody ? . contactTestBitMask = birdCategory topPipe2 . physicsBody ? . categoryBitMask = pipeCategory topPipe2 . physicsBody ? . contactTestBitMask = birdCategory

Now, create a physics body for the pipes and floor:

bottomPipe1.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: "bottomPipe"), size: self.bottomPipe1.size) bottomPipe2.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: "bottomPipe"), size: self.bottomPipe2.size) topPipe1.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: "topPipe"), size: self.topPipe1.size) topPipe2.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: "topPipe"), size: self.topPipe2.size) myFloor1.physicsBody = SKPhysicsBody(edgeLoopFromRect: myFloor1.frame) myFloor2.physicsBody = SKPhysicsBody(edgeLoopFromRect: myFloor1.frame) 1 2 3 4 5 6 7 8 9 10 11 bottomPipe1 . physicsBody = SKPhysicsBody ( texture : SKTexture ( imageNamed : "bottomPipe" ) , size : self . bottomPipe1 . size ) bottomPipe2 . physicsBody = SKPhysicsBody ( texture : SKTexture ( imageNamed : "bottomPipe" ) , size : self . bottomPipe2 . size ) topPipe1 . physicsBody = SKPhysicsBody ( texture : SKTexture ( imageNamed : "topPipe" ) , size : self . topPipe1 . size ) topPipe2 . physicsBody = SKPhysicsBody ( texture : SKTexture ( imageNamed : "topPipe" ) , size : self . topPipe2 . size ) myFloor1 . physicsBody = SKPhysicsBody ( edgeLoopFromRect : myFloor1 . frame ) myFloor2 . physicsBody = SKPhysicsBody ( edgeLoopFromRect : myFloor1 . frame )

Prevent the pipes from moving around on the screen by setting the physics dynamics to false:

bottomPipe1.physicsBody?.dynamic = false bottomPipe2.physicsBody?.dynamic = false topPipe1.physicsBody?.dynamic = false topPipe2.physicsBody?.dynamic = false 1 2 3 4 bottomPipe1 . physicsBody ? . dynamic = false bottomPipe2 . physicsBody ? . dynamic = false topPipe1 . physicsBody ? . dynamic = false topPipe2 . physicsBody ? . dynamic = false

Create a function to set up the bird’s physics by making a circular border around him:

func createBirdPhysics() { bird.physicsBody = SKPhysicsBody(circleOfRadius: CGFloat(self.bird.size.width / 2)) bird.physicsBody?.linearDamping = 1.1 bird.physicsBody?.restitution = 0 bird.physicsBody?.categoryBitMask = birdCategory bird.physicsBody?.contactTestBitMask = pipeCategory birdIsActive = true } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 func createBirdPhysics ( ) { bird . physicsBody = SKPhysicsBody ( circleOfRadius : CGFloat ( self . bird . size . width / 2 ) ) bird . physicsBody ? . linearDamping = 1.1 bird . physicsBody ? . restitution = 0 bird . physicsBody ? . categoryBitMask = birdCategory bird . physicsBody ? . contactTestBitMask = pipeCategory birdIsActive = true }

In the touchesBesgan() function, the following code will determine if the bird is active, and apply a force to give the appearance of jumping/flying:

if (birdIsActive) { self.bird.physicsBody!.applyImpulse(CGVectorMake(0, 150)) } else { createBirdPhysics() } 1 2 3 4 5 6 7 8 if ( birdIsActive ) { self . bird . physicsBody ! . applyImpulse ( CGVectorMake ( 0 , 150 ) ) } else { createBirdPhysics ( ) }

You can now run the game and see that the bird will hit the pipes and the floor. He might even fly off of the screen! Next, we’ll add some finishing touches to the game to make it more playable.

Finishing Touches:

In the update() function, we’ll add the following code to prevent the bird from moving off of the screen too far. Also, we’ll prevent him from rotating (unless this is a desired visual effect):

bird.position.x = self.frame.width / 2 bird.physicsBody?.allowsRotation = false 1 2 bird . position . x = self . frame . width / 2 bird . physicsBody ? . allowsRotation = false

Finally, we’ll add the following function to detect when the bird hits a pipe. At this point, you’ll see a message in XCode saying that the pipe was hit. Here, you could program a game over sequence, or a point system that will add a point every time a pipe is hit:

func didBeginContact(contact: SKPhysicsContact) { //GAMEOVER = TRUE print("BIRD HAS MADE CONTACT") } 1 2 3 4 func didBeginContact ( contact : SKPhysicsContact ) { //GAMEOVER = TRUE print ( "BIRD HAS MADE CONTACT" ) }

That’s it that’s all! If I missed anything or if the code can be improved, or heck if you just have a question feel free to post it in the comments.

Download the complete project here