GO TO

MakeGamesWithUs logo

Learn to make iPhone games!

Apply to our Summer Academy!

Only a few spots left!

Learn More

Build your own Flappy Bird with SpriteBuilder and Cocos2d 3.0

by benjaminencz 2 months, 1 week ago / 53 Points / 95 Comments

Only 2 Weeks left! Apply to our Summer Academy and ship your own iPhone game this summer!


Learn to build Flappy Fly!


This is the one and only tutorial that will teach you how to implement a native Flappy Bird clone for iOS. We will be using SpriteBuilder and Cocos2d 3.0 and walk you through all the steps starting with a blank project.

If you aren't familiar with SpriteBuilder you should consider starting with our beginner tutorial as we won't repeat all basic concepts of SpriteBuilder.

If you complete this tutorial you will learn how to:

  • implement an endless scroller
  • implement procedural level generation
  • use Coco2d 3.0 physics

The solution to this tutorial is available on GitHub:

Let's get started with Flappy Fly.

Getting Started

First of all let's check we are on the same page. This tutorial is written using SpriteBuilder 1.0.3. It is important that you use at least this version - as of this writing we are still using a pre-release version of Cocos2d 3.0 and using an older version of SpriteBuilder may lead to different results in some of the steps.

Check your version of SpriteBuilder:

As always the first step is to create a new SpriteBuilder project. Also download our art pack for this game. Add the art pack you just downloaded to your SpriteBuilder project by dragging the folder into the Resource Pane on the left:

Basic concepts of a side scroller in Cocos2d

If you have never built a side scroller before this introduction will help you understand some concepts. Most developers start developing a side scroller with a static hero and a level that scrolls towards this hero. However, for physics engines it is a lot easier if the hero moves through the level. We will implement the game as following:

  • The obstacles in the level are static
  • The fly moves to the right at constant speed
  • We implement a camera that follows the fly

This will make the fly appear at the same position during the complete game. If you can't follow don't worry, things will get easier once we put them into action.

Setup the Gameplay scene

Before we can start we need to change to project settings. Flappy Fly is a portrait mode game and the assets for the game are provided in 2x (iPhone retina resolution). Open the project settings and adjust these two settings:


Now publish your project and run it in Xcode! Your simulator should be displayed in portrait mode.

Adding Art

Time to add some art in SpriteBuilder. First, remove the label and the background image from the MainScene, leaving you with a blank screen.

Background Image

Now add the background image:

Set the reference corner to the top left (that means the position of the background will be defined starting in the top left corner). Set position to (0,0) and anchor point to (0,1). Now the background will stick at the top left corner, independent of the device size. This is important because we want to support 3.5-inch and 4 inch iPhones.
You can preview how your app will look on both of these devices using this setting:

Ground

The ground image has a bigger height than necessary - this way can adjust the space at the bottom of the screen to be bigger than we will make it in this tutorial. Add the ground image:

Set the reference corner to the left bottom. Set the position to (0, 12) and the anchor point to (0,0). This way the ground will stick at the left bottom, independent of the screen size.

Clouds

Add the clouds to the scene:

We want the clouds to be positioned from the top left corner, so set the reference corner to the top left. As position use (187,134) (or any other value you think looks good to provide you some sort of creative freedom ;) ).

Creating the fly

Now we're going to create new CCB-File for the fly and add an animation for the hero in our game.
Create a new Sprite CCB-File:

Set the sprite frame for the fly to fly1.png from the art pack:

Setting up the flying animation

If you have problems following the steps, you should take a look at the chapter in our beginner tutorial that explains timeline animations in detail. The animation we are about to define will be 1 second long and then loop. So in the first step we need to set the timeline duration to 1 second:

Now we are going to insert six Sprite Frame Keyframes in which we are going to switch between the two images fly1.png and fly2.png. This is how you insert a Sprite Frame Keyframe:

Note that the CCSprite needs to be selected in the timeline in order to add a Keyframe. Add 6 of these Sprite Frames and use the Sprite Frame property of the CCSprite to switch between the two different fly images. Also chain this timeline to itself, so that the animation is repeated infinitely. Once you are done, the result should look similar to this one:

Once again, if you had problems with one of these steps please check the beginner tutorial for detailed explanation.

Let the fly fall

Because our game uses physics we need to use a CCPhysicsNode. Open MainScene.ccb and drag a CCPhysicsNode below the root node, set the size of the CCPhysicsNode to be a 100% of the parents size:

Now make the ground a static physics body and add it to the CCPhysicsNode (remember? Every Node that has physics enabled needs to be below a CCPhysicsNode!):

Now drag the Hero.ccb file to this scene to add the hero to the gameplay. Make the hero a child of the CCPhysicsNode and make the hero a dynamic physics object:

Now, before we run the game and see the fly drop lets add the bush above the ground to complete the visual appeal of Flappy Fly:

Set the reference corner for the bush to bottom left, just as you did for the ground. Also make sure that all the decorative elements are placed above the CCPhysicsNode in the timeline. This will ensure that the hero will be drawn in front of the background images.

You are now again ready to publish your project and run the App from Xcode. You should see the fly slowly sailing down and coming to a rest on the ground:

Great! Now let's get to the scrolling part of our game!

Scroll the scene - move the fly

We are going to begin by moving the fly with a constant speed. First setup a code connection for the fly so that we can manipulate the velocity in code:

Now open Xcode, we are going to write some Objective-C code!

Replace the entire content of MainScene.m (except for the copyright header at the top) with this content:

#import "MainScene.h" static const CGFloat scrollSpeed = 80.f; @implementation MainScene { CCSprite *_hero; } - (void)update:(CCTime)delta { _hero.position = ccp(_hero.position.x + delta * scrollSpeed, _hero.position.y); } @end

We are creating a private instance variable for our code connection _hero. We also define a constant for the scroll speed because we will reference it from various points in code later. Finally we implement the update method (which is called every frame by Cocos2d) and use it to modify the x position of the fly. By multiplying the scroll speed with delta we ensure that the fly always moves at the same speed , independent of the frame rate. Disclaimer: setting the position manually in an update method is not the best use of a physics engine but this tutorial focuses on cloning flappy birds and not on physics best practices (stay tuned for such a tutorial in future).

Once you added this code you can run your App. You should see the fly falling down and slowly grinding over the floor until it leaves the right edge of the screen.

You're right; as a next step we need to set up a camera to follow the fly.

Setting up a "camera"

Cocos2d does not have the concept of a camera (though CCActionFollow comes close to it). This means we will implement the camera mechanism on our own. We do this by moving the complete content of the game to the left (to the player this looks the same as if the camera is moving to the right - in the end all movement is relative).

Just as in Flappy Bird the background images will be all static. The only things scrolling will be the obstacles and the ground. This means to implement the camera, we need to move the physicsNode to the left (obstacles, ground and hero are children of the physics node).

To scroll the physics node in code we need to setup a new code connection:

Now switch to Xcode and create a new private instance variable called _physicsNode for this code connection. Your private variables now should look like this:

@implementation MainScene { CCSprite *_hero; CCPhysicsNode *_physicsNode; }

Now we are going to add a second step to our update method that will move the physics node:

- (void)update:(CCTime)delta { _hero.position = ccp(_hero.position.x + delta * scrollSpeed, _hero.position.y); _physicsNode.position = ccp(_physicsNode.position.x - (scrollSpeed *delta), _physicsNode.position.y); }

The update method should now contain two lines; one to move the fly and the other to scroll the game.

Now you can run your game again.

Yay, the scrolling works! However, when we scroll too far to the right the ground disappears and the fly drops into nirvana. The problem is that by moving the complete physics world to the left, we are also moving our ground to the left that is only wide enough to fill the width of one screen.

Loop the ground

We will have to fix this by adding a second piece of ground and implement an endless scrolling of these two pieces. When a ground piece leaves the left edge we move it to the right edge of the screen to create a looping effect. Once again this will be easier to understand when we put it into practice.

