How To Create A Game Like Tiny Wings with Cocos2D 2.X Part 1

Learn how to create a game like Tiny Wings with Cocos2D 2.X in this tutorial series – including dynamic terrain generation and game physics! By Ali Hafizji.

Leave a rating/review
Save for later
Share

Update 5/18/2013 Fully updated for Cocos2D 2.X. (original post by Ray Wenderlich, update by Ali Hafizji).

Tiny Wings is an extremely popular game by Andreas Illiger involving a bird who tries to fly by catching a ride on hills.

At first glance, the gameplay of Tiny Wings looks very simple, but there are a lot of tricks going on under the hood. The hills and their textures are dynamically generated, and the game uses Box2D physics to simulate the movement of the bird.

Due to the popularity of the game and the cool technical tricks within, a lot of developers have been curious about how things are implemented.

Including you guys! You guys said you wanted this tutorial updated – well you want it, you got it! :]

This tutorial series is based on an excellent demo project written by Sergey Tikhonov that demonstrates how to implement some of the trickiest features of Tiny Wings. Thanks Sergey!

This tutorial series is split into three parts:

  1. Prerequisite: First review the How To Create Dynamic Textures with CCRenderTexture in Cocos2D 2.X tutorial, which shows you how to create the hill and background textures you’ll be using in this tutorial.
  2. Part 1: You are here! This part will show you how to create the dynamic hills that you’ll need for a game like Tiny Wings.
  3. Part 2: The second part will show you how to add the Box2D gameplay that you’ll need for a game like Tiny Wings.

This tutorial assumes you are familiar with Cocos2D. If you are new to Cocos2D, you should check out some of the other Cocos2D tutorials on this site first.

Getting Started

If you don’t have it already, download the sample project where you left off in the previous tutorial.

Next, create a class for the terrain by going to File\New\New File, choosing iOS\Cocoa Touch\Objective-C class, and clicking Next. Name the class Terrain and make sure it it a subclass of CCNode. Click next and create the file.

Then open up Terrain.h and replace its contents with the following:

@class HelloWorldLayer;

@interface Terrain : CCNode

@property (retain) CCSprite * stripes;
- (void) setOffsetX:(float)newOffsetX;

@end

Next you’re going to start implementing Terrain.m. I’m going to explain it step by step, so go ahead and delete everything currently in Terrain.m and add the following code section by section.

#import "Terrain.h"
#import "HelloWorldLayer.h"

#define kMaxHillKeyPoints 1000

@interface Terrain() {
    int _offsetX;
    CGPoint _hillKeyPoints[kMaxHillKeyPoints];
    CCSprite *_stripes;
}
@end

@implementation Terrain

@end

This declares an array called _hillKeyPoints where you’ll store all of the points representing the peak of each hill, and an offset for how far the terrain is currently being scrolled. Next add the following method in the implementation section.

- (void) generateHills {
    
    CGSize winSize = [CCDirector sharedDirector].winSize;
    float x = 0;
    float y = winSize.height / 2;
    for(int i = 0; i < kMaxHillKeyPoints; ++i) {
        _hillKeyPoints[i] = CGPointMake(x, y);
        x += winSize.width/2;
        y = (random() % (int) winSize.height);
    }
    
}

This is a method to generate the key points for some random hills. This is an extremely simple implementation just so you can have a starting point.

The first point is the left-side of the screen, in the middle along the y-axis. Each point after that moves half the width of the screen along the x-axis, and is set to a random value along the y-axis, from 0 up to the height of the screen.

Next add the following methods below the generateHills method

- (id)init {
    if ((self = [super init])) {
        [self generateHills];
    }
    return self;
}

- (void) draw {
    
    for(int i = 1; i < kMaxHillKeyPoints; ++i) {    
        ccDrawLine(_hillKeyPoints[i-1], _hillKeyPoints[i]);        
    }   
}

The init method calls generateHills to set up the hills, and the draw method simply draws lines between each of the points for debugging, so you can easily visualize them on the screen.

//Add these methods below the draw method
- (void) setOffsetX:(float)newOffsetX {
    _offsetX = newOffsetX;
    self.position = CGPointMake(-_offsetX*self.scale, 0);
}

- (void)dealloc {
    [_stripes release];
    _stripes = NULL;
    [super dealloc];
}

Think about how the terrain moves - as our hero advances along the x-axis of the terrain, the terrain is sliding to the left. So we have to multiply the offset by -1 here - and don't forget to take into consideration the scale!

Almost time to test this. Switch to HelloWorldLayer.mm and make the following changes:

// Add to top of file
#import "Terrain.h"

// Add inside @interface
Terrain * _terrain;

// Add inside onEnter BEFORE the call to genBackground
_terrain = [Terrain node];
[self addChild:_terrain z:1];

// Add at bottom of update
[_terrain setOffsetX:offset];

// Modify genBackground to the following
- (void)genBackground {
    
    [_background removeFromParentAndCleanup:YES];
    
    ccColor4F bgColor = [self randomBrightColor];
    _background = [self spriteWithColor:bgColor textureWidth:IS_IPHONE_5 ? 1024:512 textureHeight:512];
    
    CGSize winSize = [CCDirector sharedDirector].winSize;
    _background.position = ccp(winSize.width/2, winSize.height/2);
    ccTexParams tp = {GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT};
    [_background.texture setTexParameters:&tp];
    
    [self addChild:_background];
    
    ccColor4F color3 = [self randomBrightColor];
    ccColor4F color4 = [self randomBrightColor];
    CCSprite *stripes = [self stripedSpriteWithColor1:color3 color2:color4 
                              textureWidth:IS_IPHONE_5 ? 1024:512    textureHeight:512 stripes:4];
    ccTexParams tp2 = {GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_CLAMP_TO_EDGE};
    [stripes.texture setTexParameters:&tp2];
    _terrain.stripes = stripes;
    
}

Note this sets the stripes texture on the Terrain to a new random stripes texture each time you tap, which is handy for testing.

Also, when calling setTextureRect on _background in update, you might wish to multiply the offset by 0.7 to get the background to scroll slower than the terrain.

And that's it! Compile and run your code, and now you should see some lines drawn across the scene representing where the tops of your hills will eventually be:

Basic hill keypoints with debug drawing

As you watch your hills scroll by, you'll probably realize pretty quickly that these wouldn't work very well for a Tiny Wings game. Due to picking the y-coordinate randomly, sometimes the hills are too big and sometimes they are too small. There's also not enough variance in the x-axis.

But now that you have this test code working and a good way to visualize and debug it, it's simply a matter of dreaming up a better algorithm!

You can either take a few moments and come up with your own hill algorithm, replacing the code in generateHills, or you can use Sergey's implementation, shown in the next section!

Ali Hafizji

Contributors

Ali Hafizji

Author

Over 300 content creators. Join our team.