How To Create Dynamic Textures with CCRenderTexture in Cocos2D 2.X
Learn how to create dynamic textures similar to the hills in Tiny Wings using CCRenderTexture in this Cocos2D 2.X Tutorial. By Ali Hafizji.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
How To Create Dynamic Textures with CCRenderTexture in Cocos2D 2.X
25 mins
Update 5/17/2013 Fully updated for Cocos2D 2.X. (original post by Ray Wenderlich, update by Ali Hafizji).
You’re probably familiar with adding premade backgrounds into your games. But what if you could dynamically create backgrounds and modify their colors, gradients, and effects on the fly?
If you’ve seen the game Tiny Wings by Andreas Illiger on the App Store, you’ve seen an example of this in action.
In this tutorial series, you’ll get hands-on experience doing this yourself! You’ll learn:
- How to create textures on the fly
- How to create seamless textures with Gimp
- How to blend shadows and highlights onto textures for realistic effects
- How to create striped textures
- How to set up textures to repeat
- And much more!
This tutorial is based on a great sample project by Sergey Tikhonov that was created as part of an investigation of Tiny Wings on the Cocos2D forums.
Sergey did an excellent job on the demo project, so rather than reinvent the wheel, I’m going to convert his demo code into a tutorial series, along with some extra cool features!
This tutorial assumes you are familiar with Cocos2D. If you are new to either of these, check out some of the other Cocos2D tutorials on this site.
Creating Dynamic Textures with CCRenderTexture
One of the cool things about Tiny Wings is that the textures change every day, as you can see in the below screenshot:
But how can you create a dynamic texture in Cocos2D? Well, there is a cool class you can use called CCRenderTexture that allows you to draw to a texture, and then re-use that texture in your game.
Using CCRenderTexture is simple – you just take the following 5 steps:
- Create a new CCRenderTexture. You specify the width and height of the texture you want to create here.
- Call CCRenderTexture:begin. This sets up OpenGL so that any further drawing draws into the CCRenderTexture (rather than onto the screen).
- Draw into the texture. You can draw by using raw OpenGL commands, or by calling the visit methods of existing Cocos2D objects (which will issue the required OpenGL commands to draw those objects).
- Call CCRenderTexture:end. This will render the texture and turn off drawing into the texture.
- Create a new Sprite from the texture. You can now create a new sprite from the CCRenderTexture’s sprite.texture property.
Note that you can repeat steps 1-3 to continually add/modify the texture over time. For example this might be handy to implement a drawing app. However for this tutorial, you just need to do the drawing once and then you’re done.
Let’s try out RenderTexture to see how it works, to just create a simple colored texture.
But first you need a new project! So in Xcode, go to File\New\New Project, and the choose iOS\cocos2d v2.x\cocos2d iOS with Box2d template. Even though this tutorial doesn’t use Box2D, some of the follow-up tutorials will, so you’ll pick that now to be set up in advance.
Name the project TinySeal and choose iPhone for Device Family, click Next, choose a folder on your hard drive, and click Create.
Open the Supporting Files\Prefix.pch file and add the following import statement below the import to Foundation.h:
#import "cocos2d.h"
Also add the following define statements below the #endif line:
#define IS_IPHONE ( [[[UIDevice currentDevice] model] isEqualToString:@"iPhone"] )
#define IS_HEIGHT_GTE_568 [[UIScreen mainScreen ] bounds].size.height >= 568.0f
#define IS_IPHONE_5 ( IS_IPHONE && IS_HEIGHT_GTE_568 )
The above import statement will ensure that all Cocos2D header files are present across all classes in your game. The define statement you added helps you identify whether the current device you’re running on is an iPhone 5 or not (i.e. with a 4″ display). You’ll use this in the sections to come.
Then open up HelloWorldLayer.h and replace it with the following:
@interface HelloWorldLayer : CCLayer
+(CCScene *) scene;
@end
Next, switch to HelloWorldLayer.mm and replace it with the following (just to fully remove the Box2D code and get an empty scene):
#import "HelloWorldLayer.h"
@interface HelloWorldLayer() {
CCSprite *_background;
}
@end
@implementation HelloWorldLayer
+(CCScene *) scene {
CCScene *scene = [CCScene node];
HelloWorldLayer *layer = [HelloWorldLayer node];
[scene addChild: layer];
return scene;
}
-(id) init {
if((self=[super init])) {
}
return self;
}
@end
This removes the “Hello, World” Box2D code and adds an instance variable to keep track of the dynamic background you’re about to create. Also it has some basic initialisation methods to create the scene.
Build and run the project, and you should get a blank screen as shown below:
Next, add the following new method above the init method:
-(CCSprite *)spriteWithColor:(ccColor4F)bgColor textureWidth:(float)textureWidth textureHeight:(float)textureHeight {
// 1: Create new CCRenderTexture
CCRenderTexture *rt = [CCRenderTexture renderTextureWithWidth:textureWidth height:textureHeight];
// 2: Call CCRenderTexture:begin
[rt beginWithClear:bgColor.r g:bgColor.g b:bgColor.b a:bgColor.a];
// 3: Draw into the texture
// You'll add this later
// 4: Call CCRenderTexture:end
[rt end];
// 5: Create a new Sprite from the texture
return [CCSprite spriteWithTexture:rt.sprite.texture];
}
As you can see, the five steps to create a dynamic texture are the same as we discussed earlier.
Note that instead of calling the plain old CCRenderTexture:begin
method, you call a convenience method named beginWithClear:g:b:a:
that clears the texture with a particular color before drawing.
You haven’t drawn anything yet – for now let’s just see what this plain colored texture looks like.
Wrap this up by adding the following new methods:
- (ccColor4F)randomBrightColor {
while (true) {
float requiredBrightness = 192;
ccColor4B randomColor =
ccc4(arc4random() % 255,
arc4random() % 255,
arc4random() % 255,
255);
if (randomColor.r > requiredBrightness ||
randomColor.g > requiredBrightness ||
randomColor.b > requiredBrightness) {
return ccc4FFromccc4B(randomColor);
}
}
}
- (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);
[self addChild:_background z:-1];
}
- (void) onEnter {
[super onEnter];
[self genBackground];
[self setTouchEnabled:YES];
}
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self genBackground];
}
randomBrightColor
is a helper method to create a random color. Note it uses ccc4B
(so you can specify the R/G/B/A values in the 0-255 range), and makes sure at least one of them is > 192 so you don’t get dark colors. It then converts it to a ccc4F (which converts the values to the 0-1 range).
Then genBackground
method calls the spriteWithColor:textureWidth:textureHeight:
method you just wrote, and adds it to the center of the screen.
Note: Notice that you specify the width of the texture to be 1024 incase the device is an iPhone 5. This is because the iPhone 5 in landscape has a width of 1136 pixels and since Cocos2D uses points, this means 568 points. The closest power of two to 568 is 1024 hence the width is 1024. Why do you consider power of two? Read on to learn more.
Note: Notice that you specify the width of the texture to be 1024 incase the device is an iPhone 5. This is because the iPhone 5 in landscape has a width of 1136 pixels and since Cocos2D uses points, this means 568 points. The closest power of two to 568 is 1024 hence the width is 1024. Why do you consider power of two? Read on to learn more.
As for onEnter
, it calls genBackground
and enables touches so that you can re-generate another random background just by tapping the screen.
Compile and run, and every time you run the app or tap the screen it will have a different colored background: