我的应用是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
应该显示与其他操作一致的解码行为。但是,做一个例外是有用的,这样任何已经完成的动作都不会再次运行。如何在解码后阻止序列重启?
答案 0 :(得分:2)
我能想到完成你想要达到的目标的唯一方法是:
从那里你有两个选择,除了剩下多少时间,当你重新创建动作时,将其用于计算或根据剩余时间创建新动作并对其进行编码。
我不认为SKActions真的打算以这种方式使用,但至少可能是一项工作。我认为开发人员更常见的是存储"状态"他们的游戏持久性,而不是试图存储实际的精灵和行动。它与UIKit的东西是一样的。你不会为了持久性存储UIViews,而是会有一些其他对象包含根据用户进度重新创建的信息。希望其中一些至少有点帮助。祝你好运。
修改强>
在理论上提供更多信息""我会这样做,你是对的,这是一个麻烦。
这一点的重点是每个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] ]]];
无论何时对节点进行编码,方法doX
,doY
和doZ
都只会运行一次。
根据动画的不同,等待的持续时间可能看起来很奇怪。例如,假设应用程序在doX
和doY
运行后保留,在doZ
之前的1秒延迟期间保留。然后,在恢复时,应用程序将不会再次运行doX
或doY
,但会在运行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]];
}
通过此实现,如果在doX
和doY
运行后保留序列,则在恢复时,doZ
之前的延迟将仅为1秒。当然,它是一整秒(即使它在编码之前已经过了一半),但结果是可以理解的:在编码时序列中正在进行的任何操作都会重新开始,但是一旦完成,它就完成了。 / p>
当然,制作一堆这样的方法是令人讨厌的。相反,创建一个序列管理器对象,当触发这样做时,将序列分解为子序列,并以有状态的方式运行它们。