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!
digitalbreed
I wrote a tutorial on the same topic. Major difference seems to be that I’m using the physics engine for contact detection / collision response and some more SKAction magic for the game logic. Feel free to have a look: http://digitalbreed.com/2014/how-to-build-a-game-like-flappy-bird-with-xcode-and-sprite-kit
samkeeneinteractivedesign@gmail.com
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!
LewsTherin
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?
samkeeneinteractivedesign@gmail.com
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:
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.
Syrinathos
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.
samkeeneinteractivedesign@gmail.com
Hahaha, yes if you don’t like the game this definitely isn’t going to sell it to you.
Angelo salibi
I want to make one please
Film Streaming
Thank you
reachlin
excellent tutorial, makes me want to write my own
wenyue
thanks
Rouquine Fille Facile
Bon je n’ai pas eu l’occasion de terminer de lire cependant je repasserai
dans la journée
jugar online flappy bird
It’s difficult to find educated people on this topic, however, you sound like you know what you’re talking about!
Thanks
Swamp Attack iOS Hack
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.
jungle treasure 2
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.
come fare soldi
Every weekend i used to pay a visit this site,
as i want enjoyment, since this this web page conations really pleasant funny material too.