Why are some of the images in this post went missing?
Today we are going to finish up our work to build the famous Flappy Bird like game, Fatty Bird app for App Store.
This is a Day 3 of the 3 Days of the series. Check out the previous posts here.
Hour 1 : Test on iPhone5, iPad
We’re will test our app in all the supported devices to make sure it work as expected. If you don’t have all the device, you can use the simulator in your Mac.
Launch your app in the iPhone5 first. You may notice that the land is slightly lower than expected as it does not cover all the back hill.
Same thing goes to iPad. Add the following code in BaseScene.m init screen.
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { // todo landThickness = 40; } else { if (IS_WIDESCREEN) { landThickness = 35; } }
You may have to adjust the value to fit into your own design.
Next, if you run on iPad, you may notice that the background movement a bit slower in comparison to iPhone, since iPad have at least twice the area compare to iPhone in term of pixels.
Add the following in the moveBg method in both BaseScene.m and MyScene.m to increase the speed.
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { velocity = velocity*1.5; }
The bird jump seems too short and the speed of the falling is slow due to the same reason. Put the following code in the init method of MyScene.m to increase the impulse and speed of the game.
// overwrite for ipad if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { self.physicsWorld.speed = 1.4; impulse = 450; }
Go ahead and run your app again. It is doing good for my case. You can play around the value to suit your game.
Hour 2: Refine bird physics body
If you follow the bird design I show on previous post, the bird is base on round shape. If you have the bird design base on square which is not very likely, then you will not have the problem that I’m going to address later.
If you notice, the game will end when the corner of the bird hit the block, even though the body is not really touching anything.
That’s because in setupPhysics of Bird.m we define the physics body of the bird to be rectangle of the frame of the bird picture. So in this case, the bird physics body is actually a square box that cover the whole image, including those transparent part.
We need a physics body that cover just the bird body, not including any transparent part of the image.
There’s a great tool that help to generate code to construct path by drawing simple polygon using your own image. Below is my design example.
You should do it for your bird image, one for iPhone and the other is for iPad version. You don’t have to go into too much detail by clicking too many point. One thing worth mentioning is that too much point will incur additional calculation for the engine to calculate the physics and collision detection.
Once you’re done, copy the code generated and make the following changes in setupPhysics method in Bird.m to replace the previous physicBody body.
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) { CGFloat offsetX = self.frame.size.width * self.anchorPoint.x; CGFloat offsetY = self.frame.size.height * self.anchorPoint.y; CGMutablePathRef path = CGPathCreateMutable(); CGPathMoveToPoint(path, NULL, 1 - offsetX, 42 - offsetY); CGPathAddLineToPoint(path, NULL, 4 - offsetX, 31 - offsetY); CGPathAddLineToPoint(path, NULL, 32 - offsetX, 8 - offsetY); CGPathAddLineToPoint(path, NULL, 63 - offsetX, 9 - offsetY); CGPathAddLineToPoint(path, NULL, 77 - offsetX, 22 - offsetY); CGPathAddLineToPoint(path, NULL, 75 - offsetX, 43 - offsetY); CGPathAddLineToPoint(path, NULL, 65 - offsetX, 65 - offsetY); CGPathAddLineToPoint(path, NULL, 45 - offsetX, 71 - offsetY); CGPathCloseSubpath(path); self.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:path]; } else { CGFloat offsetX = self.frame.size.width * self.anchorPoint.x; CGFloat offsetY = self.frame.size.height * self.anchorPoint.y; CGMutablePathRef path = CGPathCreateMutable(); CGPathMoveToPoint(path, NULL, 1 - offsetX, 20 - offsetY); CGPathAddLineToPoint(path, NULL, 10 - offsetX, 6 - offsetY); CGPathAddLineToPoint(path, NULL, 23 - offsetX, 4 - offsetY); CGPathAddLineToPoint(path, NULL, 36 - offsetX, 7 - offsetY); CGPathAddLineToPoint(path, NULL, 39 - offsetX, 13 - offsetY); CGPathAddLineToPoint(path, NULL, 35 - offsetX, 23 - offsetY); CGPathAddLineToPoint(path, NULL, 30 - offsetX, 34 - offsetY); CGPathAddLineToPoint(path, NULL, 21 - offsetX, 34 - offsetY); CGPathCloseSubpath(path); self.physicsBody = [SKPhysicsBody bodyWithPolygonFromPath:path]; }
Give it try to run your game. The game collision detection should now restrict to the area you define in the polygon path.
Hour 3: Create App in iTunesConnect , Rating
Before you need to enable rating for your app, you have to create app to get the App ID.
Go to iTunesConnect and create the app for your game. Once you’re done, copy the App ID and do the following in touchesBegan method in MainMenuScene.m class.
if ([node.name isEqualToString:@"rateButton"]) { // important to mention this link doesnt work on simulation because simulator doesnt have app store NSString* launchUrl = @"itms-apps://itunes.apple.com/app/id822148936"; [[UIApplication sharedApplication] openURL:[NSURL URLWithString: launchUrl]]; }
You need to replace the app id with yours own app id in order to link it to your own app.
By the way, you won’t be able to get the rating button works in simulator as the simulator don’t have the App Store install on it. You have to test it on your device, but then you will still see a blank page in App Store as your app still haven’t submitted to app store.
Hour 4 : iAd
In this hour we want to talk about money and ads
In the first day of this series, we already plan to have our ads to position it on the bottom of the screen, and we already leave some extra space below the land to fit the ads banner below it.
Before you continue, you need to include iAd frameworks into your project. Go to Project Info > Linked Frameworks and Libraries, and add the iAd.framework into your project.
Change the ViewController.h to the following.
#import <iAd/iAd.h> @interface ViewController : UIViewController<ADBannerViewDelegate> { ADBannerView *adView; } @property (assign) Boolean bannerIsVisible; @property (assign) Boolean bannerSuccessLoaded; -(void)showsBanner; -(void)hidesBanner; @end
Next, add the following in the viewWillLayoutSubviews in ViewController.m class.
// replace this code to get the view to properly display the ads //SKView * skView = (SKView *)self.view; SKView * skView = (SKView *) self.originalContentView; // put it inside the initialization of scene, after the present scene [skView presentScene:scene]; self.canDisplayBannerAds = YES; adView = [[ADBannerView alloc] initWithFrame:CGRectZero]; CGRect adFrame = adView.frame; adFrame.origin.y = self.view.frame.size.height-adView.frame.size.height; adView.frame = adFrame; adView.delegate=self; [self.view addSubview:adView]; self.bannerIsVisible = NO; self.bannerSuccessLoaded = NO;
What this does is it tells the app that it can display banner ads, and we position the ads in the bottom of the screen. If you would like to position it on the top, you can do the following instead at line 13.
adFrame.origin.y = 0;
Next, we need to handle callback from the ads delegate to handle display of the banner.
-(void)bannerViewDidLoadAd:(ADBannerView *)banner { self.bannerSuccessLoaded = YES; if (self.bannerIsVisible) { [UIView beginAnimations:@"animatedAdBannerOn" context:NULL]; [UIView setAnimationDuration:1]; banner.frame = CGRectOffset(banner.frame, 0.0, 0.0); [UIView commitAnimations]; } } -(void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error { self.bannerSuccessLoaded = NO; [UIView beginAnimations:@"animatedAdBannerOff" context:NULL]; [UIView setAnimationDuration:1]; banner.frame = CGRectOffset(banner.frame, 0.0, 0.0); [adView setAlpha:0]; [UIView commitAnimations]; }
We will only show the banner under this conditions:
- When the banner successfully loaded some ads
- When we are at main menu scene
- When the game is over
Next implement the method to hide/show the ads banner like below.
-(void)hidesBanner { NSLog(@"HIDING BANNER"); [adView setAlpha:0]; self.bannerIsVisible = NO; } -(void)showsBanner { if (self.bannerSuccessLoaded) { NSLog(@"SHOWING BANNER"); [adView setAlpha:1]; } self.bannerIsVisible = YES; }
We need to tell the app when to show or hide the ads banner following the condition above. Add the following in MainMenuScene class.
// when start button is pressed [self.viewController hidesBanner]; -(void)didMoveToView:(SKView *)view { [self.viewController showsBanner]; }
The didMoveToView will called whenever the scene is loaded, so it will load the ads banner whenever the main menu scene start.
Add the following in the MyScene.m class for each of the scenario to show and hide the ads banner.
// when the game is over [self.viewController showsBanner]; // gotoMain method [self.viewController hidesBanner]; // replay method [self.viewController hidesBanner];
Go ahead and run your game. You should be able to see the ads banner when you load the game. When the game started, the ads will hide itself and will show again when the game is over.
Before you complete this hour, you need to enable iAd support for your app in the iTunesConnect.
Goes to your app details and click on the ‘Manage iAd App Network’.
Click the ‘Enable iAd Network’ to use iAd in your app.
Next, you need to setup the contracts of the iAd app network (if you haven’t done it before). Go to “Contracts, Tax and Banking” in the iTunesConnect home page and fill up the below information.
You only need to fill up the Contact Info and Tax Info if you gonna use the same Bank Info as the iOS Paid Application. Wait for 24 hours and it take the iAd contracts into effective.
Hour 5-6 : Sound Effect and Add It Into Your Game
In coming two hour, we are going to include sound effect into our game.
Sound effect has a great impact to evoke user emotion, not just for game. In my opinion, when use correctly, it’s a part of a good UX design.
We need knowledge to create good sound effect, and it need time to acquire that knowledge.
For this game, I’m going to search for some free sound effect I can found on the web and edit it to suit my need. Freesound.org has one of the largest collection of sound effect submit by others so go ahead and start looking for some you like.
*Do pay attention to the attribution and commercial statement that might attached to the work they submit.
I use the below sound effect for different action:
- Button, Flap – http://www.freesound.org/people/qubodup/sounds/60009/
- Hit – http://www.freesound.org/people/thebondman/sounds/111739/
- Collect Point – http://www.freesound.org/people/Zott820/sounds/209578/
For the collect point sound effect, I need to cut part of the original track. I use Audacity to perform the trick, and it’s very easy to do. You can refer to the tutorial here on how to do it.
The search of a suitable sound effect can take a long time, so be patient and have fun.
Once you have all your sound effect, download the file in .wav form, and add it into your XCode.
You need to include AVFoundation frameworks into your project. Go to Project Info > Linked Frameworks and Libraries, and add the AVFoundation.framework into your project.
Next, add the following in BaseScene.h class.
@property (strong, nonatomic) SKAction *coinSound; @property (strong, nonatomic) SKAction *selectSound; @property (strong, nonatomic) SKAction *flySound; @property (strong, nonatomic) SKAction *hitSound; @property (strong, nonatomic) SKAction *gameoverSound; @property (strong, nonatomic) AVAudioPlayer *audioPlayer; - (void) playButtonSound;
Add the following in BaseScene.m class init method to initialize the sound.
self.coinSound = [SKAction playSoundFileNamed:@"coin.wav" waitForCompletion:NO]; self.flySound = [SKAction playSoundFileNamed:@"select.wav" waitForCompletion:NO]; self.hitSound =[SKAction playSoundFileNamed:@"hit.wav" waitForCompletion:NO]; self.gameoverSound = [SKAction playSoundFileNamed:@"select.wav" waitForCompletion:NO]; NSURL *url = [[NSBundle mainBundle] URLForResource:@"select" withExtension:@"wav"]; NSError *error = nil; self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error]; if (!self.audioPlayer) { NSLog(@"Error creating player: %@", error); }
Implement the method to play the button sound.
-(void)playButtonSound { [self.audioPlayer play]; }
We’re done with the sound setup. Now to include sound it’s as easy as to invoke the method and call the action.
In MainMenuScene.m class, add the following when the button is triggered.
// add both into startButton and rateButton handler [self playButtonSound];
In MyScene.m class, add the following for collection point sound, hit sound and button sound.
-(void)didBeginContact:(SKPhysicsContact *)contact { // when increment the point [player runAction:self.coinSound]; // when the bird is hit [player runAction:self.hitSound]; } -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { // replay button and menu button [self playButtonSound]; // when the bird is fly [player runAction:self.flySound]; }
That’s it for this hour.
Hour 7 & 8: Add Feature and Submission to App Store
Congratulation! You have just finish building your game from sketch, design, coding and ready for submission in just 3 days.
But now that you have 2 extra hours before you complete the day, maybe can you add some extra feature into your game. You can:
- Add bird rotation animation when the bird is flying down
- Some funny scream sound effect while the bird is falling down
- Anything you find it fun to add into your game
You are ready to submit your app to App Store anytime from now.
This is the end of the series. I hope you learn something in the process and having great time here.
Let me know your thought on this tutorial. Thanks for reading.
Subscribe to my newsletter below to get my best content for building app.
The post Create Flappy Bird Game in 3 Days – Day 3 appeared first on Yoke Harn.