0
votes

I'm currently building a game for the iPhone with cocos2d and have the following problem:

I have a singleton class called GameHUD which displays a HUD in front of the current level scene. Once in a while I want the HUD to be redrawn, so it changes accordingly to the current game status. The problem is the more often I redraw the HUD, the more the frame rate drops.

I'm guessing that I fail to release some ressources, but cannot figure out which ones I have to release. (I'm pretty new to memory management.. I understand that I have to release objects that are created with one of following keywords: "new", "alloc", "copy" or "retain". But cocos2d mainly generates autorelease objects, therefor I mustn't release them manually.. correct me, if I'm wrong ;))

//static, so it can be called from other classes
+(void)redrawGameHUD{

CGSize winSize = [CCDirector sharedDirector].winSize;

//get reference to background-sprite
CCSprite *background = [[[GameHUD class] sharedHUD] towerBackground];

//remove the child from the HUD, if it exists
[[[GameHUD class] sharedHUD] removeChild:background cleanup:YES];

//create sprite containing the background-image
background = [CCSprite spriteWithFile:@"background.png"];

//add background image to HUD
[[[GameHUD class] sharedHUD] addChild:background];

//load images that should be displayed into an array
NSArray *images = [NSArray arrayWithObjects:@"image1.png", @"image2.png", @"image3.png", @"image4.png", nil];

//remove sprites from HUD before drawing them again.
//the "buildable" array contains all those already drawn sprites
for (CCSprite *entity in [[[GameHUD class] sharedHUD] buildable]) {
    [[[GameHUD class] sharedHUD] removeChild:entity cleanup:YES];
}
[[[[GameHUD class] sharedHUD] buildable] removeAllObjects];

//loop over sprites, initialize them and add them to the HUD
for(int i = 0; i < images.count; ++i) {

    NSString *image = [images objectAtIndex:i];
    CCSprite *sprite = [CCSprite spriteWithFile:image];

    //add sprite to HUD and memorize them in the "buildable" array
    [[[GameHUD class] sharedHUD] addChild:sprite];
    [[[[GameHUD class] sharedHUD] buildable] addObject:sprite];
}   

}

So every time this method is called, the frame rate drops a little bit and stays down.. Can somebody please tell me, what I am doing wrong? Thanks.

2
Are you using different images every time you redraw? Can you explain exactly what you are trying to do to the HUD? What exactly changes when you redraw?AbhinavVinay

2 Answers

2
votes

Try not to create and remove sprites at runtime, ie try to avoid doing this frequently:

[CCSprite spriteWithFile:@"background.png"];

This allocates new memory for the sprite, and theres quite a bit of things going on behind the scenes when you create a new sprite. And of course you're releasing the already existing sprites. All of that is unnecessary.

In your redrawGameHUD method I see no indication why you actually want to create the sprites anew. The sprites are using the same images every time. So why not just keep the old ones? Unless you edited the code before you posted it in the questions, there's no need to remove and re-create the HUD sprites.

You also might want to create a texture atlas for all the HUD sprite images. For one, you can then render all HUD sprites with one draw call by using a CCSpriteBatchNode. Secondly, whenever you do want to assign a new texture to an existing sprite, you would simply change the CCSpriteFrame of that sprite instead of throwing away the sprite and re-creating it.

Something else that bothers me, you keep writing this:

[[[GameHUD class] sharedHUD] addChild:sprite];

First, this is the same as writing the following, the message to class is absolutely unnecessary (makes me wonder where you picked that up?):

[[GameHUD sharedHUD] addChild:sprite];

And since you do this multiple times over, you should keep a temporary local copy of GameHUD, this again removes several unnecessary Objective-C messages:

GameHUD* gameHUD = [GameHUD sharedHUD];
// from now on use gameHUD instead of [GameHUD sharedHUD]
[gameHUD addChild:sprite];

This is particularly good advice for loops, because doing this:

for (CCSprite *entity in [[[GameHUD class] sharedHUD] buildable])

will send two extra messages (class and sharedHUD) for every entity in the buildable array. Those extra calls can quickly add up, although they're certainly not enough for the drop in framerate you're experiencing.

You also unnecessarily keep all the HUD sprites in the "buildable" array. Why not use the already existing children array that cocos2d uses? Simply add each HUD sprite that is "buildable" with the same tag, for example 123.

[gameHUD addChild:sprite z:0 tag:123];

If you need to do something with all the "buildable" sprites you can then iterate over the children like this:

CCNode* node;
CCARRAY_FOREACH([gameHUD children], node)
{
   if (node.tag == 123)
   {
      CCSprite* buildable = (CCSprite*)node;
      // do stuff with buildable sprite ...
   }
}

Again, this avoids the unnecessary adding, retaining, removing and releasing of objects in the buildable array. And you can be sure that you don't accidentally remove sprites from the node hierarchy but not the buildable array, or vice versa.

I'd like to conclude with an assumption: in your code I saw a general tendency that you're doing many extra things unnecessarily. So I'm guessing this is the case throughout the project. You might want to go back and ask yourself (better: find out) what other things there are that you're having the device perform that are rather unnecessary.

0
votes

I am 90% sure that you are testing this on the iPhone simulator, am I right?

If you are, then keep in mind that you can't profile an OpenGL app properly on the simulator .. The performance is too variable.

Test it on a real device and check the frame rate again.

If not, then you got a problem somewhere else. All the processing done in this method are trivial, unless your images are 5000x5000 px or something..