Каковы вероятные причины случайных сбоев с cocos2d и как их отследить? - PullRequest
0 голосов
/ 08 ноября 2011

У меня есть аркадная игра для iPhone, которая построена на Cocos2d-iPhone и работает нормально, за исключением того, что она случайно вылетает без видимой причины после непредсказуемого количества игр. Иногда вы можете играть на нескольких уровнях без проблем, а на других он вылетает через несколько секунд. В XCode есть несколько общих строк, которые обычно обозначаются как EXC_BAD_ACCESS, что указывает на наличие памяти, но все объекты автоматически высвобождаются с дополнительным сохранением, и сбои очень противоречивы. Одна вещь, которая кажется подходящей, состоит в том, что по мере того, как происходит больше, вероятность аварийного отказа возрастает, что снова наводит на мысль о некоторой проблеме с памятью, но затем не выдается предупреждение памяти приложения.

Некоторые примеры строк сбоя:

if(gameGlobals.gameState == GAME_STATE_PAUSED) {...}
if(![hearts isKindOfClass:[CCSprite class]]) {...}

но обе эти строки имеют глобальные объекты, которые целую вечность до краха.

Итак, мой вопрос на самом деле, как мне отследить проблему и есть ли вероятные виновники?

Спасибо.

UPDATE

Я занимался детективной работой, и у меня, по крайней мере, есть последовательность. Я думаю, что происходит две вещи:

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

2) У меня есть несколько зомби-спрайтов, и в противном случае они выпускаются без моего понимания.

Когда я добавляю спрайт пули, я использую:

CCSprite *bullet = [CCSprite spriteWithFile:@"bullet.png"];
bullet.tag = BULLET_TAG_GOODY;
bullet.position = ccp(player.position.x,50);
[self addChild:bullet];

[bullet runAction:[CCSequence actions:
[CCMoveTo actionWithDuration:2 position:ccp(player.position.x,320)],
                                   [CCCallFuncN actionWithTarget:self selector:@selector(bulletMoveFinished:)],
                                   nil]];
[bullets addObject:bullet];

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

Итак, 1) как мне это настроить, чтобы мои спрайты не выпускались? Это хорошая идея, чтобы удалить авто-релиз из CCSprite? 2) Каков наилучший способ обнаружения пуль, сталкивающихся с базой в стиле космических захватчиков, наносящих удар по спрайту при его выстреле?

В настоящее время:

if([self overlapBases: bullet.position.x :bullet.position.y :true]) {...}

-(bool)overlapBases :(GLfloat)x :(GLfloat)y :(bool)up {
//   NSLog(@"overlapping - %f,%f",x,y);
 if(y > 20 && y < 100) {
     int bn = -1;
     if(x > 28 && x < 118) {
         bn = 0;
     } 

     if(x>140 && x<230 ) {
         bn = 1;
     }

     if(x>254 && x<343 ) {
         bn = 2;
     }

     if(x>365 && x<453 ) {
         bn = 3;
     }

     if(bn> -1) {
     NSLog(@"overlapping - %f,%f",x,y);
//     for (int ix = 0; ix < NUM_BASES; ix++) {

         if (overLap(x, 2, aspeaker[bn].position.x, BASE_WIDTH * aspeaker[bn].scale)) {
             if (overLap(y, 4, aspeaker[bn].position.y, BASE_HEIGHT * aspeaker[bn].scale)) {
                 NSLog(@"ix: %i, x: %f, y: %f",bn,x,y);
                 return [self fineCollision:bn :x :y :up];
             }
         }
     }
}
return false;    
}


-(bool)fineCollision :(GLuint)baseNum :(GLfloat)x :(GLfloat)y :(bool)up {
 //convert  bullet x and y passed in to this base coords
 //check if solid
 //if so, remove bits in gfx and array, return true
 //else return false;

 GLfloat bullx = (x - aspeaker[baseNum].position.x + BASE_WIDTH) / 2.0;
 GLfloat bully = (y - aspeaker[baseNum].position.y + BASE_HEIGHT) / 2.0;
 GLint testx = (GLint)bullx;
 if ((testx < 0) | (testx >= BASE_WIDTH)) {
 return false;
 }

 GLuint testy;  
 bool hit = false;

for (int iy = -2; iy < 2; iy++) {

testy = (GLint)(bully - iy);                            
if ((testy >= 0) & (testy < BASE_HEIGHT)) {

if (baseShape[baseNum][15 - testy][testx] > 0) {


 if(showrm == YES) {
     CCSprite *maskSprite = [CCSprite spriteWithFile:@"bullet-mask.png"];

     [maskSprite setBlendFunc: (ccBlendFunc) {GL_ZERO, GL_ONE_MINUS_SRC_ALPHA}];

     maskSprite.position = ccp( x , y-(40+22-5) );

     NSLog(@"sprite: %f",maskSprite.position.x);

     NSLog(@"render mask: %f",rm.frameInterval);
     [rm addSpriteMask:maskSprite];





     [rm drawCurrent];


 }


 [self remove:testx :testy :baseNum :up];

 GLuint seed = rand()%64;
 if (seed & 1) {

 [self remove:testx - 1:testy - 1 :baseNum :up];
 }
 if (seed & 2) {
 [self remove:testx - 1:testy :baseNum :up];
 }
 if (seed & 4) {
 [self remove:testx - 1:testy + 1:baseNum :up];
 }
 if (seed & 8) {
 [self remove:testx + 1:testy - 1:baseNum :up];
 }
 if (seed & 16) {
 [self remove:testx + 1:testy :baseNum :up];
 }              
 if (seed & 32) {
 [self remove:testx + 1:testy + 1:baseNum :up];                 
 }

 hit = true;                
 }
 }
 }

 return hit;
 }



 - (void)remove:(GLint)offX :(GLint)offY :(GLuint)baseNum :(bool)up {
 if ((offX < 0) | (offX >= BASE_WIDTH)) {
 return;
 }

 if ((offY < 0) | (offY >= BASE_HEIGHT)) {
 return;
 }  
 baseShape[baseNum][15 - offY][offX] = 0;


 }