The first step will be adding a second piece of ground in SpriteBuilder:

Drag the second piece of ground below the CCPhysicsNode. Set the position to (348,12). 348 is the width of the first piece of ground 12 is the y-Position of the first piece of ground. Also set the anchor point to (0 , 0.5), the same as the first piece. This way the two elements will line up nicely. If you want to be able to see how they line up you can select Document -> Stage Border -> None from the SpriteBuilder menu which will make all Nodes visible, even if they are outside the screen bounds.

Now there are a couple of important steps to follow:

  • Make the second piece of ground you just added a static physics body
  • Setup a code connection for the first piece of ground and name it _ground1
  • Setup a code connection for the second piece of ground and name it _ground2

If you followed all the steps closely you are now ready to open Xcode.

In code we will have to add code connections for both ground pieces. For easier use we will also add an array that contains both of these ground pieces. In the update method we will perform a check for each ground piece to see if it has moved off the screen. The content of MainScene.m should look like this:

#import "MainScene.h" static const CGFloat scrollSpeed = 80.f; @implementation MainScene { CCSprite *_hero; CCPhysicsNode *_physicsNode; CCNode *_ground1; CCNode *_ground2; NSArray *_grounds; } - (void)didLoadFromCCB { _grounds = @[_ground1, _ground2]; } - (void)update:(CCTime)delta { _hero.position = ccp(_hero.position.x + delta * scrollSpeed, _hero.position.y); _physicsNode.position = ccp(_physicsNode.position.x - (scrollSpeed *delta), _physicsNode.position.y); // loop the ground for (CCNode *ground in _grounds) { // get the world position of the ground CGPoint groundWorldPosition = [_physicsNode convertToWorldSpace:ground.position]; // get the screen position of the ground CGPoint groundScreenPosition = [self convertToNodeSpace:groundWorldPosition]; // if the left corner is one complete width off the screen, move it to the right if (groundScreenPosition.x <= (-1 * ground.contentSize.width)) { ground.position = ccp(ground.position.x + 2 * ground.contentSize.width, ground.position.y); } } }

In didLoadFromCCB we create an array to be able to loop through the ground pieces instead of duplicating the code. The exciting part happens in the update method. We retrieve the current screen position for each ground piece. Since the ground pieces aren't children of the MainScene (self) we need to first get the world position of the ground pieces and then use the convertToNodeSpace method to get the position in the MainScene (self). Once we have the position we check if any piece is off the screen. If that is the case we move it to the right of the second ground piece, this creates a looping effect.

If you run your game now, the ground will scroll endlessly!

Add controls and tune physics

At the moment our fly drops to the floor (slowly) and there is nothing a player could do against it! In this section we are going to tune some physics values and add controls so that the player can stop the fly from falling down.

First, let's increase the gravity to -700 to make the fly drop faster. You can do this in SpriteBuilder when the physics node is selected:

Now we are going to add touch handling in code. Open MainScene.m and add this line to didLoadFromCCB to enable touches in our game:

self.userInteractionEnabled = TRUE;

Then add a method to handle beginning touches:

