Turn off thumb compilation in XCode for ARMv6, but turn it on for ARMv7.
Director:
// must be called before any other call to the director
if( ! [CCDirector setDirectorType
:kCCDirectorTypeDisplayLink
] )
[CCDirector setDirectorType
:kCCDirectorTypeMainLoop
];
// 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:
Make CCSprite from a corresponding CCSpriteSheet spritesheet object.
Use CCLabelBMFont or CCLabelAtlas instead of CCLabelTTF
Use CCTMXTileMap or CCTileMapAtlas to render tiles
The 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
16-bit textures for PNG/GIF/BMP/TIFF images
4-bit or 2-bit textures: Try to use PVRTC textures.
Use 32-bit textures as the last resort
Add this line before loading Textures:
[CCTexture2D setDefaultAlphaPixelFormat
:kCCTexture2DPixelFormat_RGBA4444
];
// add this line at the very beginningFor 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.
// 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 Try NOT to use Cocoa’s NSTimer. Instead use cocos2d’s own scheduler.
If you use cocos2d scheduler, you will have:
automatic pause/resume.
when the CCLayer (CCScene, CCSprite, CCNode) enters the stage the timer will be automatically activated, and when it leaves the stage it will be automatically deactivated.
Your target/selector will be called with a delta time
/**********************************************************/
// 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
}try not to update any state variable inside the draw selector.
try not to draw anything inside a scheduled selector
Instead update the state variables inside a scheduled selector.
Instead draw everything inside the draw selector
If you update state variables inside the draw selector, the pause/resume won’t work as expected.
If you draw something inside a scheduled selector, it can’t be transformed
draw is called every frame
scheduled selectors can be called with any frame rate, but no more frequently than the application’s FPS rate.
/**********************************************************/
// 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
}If possible try to use replaceScene instead of pushScene
pushScene 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
// . mainMenuWhen possible try to create 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
The creation of nodes might be expensive, so try to have them pre-created
On the other hand, be careful with the memory. Don’t have unnecessary objects in memory.
/**********************************************************/
// 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
}
}It is expensive to create certain actions, since it might require a lot of malloc(). For example: ACCSequence of a CCSpawn with a CCRotateBy with a another CCSequence, etc… is very expensive.
So try to reuse actions.
Once the action is used, save it for later if you know you will execute that type of action again. Then, instead of allocing a new action, you can just initialize it.
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
this is not a best practice
when the director receives the pause message it won’t call any scheduled target/selector.
but the draw selector will be called at a rate of 4 FPS (to reduce battery consumption)
when the director receives the resume message, the scheduled target/selectors will be called again every frame.