iPhoneФорумПрограммирование

[ios] cocos2d вызов метода после выполнения ccaction'ов

#0
21:55, 30 апр 2013

Как лучше всего организовать вызов метода после выполнения нескольких экшенов над разными спрайтами?

Один из вариантов предлагают тут stackoverflow. Смысл - вызвать метод после задержки, равной времени проигрывания анимации:

...
ccTime actionDuration = 5.0;
for (CCSprite *sprite in sprites)
{
        [sprite runAction:[CCMoveTo actionWithDuration:actionDuration position:randomPoint]];
}
[self performSelector:@selector(callbackMethod:) withObject:nil afterDelay:actionDuration];

Меня смущает, не получится ли тут какой-нибудь race condition? Возможна ли ситуация, при которой callbackMethod начнет выполняться раньше завершения всех анимаций?

Второй вариант - вызывать коллбэк метод N раз в CCSequence, и уже там считать, сколько раз анимация завершалась:

//организуем вызовы анимаций
id moveAction = [CCMoveTo actionWithDuration: 5.0 position: randomPoint];
id actionCallFunc = [CCCallFunc actionWithTarget:self selector:@selector(animationComplete)];

self.animationCount = 0;
for (CCSprite *sprite in sprites)
{
        [sprite runAction: [CCSequence actions: moveAction, actionCallFunc, nil]];
}
...
//метод, где проверяем сколько раз анимация завершилась
- (void)animationComplete
{
        @syncronized(self)
        {
                self.animationCount++;
                if (self.animationCount == self.sprites.count) //собственно только тут вызов нужного коллбэка
                        [self callbackMethod];
        }
}

Ну тут просто чувствуется костыльность. Явно есть средство лучше. Кто какие практики использует?

#1
22:16, 30 апр 2013

@syncronized зачем? Экшены ж в том же потоке выполняются.
По идее можно только для одного спрайта делать sequence.
Если времена равны, то все экшены кончатся в одном кадре!
А вот использовать performSelector крайне не рекомендую, многократно накалывался. stopActions и cleanup отложенное выполнение селектора не затрагивают. Он запросто может выполнится на удаленных объектах уже. И вообще много чудес несет.

Сам в таких ситуациях делал по первому варианту, но с action

ccTime actionDuration = 5.0;
for (CCSprite *sprite in sprites)
{
        [sprite runAction:[CCMoveTo actionWithDuration:actionDuration position:randomPoint]];
}
[self runAction: [CCSequence actionOne: [CCDelayTime actionWithDuration:actionDuration] two:[CCCallFunc/Block ....]];
#2
22:33, 30 апр 2013

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

id __weak this = self; // чтобы блок не ретейнил self; если self удалится во время анимаций, this станет nil
__block NSUInteger animationCount = sprites.count;
CCAction *actionCallBlock = [CCCallBlock actionWithBlock:^{
    animationCount--;
    if (animationCount == 0)
        [this callbackMethod];
}];

for (CCSprite *sprite in sprites) {
    [sprite runAction:[CCSequence actions:moveAction, actionCallBlock, nil]];
}
#3
22:38, 30 апр 2013

shadowstyle
Универсальный вариант - посылка NSNotification в функции CCCallBlock:.
Подписчик принимает именованную нотификашку и производит нужные пертурбации.

Но мне кажется автор имеет немного другое.
Мне в одном приложении требовался учет того, идет ли данная анимация на объекте или он уже готов к "игровому" удалению - при этом callback было использовать не совсем желательно, поскольку много кода волоклось за ним, и переводить много переменных на __weak с проверками есть переменная или уже удалена - ломало. Поэтому я использовал простую перегрузку анимации, где в конструкторе счетчик (глобальная переменная синглтон) - инкрементировал, в деструкторе - понижал. При достижении нуля - посылал NSNotification об этом. Подписчик производил необходимые действия (проверял уровень на завершение).

#4
22:42, 30 апр 2013

alcoSHoLiK
Вариант хороший, но если приложение вдруг удалит спрайт(ы) до завершения анимации CCSequence, каллбэк не сработает.

#5
2:50, 1 мая 2013

CasDev
Да. А в варианте с таймером на self он сработает. Только вопрос -- этого ли эффекта ожидает автор? В любом случае, спасибо за уточнение.

#6
9:00, 1 мая 2013

tirinox, alcoSHoLiK, CasDev, спасибо за ответы. Использовал предложенный alcoSHoLiK вариант.

iPhoneФорумПрограммирование

Тема в архиве.