如何在解码后阻止SKAction序列重启?

时间:2016-03-29 19:50:48

标签: ios sprite-kit skaction uikit-state-preservation

我的应用是SpriteKit游戏,具有应用程序状态保存和恢复功能。保留应用程序状态时,我当前SKScene中的大多数节点都已编码。

当运行SKAction的节点被编码和解码时,操作将从头开始重新启动。这似乎是标准的SpriteKit行为。

对我来说,这种行为对SKAction sequence最为明显。在解码时,无论有多少组件操作已经完成,序列都会重新启动。例如,假设运行序列的代码如下所示:

[self runAction:[SKAction sequence:@[ [SKAction fadeOutWithDuration:1.0],
                                      [SKAction fadeInWithDuration:1.0],
                                      [SKAction waitForDuration:10.0],
                                      [SKAction removeFromParent] ]]];

如果在10秒等待期间保留应用程序状态,然后恢复,SKAction序列将从头开始,第二个可见淡入和淡出。

SKAction sequence应该显示与其他操作一致的解码行为。但是,做一个例外是有用的,这样任何已经完成的动作都不会再次运行。如何在解码后阻止序列重启?

2 个答案:

答案 0 :(得分:2)

我能想到完成你想要达到的目标的唯一方法是:

  1. 当您开始操作时,将时间存储在变量中。请记住,您将需要使用" currentTime"更新函数中传递的值。
  2. 当您需要编码时,计算从创建操作到编码时的时间。
  3. 从那里你有两个选择,除了剩下多少时间,当你重新创建动作时,将其用于计算或根据剩余时间创建新动作并对其进行编码。

    我不认为SKActions真的打算以这种方式使用,但至少可能是一项工作。我认为开发人员更常见的是存储"状态"他们的游戏持久性,而不是试图存储实际的精灵和行动。它与UIKit的东西是一样的。你不会为了持久性存储UIViews,而是会有一些其他对象包含根据用户进度重新创建的信息。希望其中一些至少有点帮助。祝你好运。

    修改

    在理论上提供更多信息""我会这样做,你是对的,这是一个麻烦。

    1. 子类SKSpriteNode
    2. 创建一个新方法来对该Sprite运行操作(如 - (void)startAction:withKey:duration :),最终用键调用run action。
    3. 当调用startAction时,您将其存储到某种MutableArray中,其中包含一个存储该操作的字典,其键,持续时间和startTime(默认为0)。您甚至可能不必实际存储该操作,只需要存储密钥,持续时间和开始时间。
    4. 在此SKSpriteNode子类上添加update:方法。每个更新循环都会调用其更新并检查1是否有任何操作没有开始时间,如果这些操作仍在运行,则检查2。如果没有开始时间,则将当前时间添加为开始时间,如果没有开始运行,则将其从阵列中删除。
    5. 当您去编码/保存该精灵时,您可以使用该数组中的信息来确定这些SKAction的状态。
    6. 这一点的重点是每个SKSpriteNode在此示例中保留并跟踪其自己的SKAction。抱歉,我没有时间在Objective-C中编写代码。我也没有声称或试图暗示这比你的答案更好或更差,而是解决如果我决定如你的问题要求保存SKActions的状态我将如何处理这个问题。 =)

答案 1 :(得分:1)

SKAction序列可以分解为多个子序列,一旦特定的子序列完成,它将不再运行,因此在解码时不会重新启动。

守则

制作一个轻量级,可编码的对象,它可以管理序列,将其分解为子序列并记住(编码)已经运行的内容。我写了一个实现in a library on GitHub。这是current state of the code in a gist

这是一个例子(使用与下面相同的顺序):

HLSequence *xyzSequence = [[HLSequence alloc] initWithNode:self actions:@[
                                      [SKAction waitForDuration:10.0],
                                      [SKAction performSelector:@selector(doY) onTarget:self],
                                      [SKAction waitForDuration:1.0],
                                      [SKAction performSelector:@selector(doZ) onTarget:self] ]];
[self runAction:xyzSequence.action];

概念

第一个想法:将序列拆分为几个独立的子序列。每个子序列完成后,它将不再运行,因此如果保留应用程序,则不会对其进行编码。例如,像这样的原始序列:

[self runAction:[SKAction sequence:@[ [SKAction performSelector:@selector(doX) onTarget:self],
                                      [SKAction waitForDuration:10.0],
                                      [SKAction performSelector:@selector(doY) onTarget:self],
                                      [SKAction waitForDuration:1.0],
                                      [SKAction performSelector:@selector(doZ) onTarget:self] ]]];

可以像这样拆分:

[self runAction:[SKAction sequence:@[ [SKAction performSelector:@selector(doX) onTarget:self] ]]];

[self runAction:[SKAction sequence:@[ [SKAction waitForDuration:10.0],
                                      [SKAction performSelector:@selector(doY) onTarget:self] ]]];

[self runAction:[SKAction sequence:@[ [SKAction waitForDuration:11.0],
                                      [SKAction performSelector:@selector(doZ) onTarget:self] ]]];

无论何时对节点进行编码,方法doXdoYdoZ都只会运行一次。

根据动画的不同,等待的持续时间可能看起来很奇怪。例如,假设应用程序在doXdoY运行后保留,在doZ之前的1秒延迟期间保留。然后,在恢复时,应用程序将不会再次运行doXdoY,但会在运行doZ之前等待11秒。

为了避免可能奇怪的延迟,将序列分成一系列从属子序列,每个子序列触发下一个子序列。对于该示例,拆分可能如下所示:

- (void)doX
{
  // do X...
  [self runAction:[SKAction sequence:@[ [SKAction waitForDuration:10.0],
                                        [SKAction performSelector:@selector(doY) onTarget:self] ]]];
}

- (void)doY
{
  // do Y...
  [self runAction:[SKAction sequence:@[ [SKAction waitForDuration:1.0],
                                        [SKAction performSelector:@selector(doZ) onTarget:self] ]]];
}

- (void)doZ
{
  // do Z...
}

- (void)runAnimationSequence
{
  [self runAction:[SKAction performSelector:@selector(doX) onTarget:self]];
}

通过此实现,如果在doXdoY运行后保留序列,则在恢复时,doZ之前的延迟将仅为1秒。当然,它是一整秒(即使它在编码之前已经过了一半),但结果是可以理解的:在编码时序列中正在进行的任何操作都会重新开始,但是一旦完成,它就完成了。 / p>

当然,制作一堆这样的方法是令人讨厌的。相反,创建一个序列管理器对象,当触发这样做时,将序列分解为子序列,并以有状态的方式运行它们。