- (void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event { [_hero.physicsBody applyImpulse:ccp(0, 400.f)]; }

Whenever a touch occurs we create an impulse that lifts the fly. For now it is the easiest to use the physics values we provide. Once you completed the tutorial you can spend time tweaking the values.

Now it's time to run the game again and see how the fly can be controlled by touches.

Limiting the speed

As you maybe have realized while testing the implementation: when you touch the screen often in a short time period the impulses add up and the fly shoots out of the top edge of the screen. As in most physics games we will have to add some tweaking variables. For this game we want to limit the vertical upward velocity. We can do this by extending the update method.

Add these lines to limit the velocity:

// clamp velocity float yVelocity = clampf(_hero.physicsBody.velocity.y, -1 * MAXFLOAT, 200.f); _hero.physicsBody.velocity = ccp(0, yVelocity);

Clamping means changing a given value so that it stays within a specified range. This way we are limiting the upwards speed to 200. By using negative MAXFLOAT as bottom border, we are not limiting the falling speed. We don't need to set the x velocity because we are setting the position manually.

Make the fly rotate

One of the nice details of Flappy Bird is the way the bird rotates. When the player does not touch the screen for a little while the bird turns towards the ground, touching the screen makes the bird turn upwards again. We are going to imitate this behaviour in Flappy Fly!

There are a couple of things we will need to do to achieve this:

  • On touch turn the fly upwards
  • If no touch occurred for a while, turn the fly downwards
  • Limit the rotation between slightly up and 90 degrees down (just as in Flappy Birds)

First step, let's add a private member variable to keep track of the time since the last touch (add this one to all the other private member variables):

NSTimeInterval _sinceTouch;

Next, extend the touch method to trigger the upward rotation on a touch. We will do this by applying an angular impulse. We also need to reset the _sinceTouch value every time a touch occurs:

- (void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event { [_hero.physicsBody applyImpulse:ccp(0, 400.f)]; [_hero.physicsBody applyAngularImpulse:10000.f]; _sinceTouch = 0.f; }

This is how your touchBegan method should look now. Applying a high angular impulse will lead to the bird turning upwards fast.

Finally we need to limit the rotation of the fly and start a downward rotation if no touch occurred in a while. We will do both in the update method. Add these lines to your update method:

_sinceTouch += delta; _hero.rotation = clampf(_hero.rotation, -30.f, 90.f); if (_hero.physicsBody.allowsRotation) { float angularVelocity = clampf(_hero.physicsBody.angularVelocity, -2.f, 1.f); _hero.physicsBody.angularVelocity = angularVelocity; } if ((_sinceTouch > 0.5f)) { [_hero.physicsBody applyAngularImpulse:-40000.f*delta]; }

There are a couple things going on here. First, we increase the _sinceTouch value to capture how much time has passed since the last touch. In the next line we limit the rotation of the fly.

Next, we check if the hero allows rotation (later we will disable rotation upon death). If rotation is allowed we clamp the angular velocity to make the rotation appear less hectic, then we apply that new velocity.

Finally we check if more than half a second passed since the last touch. If that is the case we apply a strong downward rotation impulse.

Now run your game again. The behaviour should be similar to this:

By now our game looks pretty decent! There is just one very important thing missing: obstacles!

Adding obstacles

Now we are getting to a very interesting part. We will be adding obstacles and implement a mechanism to randomly create more obstacles as we fly along the level.

Go back to SpriteBuilder and create a new CCB-File for the obstacles:

We will construct our obstacles with two pipes, one at the top and one at the bottom and CCNode in between which we will use as goal (the player will get a point when colliding with this goal).

Add pipe_top.png and pipe_bottom.png to your new Obstacle.ccb. Also add a CCNode between these two pipes. There are a lot of ways to set the positioning for this obstacle up, but it is easier if you follow my exact instructions.

The root node:

Content size is (80, 568). The anchor point is (0,0).

The top pipe:

The reference corner is top left. The anchor point is (0.5, 0). The x-position is 50% of the parent size.

The bottom pipe:

The reference corner is top left. The anchor point is (0.5, 1). The x-position is 50% of the parent size.

The exact goal node position is not that relevant. This is the one I used:

Most importantly it has to be stretched over both pipes.

It is important to get this positioning right, that will ensure that the obstacles look the same on an 3.5-inch and a 4-inch phone.

You can test if you setup the obstacle correct by adding one to the MainScene.ccb. The result should be similar to this one:

Once you have successfully tested that the obstacle looks correct, remove it from MainScene.ccb, as we will be adding obstacles from code.

Generate obstacles in code

Now we will put the obstacles we just created in our game. Open Xcode and add a member variable to MainScene.m:

NSMutableArray *_obstacles;

We will use this array to keep track of the obstacles we create.

Add to constants (below the scrolling speed constant we already have):

static const CGFloat firstObstaclePosition = 280.f; static const CGFloat distanceBetweenObstacles = 160.f;

These constants describe the x position of the first obstacle and the distance between two obstacles.

Now add the method that takes care of spawning obstacles:

- (void)spawnNewObstacle { CCNode *previousObstacle = [_obstacles lastObject]; CGFloat previousObstacleXPosition = previousObstacle.position.x; if (!previousObstacle) { // this is the first obstacle previousObstacleXPosition = firstObstaclePosition; } CCNode *obstacle = [CCBReader load:@"Obstacle"]; obstacle.position = ccp(previousObstacleXPosition + distanceBetweenObstacles, 0); [_physicsNode addChild:obstacle]; [_obstacles addObject:obstacle]; }

This method creates a new obstacle by loading it from the CCB-File and places it within the defined distance of the last existing obstacle. If now other obstacle exists it will position the obstacle at the firstObstaclePosition we just defined.

Now all we have to do is use this code from the didLoadFromCCB method, which is called as soon as our scene is initialized. Add this to didLoadFromCCB:

_obstacles = [NSMutableArray array]; [self spawnNewObstacle]; [self spawnNewObstacle]; [self spawnNewObstacle];

We first initialize the array for the obstacles and then spawn three of them. You are now once again ready to play your game!

You should see exactly three obstacles passing by, followed by - nothing.

This will be our next task spawning and endless amount of obstacles!

Spawning new obstacles when old ones leave the screen

We will now implement a mechanism that checks which obstacles moved off the screen and spawns new obstacles for these ones. Add these lines to your update method:

NSMutableArray *offScreenObstacles = nil; for (CCNode *obstacle in _obstacles) { CGPoint obstacleWorldPosition = [_physicsNode convertToWorldSpace:obstacle.position]; CGPoint obstacleScreenPosition = [self convertToNodeSpace:obstacleWorldPosition]; if (obstacleScreenPosition.x < -obstacle.contentSize.width) { if (!offScreenObstacles) { offScreenObstacles = [NSMutableArray array]; } [offScreenObstacles addObject:obstacle]; } } for (CCNode *obstacleToRemove in offScreenObstacles) { [obstacleToRemove removeFromParent]; [_obstacles removeObject:obstacleToRemove]; // for each removed obstacle, add a new one [self spawnNewObstacle]; }

The basics of this code will remind you of the ground looping we implemented previously. We check which obstacles are off the screen - we add these obstacles to a separate array (because we cannot remove objects from an array we are currently looping through). Then we iterate over the array of obstacles that need to be removed. We remove them from the scene, from the array of obstacles and spawn a new obstacle.

Now run your game. You should see an endless amount of scrolling obstacles! You are getting close to completing Flappy Fly!

Generate random obstacles

The next challenge we are going to tackle is generating random obstacles. This means we will be varying the position of the gap between the two pipes. The first step is setting up a custom class for the obstacle. Call the class Obstacle and be sure to have the root node selected when you set it:

Also set up a code connection for the top and the bottom pipe. You have done this many times before so we won't repeat. Call the connection _topPipe and _bottomPipe.

Once you have setup everything create a new class Obstacle in XCode. It should be a subclass of CCNode:

I will give you the complete content of Obstacle.m at once. Add it to your file, read it and then we will discuss it:

#import "Obstacle.h" @implementation Obstacle { CCNode *_topPipe; CCNode *_bottomPipe; } #define ARC4RANDOM_MAX 0x100000000 // visibility on a 3,5-inch iPhone ends a 88 points and we want some meat static const CGFloat minimumYPositionTopPipe = 128.f; // visibility ends at 480 and we want some meat static const CGFloat maximumYPositionBottomPipe = 440.f; // distance between top and bottom pipe static const CGFloat pipeDistance = 142.f; // calculate the end of the range of top pipe static const CGFloat maximumYPositionTopPipe = maximumYPositionBottomPipe - pipeDistance; - (void)setupRandomPosition { // value between 0.f and 1.f CGFloat random = ((double)arc4random() / ARC4RANDOM_MAX); CGFloat range = maximumYPositionTopPipe - minimumYPositionTopPipe; _topPipe.position = ccp(_topPipe.position.x, minimumYPositionTopPipe + (random * range)); _bottomPipe.position = ccp(_bottomPipe.position.x, _topPipe.position.y + pipeDistance); } @end

First, let's discuss what happens on a high level. At the top of the file we define a couple of constants. These constants describe the minimum and maximum positions for the top and bottom pipes. I have chosen these values in way that every pipe at the top reaches at least 30 points into the screen for a 3.5-inch iPhone. This means on an iPhone 4S or older the top obstacle will always be clearly visible. The iPhone 5 and 5s will see a larger portion of the top pipe, but this will not change anything about the gameplay. The maximum value for the bottom pipe is defined such that a pipe always sticks at least 30 points out of the ground to make it clearly visible.

Additionally we define a pipeDistance that describes how large the gap between the pipes should be.

Now we implement a new method setupRandomPosition that uses the defined border values to calculate a range. Then it generates a random number between 0 and 1 to define which portion of the possible range will be used. It uses the result to position the top pipe and positions the bottom pipe in the defined distance.

Now let's use this method to generate random obstacles in our game.

Add the necessary method declaration to Obstacle.h:

#import "CCNode.h" @interface Obstacle : CCNode - (void)setupRandomPosition; @end

Now we can use the method from MainScene.m. First add a import statement to the top of MainScene.m:

#import "Obstacle.h"

Now we need to change the code that we use to load an Obstacle.ccb:

- (void)spawnNewObstacle { CCNode *previousObstacle = [_obstacles lastObject]; CGFloat previousObstacleXPosition = previousObstacle.position.x; if (!previousObstacle) { // this is the first obstacle previousObstacleXPosition = firstObstaclePosition; } Obstacle *obstacle = (Obstacle *)[CCBReader load:@"Obstacle"]; obstacle.position = ccp(previousObstacleXPosition + distanceBetweenObstacles, 0); [obstacle setupRandomPosition]; [_physicsNode addChild:obstacle]; [_obstacles addObject:obstacle]; }

In the above code I have added a cast, so that we can treat the loaded Obstacle.ccb as an instance Obstacle. Additionally I added a line to call the new setupRandomPosition method we just implemented.

If you run the game now you should see random obstacles occurring! Before we move on to the last major step - implementing collisions - we are going to fix a small issue with the drawing order.

Fixing the drawing order

Because we add the pipes in code they are drawn in front of the ground. By default Cocos2d renders the elements in the reverse order of drawing. We are going to change the draw order manually, forcing the ground to be drawn above the pipes. Add this enum after the constants you have defined in MainScene.m:

typedef NS_ENUM(NSInteger, DrawingOrder) { DrawingOrderPipes, DrawingOrderGround, DrawingOrdeHero };

If you are interested in details, read more about the approach we are using here. Next we are going to apply these drawing order values.

In didLoadFromCCB set the zOrder for grounds and the hero by adding:

for (CCNode *ground in _grounds) { ground.zOrder = DrawingOrderGround; } _hero.zOrder = DrawingOrdeHero;

When we spawn new obstacles set their zOrder. Add this line to spawnNewObstacle:

obstacle.zOrder = DrawingOrderPipes;

Now you should run the App and see the pipes drawn behind the ground:

The final steps: setting up collisons

First of all: Congratulations for following along this far. Now we are going to set up collision handling so that we can add a portion of frustration to Flappy Fly.

First open Obstacle.m. and set the collision type:

- (void)didLoadFromCCB { _topPipe.physicsBody.collisionType = @"level"; _topPipe.physicsBody.sensor = TRUE; _bottomPipe.physicsBody.collisionType = @"level"; _bottomPipe.physicsBody.sensor = TRUE; }

We set the collisionType to "level", we will use this collision type for all deadly objects (ground and obstacles). Setting the sensor value to TRUE tells Chipmunk that the collision handler shall be called upon a collision with this type of object - but that no actual collision shall be performed. This implementation is the same as in Flappy Bird where the bird never actually collides with a pipe but instead drops dead immediately.

In MainScene.h define that we are implementing CCPhysicsCollisionDelegate:

#import "CCNode.h" @interface MainScene : CCNode <CCPhysicsCollisionDelegate> @end

This delegate will be called whenever two objects collide.

In MainScene.m we need to set the collision type for the hero and the grounds. Change the didLoadFromCCB to look as following:

- (void)didLoadFromCCB { self.userInteractionEnabled = TRUE; _grounds = @[_ground1, _ground2]; for (CCNode *ground in _grounds) { // set collision txpe ground.physicsBody.collisionType = @"level"; ground.zOrder = DrawingOrderGround; } // set this class as delegate _physicsNode.collisionDelegate = self; // set collision txpe _hero.physicsBody.collisionType = @"hero"; _hero.zOrder = DrawingOrdeHero; _obstacles = [NSMutableArray array]; [self spawnNewObstacle]; [self spawnNewObstacle]; [self spawnNewObstacle]; }

We have added a collision type for the hero and the ground and did set this object as collision delegate of the physics node.

As a next step open SpriteBuilder again and enable physics for both pipes, turning them into static physics bodies:

Last, we need to implement a collision handler method. As parameter names we have to use the collision types that we defined earlier. Add this method to MainScene.ccb:

-(BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair hero:(CCNode *)hero level:(CCNode *)level { NSLog(@"Game Over"); return TRUE; }

The method above will be called whenever a object with type hero collides with an object of type level.

Publish & Run in Xcode. Now any time you collide with the ground or a pipe "Game Over" will be printed to the console.

Implement "Game over" situation

Instead of only showing a message in the console we now want implement a game over situation:

  • Fly falls to ground
  • Screen rumbles
  • Restart button appears
  • Game restarts when restart button is pressed

First lets add a game over button in SpriteBuilder:

Center the button. Set a code connection up: _restartButton and a selector that shall be called when the button is pressed: restart.

Now set the button to be invisible:

We will make the button visible once the game over situation occurs. Now switch to MainScene.m and add this instance variable:

CCButton *_restartButton;
Next, extend the collision handling method to show this restart button:
-(BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair hero:(CCNode *)hero level:(CCNode *)level { NSLog(@"Game Over"); _restartButton.visible = TRUE; return TRUE; }
Finally implement a restart method that will be called once the restart button is pressed:
- (void)restart { CCScene *scene = [CCBReader loadAsScene:@"MainScene"]; [[CCDirector sharedDirector] replaceScene:scene]; }

This method will reload the entire scene - the complete game will restart. Now you can test this new functionality!

You will see that restarting the game works, but we don't have a real "game over" state yet. The scrolling goes on and we don't have a real visualization of a game over situation.

Now we are going to add a _gameOver flag and a gameOver method. We are also replacing the scrollSpeed constant with a _scrollSpeed variable. Add these instance variables:

BOOL _gameOver; CGFloat _scrollSpeed;

Initialize the new _scrollSpeed variable in didLoadFromCCB:

_scrollSpeed = 80.f;

Replace the two occurrences of scrollSpeed with _scrollSpeed.

Now add the new gameOver method to MainScene.m:

- (void)gameOver { if (!_gameOver) { _scrollSpeed = 0.f; _gameOver = TRUE; _restartButton.visible = TRUE; _hero.rotation = 90.f; _hero.physicsBody.allowsRotation = FALSE; [_hero stopAllActions]; CCActionMoveBy *moveBy = [CCActionMoveBy actionWithDuration:0.2f position:ccp(-2, 2)]; CCActionInterval *reverseMovement = [moveBy reverse]; CCActionSequence *shakeSequence = [CCActionSequence actionWithArray:@[moveBy, reverseMovement]]; CCActionEaseBounce *bounce = [CCActionEaseBounce actionWithAction:shakeSequence]; [self runAction:bounce]; } }
And call this new method from our collision handler:
-(BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair hero:(CCNode *)hero level:(CCNode *)level { [self gameOver]; return TRUE; }
You also need to update the touchBegan method to ensure that the user cannot "jump" when the hero is dead:
- (void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event { if (!_gameOver) { [_hero.physicsBody applyImpulse:ccp(0, 400.f)]; [_hero.physicsBody applyAngularImpulse:10000.f]; _sinceTouch = 0.f; } }

Now you can run your app again and you should see a complete game over sequence. Now there is literally one last point left: implementing points!

Implementing points

Now that the player can die, lets implement the very last step and let players collect points.

First create a LabelTTF in MainScene.ccb to display the current score:

Define a code connection with the variable _scoreLabel.

Now open Obstacle.ccb to make the CCNode between the pipes a static physics body and also set a custom class called Goal:

We will now switch to Xcode to implement a score increase once the player hits one of these goals.

In Xcode create a new Goal class:

Add this method to Goal.m:

- (void)didLoadFromCCB { self.physicsBody.collisionType = @"goal"; self.physicsBody.sensor = TRUE; }

We will use the collision type in MainScene.m to detect when a player passed through a pipe.

Open MainScene.m and create a new member variable for the score label and the score:

NSInteger _points; CCLabelTTF *_scoreLabel;
Now, as a very final step, implement a second collision handler that will be called when the player reaches a goal. Add this method to MainScene.m:
-(BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair hero:(CCNode *)hero goal:(CCNode *)goal { [goal removeFromParent]; _points++; _scoreLabel.string = [NSString stringWithFormat:@"%d", _points]; return TRUE; }

Congratulations! Now you should see the complete game previewed at the beginning of this tutorial. You should have learned a lot along the way.

If you enjoyed this you should apply to our Summer Academy and ship your own iPhone game this summer!

[email protected]

aros_lins 5 days, 11 hours ago
paul_hewson 1 week ago

Thanks for the lesson, one question and I notice no other website or tutorial mentions this; but what about sound effects? what codes do we use for " flapping wings", or " collision ", etc? Even annoying background music ? Thanks

Reply
tom_slocombe 2 weeks, 2 days ago

Hi thanks for the awesome tutorial - it worked first time for me! I would like to ask (I know you say there is a tutorial coming soon but I am impatient!) - you say it is bad practice to set the position of a sprite manually in the update method if it has physics enabled - can I ask why, and what is the better solution (is it ok to move them from the update method using CCActions?). I am making a Space Invaders type game and am moving the aliens from the update method by setting their position manually - it is not giving me any problems but if there is a better way I would love to know!
Many thanks!!

Reply
maciek_maciex 2 weeks, 4 days ago

Hey

I just started creating this funny game, but at the beggining I've got trobule with:

http://www19.zippyshare.com/v/83476341/file.html

I'm stuck at "Scroll the scene - move the fly"

Could you help me?

Reply
m7mdabarakat 1 week, 2 days ago

just remove the underscore from the _scrollspeed and you will be fine.

benjaminencz 1 week, 1 day ago

Thanks for pointing this out. We have fixed the error.

micheal_javis 1 month ago

When update the _physicsNode ,there is a crack between the _ground1 and _ground2 .
But when i change the method ,use _physicsNode.position = ccp(_physicsNode.position.x - 1, _physicsNode.position.y) ,it will be Ok.

and also when use
CCAction *_follow= [CCActionFollow actionWithTarget:_hero];
[_physicsNode runAction:_follow];

it also has a crack



can u tell me why?

Reply
joao_abrantes 1 week, 3 days ago

I also have this crack. Did you find out why it happens?

joshua_gare 1 month ago *

Since the update of SpriteBuilder to version 1.0.4 thus implementing the new version of cocos2d the hero has issues with the x direction. Everytime you press the screen the hero now moves to the left until it disappears off the screen. I'm not sure why this is and can't seem to fix it.

Is anyone else experiencing this and do you know how to fix it?

Thanks

EDIT: I have messed around with it and I have found what is causing the problem:

[_hero.physicsBody applyAngularImpulse:10000.f];

Whenever applyAngularImpulse is called the birds trajectory is changed. Commenting out the lines fixes the problem but stops the bird from rotating when you press the screen and when you fail to press the screen for a while.

Reply
sonbkap 1 month, 1 week ago

thanks

Reply
cam_young 1 month, 1 week ago

I have added a start scene to this game and changed the default load screen in the App Delegate to the start scene instead of the "main scene". When I switch from my start scene to the main scene the fly disappears. Is there anything in the code that requires the "main scene" to be the starting scene? Is there anyway I can load the "main scene" from my start scene without having the fly disappear? Any help would be greatly appreciated.

Reply
mardafakhrurrazi 1 month, 1 week ago

Interested. How I can add scoreboard with medal similar flappy bird and integrated with leaderboard from google?

Reply
j_cov 1 month, 1 week ago

Hi
I download from github but the SpriteBuilder project file seems to be Windows. Possible to get Mac SpriteBuilder version of project so I can open and follow along with your tutorial?
Thanks!

Reply
cam_young 1 month, 1 week ago

SpriteBuilder is available on the Mac App Store accessible from your Apple computer

ben_coombes 1 month, 1 week ago

Hi guys. Really stuck with implementing the collision handler method. I cannot work out where to insert this piece of code...

-(BOOL)ccPhysicsCollisionBegin:(CCPhysicsCollisionPair *)pair hero:(CCNode *)hero level:(CCNode *)level {
NSLog(@"Game Over");
return TRUE;

}

Any help would be gratefully received, thank you!

Reply
minhviet93 1 month, 1 week ago *

Just google a bit and found out : http://www.cocos2d-iphone.org/forums/topic/xcode5-beta5-errors/
the collision delegate setter uses objective c runtime functions to check whether or not the implemented selector is returning the correct data type.

following that, it seems that the return type check value has changed from previously lowercase ‘c’ to a capital ‘B’ in this version of sdk, certainly inconvenient but not a devastating problem.

to remedy this, a case checking for the changed return type value should be added to the following assertion to ensure the same function, from CCPhysicsNode.m starting from line 349~

NSAssert(strcmp(returnType, "c") == 0 || strcmp(returnType, "B") == 0, @"CCPhysicsCollisionBegin delegate methods must return a BOOL.");
and similarly to the line 352~

NSAssert(strcmp(returnType, "c") == 0 || strcmp(returnType, "B") == 0, @"CCPhysicsCollisionPreSolve delegate methods must return a BOOL.");

ty_harrison 1 month, 1 week ago

Hey guys,

Great tutorial! Completed the whole thing and on to developing my own game. I am trying to create a game where objects are continuously falling on a non-scrolling screen. I got the objects to fall using a move object timer and set up the NSMutable array just fine. But I cannot get the array to repeat and spawn an endless amount of objects! Please Help!



- (void) update:(CCTime)delta {
NSMutableArray *offScreenObstacles = nil;
for (CCNode *obstacle in _obstacles) {
CGPoint obstacleWorldPosition = [_physicsNode convertToWorldSpace:obstacle.position];
CGPoint obstacleScreenPosition = [self convertToNodeSpace:obstacleWorldPosition];
if (obstacleScreenPosition.y <= -obstacle.contentSize.height) {
if (!offScreenObstacles) {
offScreenObstacles = [NSMutableArray array];

}
[offScreenObstacles addObject:obstacle];
}
}
for (CCNode *obstacleToRemove in offScreenObstacles) {
[obstacleToRemove removeFromParent];
[_obstacles removeObject:obstacleToRemove];
//for each removed obstacle, add a new one
[self spawnNewObstacle];

}
}


Reply
luke_sadler 1 month, 1 week ago

Hi. Maybe try something like this
if (obstacleScreenPosition.y < -obstacle.contentSize.height) {
obstacle.position = ccp(insert code to make x random between 20 and 300?, _physicsNode.contentSize.height +50 );


I've not tested this, but it should give you a starting point. Try and see if you can experiment by commenting out the code for adding objects to an off-screen array, then copy over the code that you is used for looping the ground, then use the above code to have them respawn on top of the screen. With enough playing around, it should work.
Luke

luke_sadler 1 month, 1 week ago

Thanks a lot for the tutorial. I used this template to create my game, which has just become available on the app store for free. It's called 'Final Flight', so go check that out to see what can be done with this.
I've added difficulty levels, high scores, game centre, achievements, music, sound effects, particle effects, menus, level ups, out of bound limiters and the background that moves at a different speed to give perspective. I tweaked the pipe heights so it works on both iPhone and iPad. I also tweaked the impulse/ gravity / rotate values to give it a more natural/ realistic feel.

I'm really quite proud of it, so please go check it out. It's free, fun, interesting (from a programming POV) and pretty rewarding. If anyone needs help on some of the features, then follow me on twitter @mylogon_ (don't forget the underscore) and for extended chats add me on Skype on mylogon341.

Enjoy!

Reply
chrisgonzalez7923 1 month, 1 week ago

Hello! I am currently working on this tutorial, but I decided to challenge myself by making the game a vertical scroller instead of a horizontal scroller....Currently im stuck. Im trying to make the ground loop infinitely, vertically but i just cant get it to work for (CCNode *ground in _grounds) {
// get the world position of the ground
CGPoint groundWorldPosition = [_physicsNode convertToWorldSpace:ground.position];
// get the screen position of the ground
CGPoint groundScreenPosition = [self convertToNodeSpace:groundWorldPosition];
// if the left corner is one complete width off the screen, move it to the right
if (groundScreenPosition.y <(-1 * ground.contentSize.height)) {
ground.position = ccp(ground.position.x , ground.position.y + 2 * ground.contentSize.height);
}


I rearrange the code to try and get it to work vertically instead of horizontally but it doesnt seem to be working. Is there any way to get this to work?

Reply
luke_sadler 1 month, 1 week ago

I think you need to change this line
_physicsNode.position = ccp(_physicsNode.position.x - (_scrollSpeed *delta), _physicsNode.position.y);

Maybe to something like this

_physicsNode.position = ccp(_physicsNode.position.x, _physicsNode.position.y - (_scrollSpeed *delta));


Hope this helps :)

Luke

chrisgonzalez7923 1 month, 1 week ago

Hello! I am currently working on this tutorial, but I decided to challenge myself by making the game a vertical scroller instead of a horizontal scroller....Currently im stuck. Im trying to make the ground loop infinitely, vertically but i just cant get it to work for (CCNode *ground in _grounds) {
// get the world position of the ground
CGPoint groundWorldPosition = [_physicsNode convertToWorldSpace:ground.position];
// get the screen position of the ground
CGPoint groundScreenPosition = [self convertToNodeSpace:groundWorldPosition];
// if the left corner is one complete width off the screen, move it to the right
if (groundScreenPosition.y <(-1 * ground.contentSize.height)) {
ground.position = ccp(ground.position.x , ground.position.y + 2 * ground.contentSize.height);
}


I rearrange the code to try and get it to work vertically instead of horizontally but it doesnt seem to be working. Is there any way to get this to work?

Reply
judemurphy 1 month, 2 weeks ago

Question, is it possible I could use the clouds from this game in my app, or are the images under some sort of license that states they aren't allowed to be reused.

Reply
benjaminencz 1 month, 1 week ago

The art is part of our free art library that will be available soon - you can use it in your app.

perry_nally 1 month, 2 weeks ago

I'm attempting to move the obstacles 2 times faster than the ground. Here is my sample code:

/***************************************
* SPAWN new OBSTACLES when old ones
* leave the playing area.
* SPEED - make obstacles move 2x speed
* of scroll speed.
***************************************/
NSMutableArray *offScreenObstacles = nil;
for (CCNode *obstacle in _obstacles) {


// Move the obstacle 2X faster! -- NOT WORKING visually, but it seems to be working in the calculation
obstacle.position = ccp(obstacle.position.x - ( scrollSpeed * 2 * delta), obstacle.position.y);


CGPoint obstacleWorldPosition = [_physicsNode convertToWorldSpace:obstacle.position];
CGPoint obstacleScreenPosition = [self convertToNodeSpace:obstacleWorldPosition];

// NSString *contentWidth = [NSString stringWithFormat:@"obstacle width: %f",-obstacle.contentSize.width];
// CCLOG(contentWidth);

if (obstacleScreenPosition.x < -obstacle.contentSize.width) {
CCLOG(@"Destroy Object!");
if (!offScreenObstacles) {
offScreenObstacles = [NSMutableArray array];
}
[offScreenObstacles addObject:obstacle];
//NSString *newPos = [NSString stringWithFormat:@"object position x1: %f",obstacleScreenPosition.x];
//CCLOG(newPos);
}
else
{
//NSString *xPosition2 = [NSString stringWithFormat:@"object position x2: %f",obstacleScreenPosition.x];
//CCLOG(xPosition2);
}
}
for (CCNode *obstacleToRemove in offScreenObstacles) {
CCLOG(@"Remove Object!");
[obstacleToRemove removeFromParent];
// for each removed obstacle, add a new one
[self spawnNewObstacle];
// remove the object after spawning a new one, since if there is only one object this logic will not work
[_obstacles removeObject:obstacleToRemove];
}


notice that I'm setting the "obstacle.position" right near the start of my sample. This does actually move the obstacle.position twice as fast, but the on screen image of the obstacle does not move faster. It stays the same and moves with the ground. Spawning new ones (moving the obstacles works fine) and in fact when running the code above where I set the speed to be 2x faster, the obstacle gets moved off the screen about half way through the screen. So I know some position of the obstacle is moved, but the image does not seem the get moved. any help would be greatly appreciated. Thank in advance!
Perry

Reply
perry_nally 1 month, 2 weeks ago

Just wanted to follow up with everyone so they know the solution to this issue. I found that I was creating a new node, then dragging a sprite to the node, then enabling physics on the sprite. The node was actually moving correctly, but the sprite had physics, so it would interact with the physics world and would not be affected by the node.position updates. So I removed physics on the sprite, and selected the node in the timeline and added physics on that. This corrected everything. The node then had the physics which has the attached sprite. the node would move based on my custom positioning and the physics calculations which dragged the attached sprite with it.

This sort of thing has potential to be complex if you are adding building an object out of other nodes. You need to add the physics to the root node in the timeline, not the sprites. If you need to have a single node that has collision detection for multiple sprites separately, then you'll need to create the separate nodes, add the sprites, add physics to the root nodes, then create another parent node, and add all those nodes to it. You'll then be able to animate the parent node and all the child nodes will have physics attached. The only catch is that you'll then need to add joints and figure out if the joints should move or not. That way you'll get collision detection for an object that has concave surfaces. I hope I'm making sense.

alessiopizziol 1 month, 2 weeks ago

I Have a problem with undeclared "delta" anyone can help me?

Reply
olle_svensson 1 month, 3 weeks ago *

Is it possible to make a main screen? You know where you have your start button. Then is it possible to also have so u are just looping and it says "Click to play" or something

EDIT: Figured it out :)

Reply
james_cusack 1 month, 2 weeks ago

can you tell us how?

dtaquiri 1 month, 3 weeks ago

Please help me

When i insert this code

if (groundScreenPosition.x <= (-1 * ground.contentSize.width)) {
ground.position = ccp(ground.position.x + 2 * ground.contentSize.width, ground.position.y);

<= means this " & l t
this error appears: use of undeclared identifier "lt"

Reply
chriskaramatic 1 month, 3 weeks ago

- (void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event

Reply
dtaquiri 1 month, 3 weeks ago

This step is impossible, mi hero doesnt appear in any of the position , please help me

Now drag the Hero.ccb file to this scene to add the hero to the gameplay. Make the hero a child of the CCPhysicsNode and make the hero a dynamic physics object:

Reply
dtaquiri 1 month, 3 weeks ago

This step is impossible, mi hero doesnt appear in any of the position , please help me

Now drag the Hero.ccb file to this scene to add the hero to the gameplay. Make the hero a child of the CCPhysicsNode and make the hero a dynamic physics object:

Reply
dtaquiri 1 month, 3 weeks ago

This step is impossible, mi hero doesnt appear in any of the position , please help me

Now drag the Hero.ccb file to this scene to add the hero to the gameplay. Make the hero a child of the CCPhysicsNode and make the hero a dynamic physics object:

Reply
chriskaramatic 1 month, 3 weeks ago *

use of undeclared identifier 'lt' and expected expression
if (groundScreenPosition.x <= (-1 * ground.contentSize.width)) {

somebody help me please

Reply
timothy_allard 1 month, 3 weeks ago

What would the pipe values be to also work on an ipad? Thank you!!

Reply
dennis_pascual 1 month, 3 weeks ago

Hello! i have one error from class spawnNewObstacle and keep telling me thread 1 error
-(void)spawnNewObstacle
{
CCNode *previousObstacle = [_obstacles lastObject];
CGFloat previousObstacleXPosition = previousObstacle.position.x;
if(!previousObstacle)
{
//1st obstacle
previousObstacleXPosition = firstObstaclePostion;
}

Obstacle *obstacle = (Obstacle *)[CCBReader load:@"Obstacle"];
obstacle.position =ccp(previousObstacleXPosition + distanceBetweenObstacles, 0);
[obstacle setupRandomPosition];
[_physicsNode addChild:obstacle];
[_obstacles addObject:obstacle];
obstacle.zOrder = DrawingOrderPipes;

}
anyone can help me? thanks by the way this tutorial is great

Reply
kirit_modi 1 month, 3 weeks ago

hello i am set variable _topPipe for TopPipe and _bottomPipe for BottomPipe althought i have error and not got Pipes only one time pipe is disply. please help me about it.

CCBReader: Couldn't find member variable: _topPipe
CCBReader: Couldn't find member variable: _bottomPipe

Reply
matt_garrett 1 month, 4 weeks ago

Hi. Thanks for the great guide.

Everything has worked perfectly up until the very end when it comes time to implement points. Anytime I enable physics on the CCNode in Obstacles.cbb the app builds but when it runs I get a libc++abi.dylib: terminating with uncaught exception of type NSException error. I have double checked my connections and code and all seems right but I have obviously missed something somewhere. Any ideas?

Reply
andrey_safronov 1 month, 4 weeks ago

Hi!

Is it possible to change animation speed of the fly? I mean - if I make frame based timeline in cocosbuilder - can I change its speed in my code in realtime?

Reply
michael_petty 1 month, 4 weeks ago

I'll attempt to answer this question... if you're talking about the animation of the flys wings batting, inside SpriteBuilder you can make the animation keyframes closer together (if possible). If it's not you could try setting the loop speed to a shorter time maybe.

andrey_safronov 1 month, 3 weeks ago

I know how to create and edit timelines in spritebuilder. I created a timeline based on sprite frame animation. It is rotating coin. Now I want to load it from my code and add several rotating coins on a layer. But it looks terrible if all coins rotating with same speed...

michael_petty 1 month, 4 weeks ago *

I'm working on getting the pipes to randomly generate. In my main.m file it gives me a thred 1 signal SIGABORT error. I traced this down a bit further once before to and error on the line about [obstacle setupRandomObstacle]; I have - (void)setupRandomObstacle listed in the obstacle.h and all the needed code correctly in the obstacle.m and mainscene.m

- (void)spawnNewObstacle {
CCNode *previousObstacle = [_obstacles lastObject];
CGFloat previousObstacleXPosition = previousObstacle.position.x;

if (!previousObstacle) {
// this is the first obstacle
previousObstacleXPosition = firstObstaclePosition;
}

Obstacle *obstacle = (Obstacle *)[CCBReader load:@"Obstacle"];
obstacle.position = ccp(previousObstacleXPosition + distanceBetweenObstacles, 0);
[obstacle setupRandomPosition];
[_physicsNode addChild:obstacle];
[_obstacles addObject:obstacle];

}

Anyone know what may be causing this?

Reply
rickyhat 1 month, 4 weeks ago

I'm having an issue with the spawnNewObstacle method..... it keeps telling me use of undeclared indetifier 'spawnNewObstacle"

here's my method... directly from the tutorial

- (void)spawnNewObstacle {
CCNode *previousObstacle = [_obstacles lastObject];
CGFloat previousObstacleXPosition = previousObstacle.position.x;
if (!previousObstacle) {
// this is the first obstacle
previousObstacleXPosition = firstObstaclePosition;
}
CCNode *obstacle = [CCBReader load:@'Obstacle'];
obstacle.position = ccp(previousObstacleXPosition + distanceBetweenObstacles, 0);
[_physicsNode addChild:obstacle];
[_obstacles addObject:obstacle];

}

Reply
timothy_allard 1 month, 4 weeks ago *

Hello! First off... thank you for this amazing tutorial. I have learned so much from this.
I have a question.. I am trying to implement a new object.. Lets say a monster.

I can successfully generate a new instance but it falls right off the screen... Literally, I see it for a second,...then it falls behind the ground and into the bottomless pit. I am also trying to move it along the ground without success with physics but it is not quite working..

Any pointers?

Thank you!

Reply
timothy_allard 1 month, 4 weeks ago

Hmm... doing this fixed it: _monster.physicsBody.sensor = FALSE;
Now on to the x-axis movement!

james_cusack 2 months ago

Making highscore????????????????

Reply
james_cusack 2 months ago

Does anyone know how to make it so it can save your highscore?

Reply
james_cusack 2 months ago

I would really appreciate it if I can get an answer fast. Thanks.

andy_triboletti 2 months ago

Does anyone know of a way to have the hero image be downloaded from the internet and configurable at runtime?

Reply
r_b 2 months ago *

How do you make the fly gain the same amount of vertical elevation with each jump? I noticed that if the fly is falling for a long period of time, a jump will not make it gain much elevation.

Thanks!

Reply
ryan_kilmurray 2 months ago

Thanks for a great tutorial. Just wondered though, I'm having issues with the Goal CCNodes. When I enable static physics on them, my Hero collide physically with them. How do I make it so that my Hero flies straight through the Goals? I might have missed it in the tutorial, but I can't seem to get it.

Thanks in advance.

Reply
ryan_kilmurray 2 months ago

Got it. Just as a tip to anyone else who's messed it up like I have, I didn't realise you'd made an additional collision detector.

Thanks again for a great tutorial.

andy_triboletti 2 months ago *

Has anyone implemented ads using this code? I implemented Revmob interstitials and Admob banner ads inside my game. Everything works fine if the user dismisses the ad. However, if the user clicks the interstitial or banner ad, once they come back to the game, the game is unplayable.

When you come back from clicking the ad, the character isn't moving across the screen. When you click the screen, the sound effect I added happens but the character doesn't go up.

The RevMob support person says "You have to consider in your game the situations of closing and coming back. The problem is not RevMob but your game that does not deal with the user closing the app and coming back."

edit: this partially fixed it http://stackoverflow.com/questions/3994522/how-to-reset-game-in-cocos2d. Now the app doesn't run in the background. However, if a popup system alert comes in while the user is playing, and they then click ok to it, after that the game is unplayable unless you exit.

Reply
andy_triboletti 2 months ago

I tried porting to Android using apportable (using the latest version release_1.1.04.1) after adding Admob to my project using this tutorial. I got a compile error.

http://pastebin.com/aWgRvUjr

Reply
joshdavies338863 2 months ago
kiritmodi_kiritmodi 2 months ago

It's nice tutorials any other tutorials with cocos2d.

Reply
joshua_yamen 2 months ago

Hi, every time I try to run the project in Xcode it comes up with the sprite builder wallpaper and doesn't let me do anything past that. Also I have a problem with the coding with the part that makes the fly face down when you don't touch the screen for a little, it says: Use of undeclared identifier 'delta'. Here is my code:


//
// MainScene.m
// PROJECTNAME
//
// Created by Viktor on 10/10/13.
// Copyright (c) 2013 Apportable. All rights reserved.
//

#import "MainScene.h"
static const CGFloat scrollSpeed = 80.f;
@implementation MainScene {
CCSprite *_bird;
CCPhysicsNode *_physicsNode;
CCNode *_ground1;
CCNode *_ground2;
NSArray *_grounds;
NSTimeInterval _sinceTouch;

}
- (void)didLoadFromCCB {
_grounds = @[_ground1, _ground2];
self.userInteractionEnabled = TRUE;

}
- (void)update:(CCTime)delta {
// clamp velocity
float yVelocity = clampf(_bird.physicsBody.velocity.y, -1 * MAXFLOAT, 200.f);
_bird.physicsBody.velocity = ccp(scrollSpeed, yVelocity);
_physicsNode.position = ccp(_physicsNode.position.x - (scrollSpeed *delta), _physicsNode.position.y);
// loop the ground
for (CCNode *ground in _grounds) {
// get the world position of the ground
CGPoint groundWorldPosition = [_physicsNode convertToWorldSpace:ground.position];
// get the screen position of the ground
CGPoint groundScreenPosition = [self convertToNodeSpace:groundWorldPosition];
// if the left corner is one complete width off the screen, move it to the right
if (groundScreenPosition.x <= (-1 * ground.contentSize.width)) {
ground.position = ccp(ground.position.x + 2 * ground.contentSize.width, ground.position.y);
}
}

}
- (void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
[_bird.physicsBody applyImpulse:ccp(0, 400.f)];
[_bird.physicsBody applyAngularImpulse:10000.f];
_sinceTouch = 0.f;

_sinceTouch += delta;
_bird.rotation = clampf(_bird.rotation, -30.f, 90.f);
if (_bird.physicsBody.allowsRotation) {
float angularVelocity = clampf(_bird.physicsBody.angularVelocity, -2.f, 1.f);
_bird.physicsBody.angularVelocity = angularVelocity;
}
if ((_sinceTouch > 0.5f)) {
[_bird.physicsBody applyAngularImpulse:-40000.f*delta];
}


}
@end

Reply
edi_held 2 months ago *

Is there a way to make the fly smoother?

Compared to Flappy Bird the fly moves quite laggy in the x-direction. What is the reason for that and how could that be changed? Thanks

Reply
benjaminencz 2 months ago

I fixed this issue in the tutorial. Because the scrolling of the physics node is applied by manually setting the position it needs to be the same for the fly.

Replace the code that moves the fly with these lines:

float yVelocity = clampf(_hero.physicsBody.velocity.y, -1 * MAXFLOAT, 200.f);
_hero.physicsBody.velocity = ccp(0, yVelocity);
_hero.position = ccp(_hero.position.x + delta * _scrollSpeed, _hero.position.y);


joel_ran 2 months ago

Anyone tried porting to android using apportable? I tried and it works except that the screen got totally screwed up. Anyone tried with success?

Reply
josh_durcau 2 months ago

There seems to be a problem that causes the bird to fall behind the screen after a while of playing (up to about score 10 it's noticable, and by score 20 it's half off the screen)
This only happens when the bird 'flaps' - I disabled the gameOver method so that I could just let the bird sit on the ground while it scrolled past, and the bird does not fall behind. It only happens when you tap the screen.

Reply
julio_lorenzo 2 months ago

Noticed the same here!

benjaminencz 2 months ago

I fixed this issue in the tutorial. Because the scrolling of the physics node is applied by manually setting the position it needs to be the same for the fly.

Replace the code that moves the fly with these lines:

float yVelocity = clampf(_hero.physicsBody.velocity.y, -1 * MAXFLOAT, 200.f);
_hero.physicsBody.velocity = ccp(0, yVelocity);
_hero.position = ccp(_hero.position.x + delta * _scrollSpeed, _hero.position.y);


julio_lorenzo 2 months ago

Great, Thanks!

ethantran144 2 months, 1 week ago

Is there a way to make the collision more precise? When the fly collides with an obstacle, it is not touching the obstacle by a few pixels. Also, how do collisions deal with shapes that do not have square angles? The obstacles in this project are curved, but, from what I can tell, the fly is hitting an invisible rectangle around the obstacle.

Reply
edi_held 2 months ago

You can make the collision more precise. It is described in detail here: https://www.makegameswith.us/tutorials/getting-started-with-spritebuilder/

jonathan_spence 2 months, 1 week ago

Everything seems to work after doing the tutorial, except for the score. It stays at 0. I have checked the _scoreLabel in the sprite builder. I am not sure what to do, THANKS!

Reply
jonathan_spence 2 months ago

I have looked all over and tried to compare the solution to the one I built and I can't figure out why it won't update from 0. Everything else works for me. Thanks for your help and this tutorial!

diana_perkins 1 month, 2 weeks ago *

I had the same problem - I didn't have the goal in the Obstacle.ccb file as a static physics body. Had to turn on physics / set it to static.

That sounds like the kind of game I would make. You can never make progress, only lose. :)

teofilo_vizcaino 2 months, 1 week ago

HOW TO LIMIT THE Y POSITION OF THE FLY:

Touch fast on the screen. The fly goes up and up and goes off screen. How to limit the Y position.

Reply
justin4vn 2 months ago

In MainScene, add this line:

CGRect screenRect;

In didLoadFromCCB, add this line:

screenRect = [[UIScreen mainScreen] bounds];

In update:(CCTime)delta, add these line:

if (_hero.position.y >= screenRect.size.height)
{
_hero.position = ccp(_hero.position.x, screenRect.size.height);
}


rboorman 5 days, 23 hours ago *

This solution seems to work fine for the iPhone. However I am having issues on the iPad. My hero goes up in the air off the screen but only on the iPad. Thoughts?

k_ferguson 2 months, 1 week ago *

**EDIT: When you do the code connections in SpriteBuilder, make sure you select "Doc root var" and not the default"Don't Assign". It shows in the screenshots, but I don't remember reading it in the text.


I keep getting a problem creating the array when I do
_grounds = @[_ground1, _ground2];

even when I copied the exact .m example you gave. Is there an alternative to using arrays?

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[0]'

Reply
teofilo_vizcaino 2 months, 1 week ago

It's because _ground1 and _ground2 are nil. Make sure those are the connection names in SpriteBuilder and you published your last changes.

chriswilcoxson1 2 months, 1 week ago

Is anyone having issues building right after adding the spawnNewObject method? The error states it can't find my ccbi file, but I have verified that it is in the project resources and named correctly. Here is the error.

2014-02-13 21:36:17.924 FlappyFalcon[11853:70b] *** Assertion failure in -[CCPhysicsNode addChild:], /Users/*******/Documents/FlappyFalcon.spritebuilder/Source/libs/cocos2d-iphone/cocos2d/CCNode.m:679
2014-02-13 21:36:17.927 FlappyFalcon[11853:70b] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Argument must be non-nil'

Reply
teofilo_vizcaino 2 months, 1 week ago

Is that the entire error log? Check your connections in SpriteBuilder

anthony_orias 2 months, 1 week ago

Are any of you able to get the ground scrolling working? I followed the tut and no go.

Also I get a break on ' _hero.zOrder = DrawingOrderHero; ' ...

Reply
robbietrahan 2 months, 1 week ago

full

typedef NS_ENUM(NSInteger, DrawingOrder) {
DrawingOrderPipes,
DrawingOrderGround,
DrawingOrdeHero

};


Should be after the 3 static constants

and before the
@implementation MainScene

robbietrahan 2 months, 1 week ago

This tutorial is awesome and i followed it to the letter but i keep getting this error on the absolute final step.

Lexical or Preprocessor Issue
'CCNode.h' not found
/Developer/SpriteBuilderProjects/FlappyFly.spritebuilder/Source/Goal.m:9:

What gives?

Reply
matthew_strang 2 months, 1 week ago

I am also having some trouble with "No visible @interface for 'Main Scene' declares the selector 'spawnNewObstacle'"

[self spawnNewObstacle];
[self spawnNewObstacle];
[self spawnNewObstacle];


and "Property 'zOrder' not found on object of type 'Obstacle'"
Obstacle.zOrder = DrawingOrderPipes;

Reply
teofilo_vizcaino 2 months, 1 week ago

You didn't add the spawnNewObstacle method to MainScene.m:


- (void)spawnNewObstacle {
CCNode *previousObstacle = [_obstacles lastObject];
CGFloat previousObstacleXPosition = previousObstacle.position.x;
if (!previousObstacle) {
// this is the first obstacle
previousObstacleXPosition = firstObstaclePosition;
}
Obstacle *obstacle = (Obstacle *)[CCBReader load:@"Obstacle"];
obstacle.position = ccp(previousObstacleXPosition + distanceBetweenObstacles, 0);
[obstacle setupRandomPosition];
[_physicsNode addChild:obstacle];
[_obstacles addObject:obstacle];

}


And check the capitalisation for the other line

obstacle.zOrder = DrawingOrderPipes;

leeham14 2 months, 1 week ago

change
if (groundScreenPosition.x <= (-1 * ground.contentSize.width))
to
if (groundScreenPosition.x <= -1 * ground.contentSize.width)

Reply
matthew_strang 2 months, 1 week ago

Thanks, Could you help with this "Expected Expression" error?

if ((_sinceTouch > 0.5f;)) {

teofilo_vizcaino 2 months, 1 week ago

For the guy asking for "Expected Expression" error, delete the semicolon, and a pair of parentheses if you want:

if (_sinceTouch > 0.5f) {

matthew_strang 2 months, 1 week ago

I keep getting an error saying that there is an "expected expression" and a "use of undeclared identifier 'lf'" on this line:

if (groundScreenPosition.x <= (-1 * ground.contentSize.width))

Reply
leeham14 2 months, 1 week ago

change
if (groundScreenPosition.x <= (-1 * ground.contentSize.width))
to
if (groundScreenPosition.x <= -1 * ground.contentSize.width)

bagome 2 months, 1 week ago

Hey, first thanks for your tutorial, it's very awesome and very well written!
i would like to suggest a best way to move the hero when the touch occur, the problem is that with applyImpulse: the movement is not always the same, some times it moves by 100px and some times more or less, it depends on the acceleration the object has gained at that exact point.
So i would replace this line [_hero.physicsBody applyImpulse:ccp(0, 400.f)]; in the method - (void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event
with this 3 lines:

CCActionMoveBy *move = [CCActionMoveBy actionWithDuration:0.5 position:ccp(0, 50)];
[_hero runAction:move];
[_hero.physicsBody applyImpulse:ccp(0, -1 * _hero.physicsBody.velocity.y)];

in this way with do 2 things:
1) move the hero of always the same amount of space
2) nullify the current velocity the object has so the move is not affected by other forces

What do you think? i have found the game more playable in this way.
Thanks again for the great resource!

Reply
benjaminencz 2 months, 1 week ago

I like this idea and will take a look at it. However, the idea of this tutorial is to imitate Flappy Bird and it seems like the game is implemented in a similar way (applying an impulse) which makes it so hard.

I will definitely try this out - thanks a lot!

P.S.: You are also welcome to fork the GitHub project!

tough_guy 2 months, 1 week ago

How do we implement a menu, sounds, and a high score into this game?

Reply
olle_svensson 1 month, 3 weeks ago

Yeah I want to know that too!

evan_turner 2 months, 1 week ago
Top TopTop