bool overLap(GLfloat x1, GLfloat w1, GLfloat x2, GLfloat w2) {

GLfloat left1, left2;
GLfloat right1, right2;

GLfloat halfWidth1 = w1 * 0.5f;
GLfloat halfWidth2 = w2 * 0.5f;

left1 = x1 - halfWidth1;
left2 = x2 - halfWidth2;
right1 = x1 + halfWidth1;
right2 = x2 + halfWidth2;

if (left1 > right2) {
    return false;
}

if (right1 < left2) {
    return false;
}

return true;
}

Для обнаружения и визуализации маски используйте множество спрайтов и режим их наложения для истощения.

Спасибо.

ОБНОВЛЕНИЕ СНОВА :)) 1034 *

Хорошо, я нашел несколько зомби!

Address Category    Event Type  RefCt   Timestamp   Size    Responsible Library Responsible Caller

0   0x13a00e90  CCSprite    Malloc  1   00:32.974.212   432 SpacedInvaders  +[CCSprite spriteWithFile:]

1   0x13a00e90  CCSprite    Autorelease <null>  00:32.974.235   0   SpacedInvaders  +[CCSprite spriteWithFile:]

2   0x13a00e90  CCSprite    Retain  2   00:32.974.546   0   SpacedInvaders  -[CCArray insertObject:atIndex:]

3   0x13a00e90  CCSprite    Retain  3   00:32.974.629   0   SpacedInvaders  -[CCActionManager addAction:target:paused:]

4   0x13a00e90  CCSprite    Retain  4   00:32.974.634   0   SpacedInvaders  -[CCArray addObject:]

5   0x13a00e90  CCSprite    Release 3   00:32.986.279   0   QuartzCore  CA::Display::DisplayLink::dispatch(unsigned long long, unsigned long long)

6   0x13a00e90  CCSprite    Release 2   00:33.074.889   0   SpacedInvaders  -[CCArray removeObject:]

7   0x13a00e90  CCSprite    Release 1   00:33.074.915   0   SpacedInvaders  -[CCActionManager deleteHashElement:]

8   0x13a00e90  CCSprite    Release 0   00:33.074.918   0   SpacedInvaders  -[CCArray removeObject:]

9   0x13a00e90  CCSprite    Zombie  -1  00:33.074.939   0   SpacedInvaders  -[GameLayer update:]

Так что здесь происходит? Я вижу, что количество удержаний слишком низкое, но как мне этого избежать. Я предполагаю, что делаю что-то тупое между прочим.

Спасибо.

Ответы [ 5 ]

1 голос
/ 08 ноября 2011

Единственный способ, которым эта линия может потерпеть крах

if(gameGlobals.gameState == GAME_STATE_PAUSED) {...}

- это если gameGlobals является объектом Objective C, который уже был освобожден.

Если gameGlobals - это C struct, вы не увидите EXC_BAD_ACCESS, потому что это не указатель.

Если gameGlobals является объектом nil, вы не увидите EXC_BAD_ACCESS, потому что отправка сообщений на nil - это нормально.

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

Если вы все еще не можете понять это, первая часть ответа Аде - путь (NSZombie), хотя вторая часть, вероятно, не поможет. Я сомневаюсь, что твои объекты когда-нибудь станут равными нулю.

0 голосов
/ 09 ноября 2011

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

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

Лучше всего сначала запустить Analyzer, а затем запустить Instruments, профилировать с помощью инструмента Leaks.при обнаружении EXC_BAD_ACCESS ошибок на сайте Лу Франко:

Понимание EXC_BAD_ACCESS

0 голосов
/ 08 ноября 2011

«все объекты являются автоматически выпущенными» звучит как вероятная причина.Авто-релиз вступает в силу, когда пользовательский интерфейс «выходит в эфир» (управление возвращается из текущей логики обработки событий пользовательского интерфейса), и если у вас есть автоматически выпущенная переменная, которую вы ожидаете продолжить использовать после этого, она, вероятно, стала «пуф»(хотя часто вы можете использовать его некоторое время спустя, пока хранилище не будет повторно использовано).

Запускали ли вы анализатор или какие-либо другие инструменты?

0 голосов
/ 08 ноября 2011

Если я правильно вас понял, то «автоматическое освобождение с дополнительными удержаниями» звучит как проблема (если у вас также нет дополнительных выпусков), которая может привести к утечке, ведущей к увеличению использования памяти, что приведет к освобождению других объектов, которые вы впоследствии будете попробуйте получить доступ.

попробуйте включить nszombies: Как включить NSZombie в Xcode?

также использует операторы nslog для отслеживания, когда объекты являются нулевыми, когда вы ожидаете, что они не будут.

если вам известны некоторые случаи, когда вы получаете сбой, то, возможно, вы можете поставить точку останова в другой проверке NULL до этого:

if(gameGlobals.gameState != NULL) {
    if(gameGlobals.gameState == GAME_STATE_PAUSED) {...}
} else {
    //break or nslog here
}
0 голосов
/ 08 ноября 2011

Для меня это определенно похоже на проблемы с памятью. Если это большая утечка памяти, вы также не обязательно получите предупреждение о памяти раньше.

Вы пробовали профилировать вашу игру на утечки с помощью XCode? Если это проблема с памятью, вы увидите ее при сбое во время профилирования.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...