Многократное перерисовывание GameHUD приводит к значительному снижению частоты кадров cocos2d

#iphone #performance #cocos2d-iphone #ccsprite

#iPhone #Производительность #cocos2d-iphone #ccsprite

Вопрос:

В настоящее время я создаю игру для iPhone с cocos2d и сталкиваюсь со следующей проблемой:

У меня есть одноэлементный класс GameHUD, который отображает HUD перед сценой текущего уровня. Время от времени я хочу, чтобы HUD перерисовывался, чтобы он менялся в соответствии с текущим статусом игры. Проблема в том, что чем чаще я перерисовываю HUD, тем больше падает частота кадров.

Я предполагаю, что мне не удается освободить некоторые ресурсы, но не могу понять, какие из них я должен освободить. (Я довольно новичок в управлении памятью.. Я понимаю, что мне нужно освободить объекты, созданные с одним из следующих ключевых слов: «создать», «выделить», «скопировать» или «сохранить». Но cocos2d в основном генерирует объекты авторелиза, поэтому я не должен освобождать их вручную.. поправьте меня, если я ошибаюсь ;))

 //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];
}   
 

}

Поэтому при каждом вызове этого метода частота кадров немного падает и остается низкой.. Может кто-нибудь, пожалуйста, скажите мне, что я делаю не так? Спасибо.

Комментарии:

1. Вы используете разные изображения каждый раз при перерисовке? Можете ли вы точно объяснить, что вы пытаетесь сделать с HUD? Что именно меняется при перерисовке?

Ответ №1:

Старайтесь не создавать и не удалять спрайты во время выполнения, т.Е. старайтесь избегать частого выполнения:

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

Это выделяет новую память для спрайта, и при создании нового спрайта за кулисами происходит немало событий. И, конечно же, вы выпускаете уже существующие спрайты. Во всем этом нет необходимости.

В вашем методе redrawGameHUD я не вижу никаких указаний на то, почему вы на самом деле хотите создать спрайты заново. Спрайты каждый раз используют одни и те же изображения. Так почему бы просто не сохранить старые? Если вы не отредактировали код до того, как опубликовали его в вопросах, нет необходимости удалять и заново создавать спрайты HUD.

Вы также можете создать атлас текстур для всех изображений спрайтов HUD. Во-первых, затем вы можете визуализировать все HUD-спрайты одним вызовом draw, используя CCSpriteBatchNode . Во-вторых, всякий раз, когда вы хотите назначить новую текстуру существующему спрайту, вы просто измените CCSpriteFrame этого спрайта вместо того, чтобы выбрасывать спрайт и создавать его заново.

Что-то еще, что меня беспокоит, ты продолжаешь писать это:

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

Во-первых, это то же самое, что и при написании следующего, сообщение классу абсолютно не нужно (заставляет меня задуматься, где вы это взяли?):

 [[GameHUD sharedHUD] addChild:sprite];
 

И поскольку вы делаете это несколько раз, вам следует сохранить временную локальную копию GameHUD, это снова удаляет несколько ненужных сообщений Objective-C.:

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

Это особенно хороший совет для циклов, потому что при этом:

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

отправит два дополнительных сообщения (class и sharedHUD) для каждого объекта в сборном массиве. Эти дополнительные вызовы могут быстро накапливаться, хотя их, конечно, недостаточно для снижения частоты кадров, которое вы испытываете.

Вы также без необходимости сохраняете все спрайты HUD в «сборном» массиве. Почему бы не использовать уже существующий дочерний массив, который использует cocos2d? Просто добавьте каждый «сборный» HUD-спрайт с тем же тегом, например 123.

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

Если вам нужно что-то сделать со всеми «сборными» спрайтами, вы можете выполнить итерацию по дочерним элементам следующим образом:

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

Опять же, это позволяет избежать ненужного добавления, сохранения, удаления и освобождения объектов в сборном массиве. И вы можете быть уверены, что случайно не удалите спрайты из иерархии узлов, но не из сборного массива, или наоборот.

В заключение я хотел бы сделать предположение: в вашем коде я увидел общую тенденцию к тому, что вы делаете много лишних вещей без необходимости. Так что я предполагаю, что это имеет место во всем проекте. Возможно, вы захотите вернуться назад и спросить себя (лучше: выяснить), какие другие функции, которые выполняет устройство, вам не нужны.

Комментарии:

1. спасибо, это отличный совет! Я понимаю, что в этом разделе кода (и, вероятно, во всем проекте) есть что улучшить.. Я не помню, где я получил вызов [GameHUD class].. Я думаю, что я рассматривал это как единственный способ получить доступ к статическому методу из его класса .. отличная идея с тегами, не подумал о них в данном случае..

Ответ №2:

Я на 90% уверен, что вы тестируете это на симуляторе iPhone, я прав?

Если да, то имейте в виду, что вы не можете правильно профилировать приложение OpenGL в симуляторе.. Производительность слишком изменчива.

Протестируйте его на реальном устройстве и еще раз проверьте частоту кадров.

Если нет, то у вас проблема в другом месте. Вся обработка, выполняемая этим методом, тривиальна, если только ваши изображения не имеют размер 5000×5000 пикселей или что-то в этом роде..