Turn off thumb compilation in XCode for ARMv6, but turn it on for ARMv7.
Director:
DisplayLink. DisplayLink is the best Director, but is only available in SDK 3.1 or higher. If DisplayLink is not present, use MainLoop or ThreadMainLoop.// must be called before any other call to the director if( ! [CCDirector setDirectorType:kCCDirectorTypeDisplayLink] ) [CCDirector setDirectorType:kCCDirectorTypeMainLoop];
CCDirector with a very low (quick) interval like 1/240// If you are using "NSTimer" Director you could set a very low interval [[CCDirector sharedDirector] setAnimationInterval:1/240.0];
When possible try to use texture atlas:
CCSprite from a corresponding CCSpriteSheet spritesheet object.CCLabelBMFont or CCLabelAtlas instead of CCLabelTTFCCTMXTileMap or CCTileMapAtlas to render tilesThe Atlas versions of these objects provide faster rendering at the expense of code and art complexity. The Atlas versions utilize an AtlasManager that keeps one large image with multiple frames, and the individual Atlas object refer to a frame of that larger image. This saves on texture counts and accelerates OpenGL ES calls.
Atlas = a book of illustration or diagrams. In this context it means you have one big image loaded into an opengl texture which actually holds a series of smaller images. As opposed to all the smaller images each loaded in to their own texture.
When possible, try to use 4-bit or 16-bit textures
Add this line before loading Textures:
[CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA4444]; // add this line at the very beginning
For background images you can drop the alpha channel by using RGB565 format. This format has the advantage that the visual quality is better than using RGBA4444.
Simply reducing the color by setting the pixel format has the disadvantage of quality loss and the size of the resources still stays big.
Dithering TODO: explain dithering.
PVR textures TODO: explain the benefits of PVR TODO: explain .pvr.gz / .pvr.ccz
The PVR format lets you create RGBA8888, RGBA4444, RGBA5551 and RGB565 textures
Using PVR-Sprites:
CCSprite *sprite = [CCSprite spriteWithFile: @"sprite.pvr"];
* There are 2 type of particles: Quad and Point particle system. * Point particle system seems to be a little bit faster on 1st and 2nd gen devices, but it is much slower on 3rd gen devices / iPad.
So, either you can check the device at runtime, or, the “lazy” approach would be to use Quad Particle.
CCTextureCacheCCTextureCache caches all images// textures with retain count 1 will be removed // you can add this line in your scene#dealloc method [[CCTextureCache sharedTextureCache] removeUnusedTextures]; // since v0.8 // removes a certain texture from the cache CCTexture2D *texture = [sprite texture]; [[CCTextureCache sharedTextureCache] removeTexture: texture]]; // available in v0.7 too // removes all textures... only use when you receive a memory warning signal [[CCTextureCache sharedTextureCache] removeAllTextures]; // available in v0.7 too
NSTimer. Instead use cocos2d’s own scheduler./**********************************************************/ // OK OK OK OK OK /**********************************************************/ -(id) init { if( (self=[super init] ) ) { // schedule a callback [self scheduleUpdate]; // available since v0.99.3 [self schedule: @selector(tick2:) interval:0.5]; } return self; } -(void) update: (ccTime) dt { // bla bla bla } -(void) tick2: (ccTime) dt { // bla bla bla } /**********************************************************/ // BAD BAD BAD BAD /**********************************************************/ // Why BAD ? // You can't pause the game automatically. -(void) onEnter { [super onEnter]; timer1 = [NSTimer scheduledTimerWithTimeInterval:1/FPS target:self selector:@selector(tick1) userInfo:nil repeats:YES]; timer2 = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(tick2) userInfo:nil repeats:YES]; } -(void) onExit { [timer1 invalidate]; [timer2 invalidate]; [super onExit]; } -(void) tick { // bla bla bla } -(void) tick2 { // bla bla bla }
/**********************************************************/ // OK OK OK OK OK /**********************************************************/ -(void) draw { [item draw]; // OK: DRAW INSIDE DRAW } -(void) update:(ccTime) dt { item.position = dt * finalPosition; // OK, UPDATE STATE IN SCHEDULED SELECTOR } /**********************************************************/ // BAD BAD BAD BAD 1 /**********************************************************/ -(void) draw { dt = [self calculateDelta]; // DONT UPDATE STATE IN DRAW. item.position = dt * finalPosition; // Pause won't work [item draw]; } /**********************************************************/ // BAD BAD BAD BAD 2 /**********************************************************/ -(void) update:(ccTime) dt { item.position = dt * finalPosition; [item draw]; // <--- DON'T DRAW IN SCHEDULED SELECTOR // because transformations won't alter your image }
replaceScene instead of pushScenepushScene is very handy, but it will put the pushed scene into memory, and memory is a precious resource in the iPhone.// TRY TO AVOID A BIG STACK OF PUSHED SCENES -(void) mainMenu() { // etc [[CCDirector sharedDirector] pushScene: gameScene]; } // stack: // . game <-- running scene // . mainMenu -(void) game { [[CCDirector sharedDirector] pushScene: gameOverScene]; } // stack: // . gameOver <-- running scene // . game // . mainMenu -(void) showGameOver { [[CCDirector sharedDirector] pushScene: hiScoreScene]; } // stack: // . scores <-- running scene (4 pushed scenes... expensive) // . gameOver // . game // . mainMenu
CCNode objects (CCSprite, CCLabel, CCLayer, etc) or any other kind of object in the init selector and not in the draw and any other scheduled selector/**********************************************************/ // OK, MOST OF THE TIME /**********************************************************/ -(id) init { // etc... sprite1 = [CCSprite create]; // <-- USUALLY IT IS BETTER TO CREATE OBJECTS IN INIT // etc... } -(void) tick: (ccTime) dt { // etc... if( someThing ) { [sprite1 show]; // <--- BUT IF YOU DON'T USE THEM FREQUENTLY, MEMORY IS WASTED } } /**********************************************************/ // BAD, MOST OF THE TIME /**********************************************************/ -(void) tick: (ccTime) dt { // etc... if( someThing ) { sprite = [CCSprite create]; // <--- EXPENSIVE [sprite1 show]; //... [sprite1 release]; // <-- AT LEAST MEMORY IS RELEASED } }
malloc(). For example: A CCSequence of a CCSpawn with a CCRotateBy with a another CCSequence, etc… is very expensive.2010.09.26 - This is confusing because most ObjectiveC classes do NOT expect their init method to be called again on an already initialized object. And this will NOT work for some CCAction subclasses, CCIntervalAction as an example. CCSequence will leak memory if you call InitOne:Two: on it a second time. Etc. To make CCSequence reusable I think you'd have to write a setActionOne:andTwo: and code the class to release the previous actions and then retain the new ones. As a result, I do not believe this “you can just initialize it” advice is good or complete enough if there is some other kind of re-initialization that will work.
this is not a best practice but a Tip
MenuItemImage and place your menu using menu.position = ccp(x,y). See the MenuTest example for more details.
this is not a best practice