Каковы хорошие способы обойти ограничение кодирования блоков кода SKAction во время сохранения состояния приложения?

проблема

Когда иерархия узлов закодирована, как это обычно происходит во время сохранения состояния приложения или «сохранения игры», узлы работаютSKAction Действия с кодовыми блоками должны обрабатываться специально, так как кодовые блоки не могут быть закодированы.

Пример 1. Задержка обратного вызова после анимации.

Здесь орк был убит. Это анимируется, чтобы исчезнуть и затем удалить себя из иерархии узла:

SKAction *fadeAction = [SKAction fadeOutWithDuration:3.0];
SKAction *removeAction = [SKAction removeFromParent];
[orcNode runAction:[SKAction sequence:@[ fadeAction, removeAction ]]];

Если узел orc закодирован и затем декодирован, анимация восстановится правильно и завершится, как и ожидалось.

Но теперь пример изменен, чтобы использовать блок кода, который выполняется после исчезновения. Возможно, код очищает игровое состояние, когда орк (наконец-то) мертв.

SKAction *fadeAction = [SKAction fadeOutWithDuration:3.0];
SKAction *removeAction = [SKAction removeFromParent];
SKAction *cleanupAction = [SKAction runBlock:^{
  [self orcDidFinishDying:orcNode];
}];
[orcNode runAction:[SKAction sequence:@[ fadeAction, removeAction, cleanupAction ]]];

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

SKAction: Действия блока Run не могут быть правильно закодированы, блоки Objective C не поддерживают NSCoding.

После декодирования орк исчезнет и будет удален из родительского, но метод очисткиorcDidFinishDying: не будет называться.

Каков наилучший способ обойти это ограничение?

Пример 2: анимация

SKAction customActionWithDuration:actionBlock: кажется, прекрасно подходит для твининга. Мой стандартный код для такого рода вещей таков:

SKAction *slideInAction = [SKAction customActionWithDuration:2.0 actionBlock:^(SKNode *node, CGFloat elapsedTime){
  CGFloat normalTime = (CGFloat)(elapsedTime / 2.0);
  CGFloat normalValue = BackStandardEaseInOut(normalTime);
  node.position = CGPointMake(node.position.x, slideStartPositionY * (1.0f - normalValue) + slideFinalPositionY * normalValue);
}];

К несчастью,customActionWithDuration:actionBlock: не может быть закодирован. Если игра сохранена во время анимации, она не будет восстановлена ​​должным образом при загрузке игры.

Опять же, каков наилучший способ обойти это ограничение?

Несовершенные решения

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

Несовершенное решение: использованиеperformSelector:onTarget: скорее, чемrunBlock: в анимации. Это решение несовершенно, поскольку аргументы не могут быть переданы вызываемому селектору; контекст для вызова может быть выражен только целью и именем селектора. Не хорошо.

Несовершенное решение: во время кодирования удалитеSKAction последовательность из любых соответствующих узлов и продвигать состояние программы, как если бы последовательность была завершена. В первом примере это будет означать установку узлаalpha немедленно0.0удаление узла orc из родительского узла и вызовorcDidFinishDying:, Это неудачное решение по крайней мере по двум причинам: 1) требуется специальный код обработки во время кодирования; 2) Визуально у узла не будет возможности закончить анимацию.

Несовершенное решение: во время кодирования удалитеSKAction кодировать блоки из любых соответствующих узлов и воссоздавать их во время декодирования. Это нетривиально.

Несовершенное решение: никогда не используйтеSKAction кодовые блоки, особенно после задержки. Никогда не полагайтесь на завершение анимации, чтобы восстановить хорошее состояние приложения. (Если вам нужно запланировать будущее событие в кодируемом виде, создайте свою собственную очередь событий, не используя блоки кода.) Это решение несовершенно, потому чтоrunBlock а такжеcustomActionWithDuration:actionBlock: просто чертовски полезны, и было бы стыдно (и повторяющейся ловушкой для новичков) считать их злыми.

Ответы на вопрос(1)

Ваш ответ на вопрос