3

私のアプリは、アプリケーションの状態の保存と復元を備えた 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シーケンスは最初から再び開始され、2 回目のフェードアウトとインが表示されます。

SKAction sequenceが他のアクションと一致するデコード動作を示すべきであることは理にかなっています。ただし、既に完了したアクションが再度実行されないように、例外を作成すると便利です。デコード後にシーケンスが再開しないようにするにはどうすればよいですか?

4

2 に答える 2

2

あなたが達成しようとしていることを達成するために私が考えることができる唯一の方法は、次のとおりです。

  1. アクションを開始すると、時間を変数に格納します。更新関数で渡される「currentTime」値を使用することに注意してください。
  2. エンコードする必要がある場合は、アクションを作成してからエンコードするまでに経過した時間を計算します。

そこから、残り時間を保存する 2 つの選択肢があり、アクションを再作成するときにそれを計算に使用するか、残り時間に基づいて新しいアクションを作成してエンコードします。

SKActions が実際にこの方法で使用されることを意図していたとは思いませんが、少なくとも回避策になる可能性があります。開発者は、実際のスプライトやアクションを保存しようとするのではなく、ゲームの「状態」を永続化のために保存する方が一般的だと思います。UIKitのものと同じです。永続性のために UIView を保存するのではなく、ユーザーの進行状況に基づいて再作成する情報を含む他のオブジェクトを使用します。うまくいけば、そのいくつかは少なくとも少しは役に立ちました。幸運を祈ります。

編集

「理論的に」私がこれをどのように行うかについての詳細情報を提供するために、あなたが正しいのは面倒です。

  1. サブクラス SKSpriteNode
  2. そのスプライトでアクションを実行する新しいメソッド (-(void)startAction:withKey:duration: など) を作成します。このメソッドは、最終的にキーで実行アクションを呼び出します。
  3. startAction が呼び出されると、そのアクション、そのキー、期間、および startTime (デフォルトは 0) を格納するディクショナリを使用して、ある種の MutableArray に格納します。そのアクションを実際に保存する必要さえないかもしれません。キー、期間、および開始時間だけです。
  4. update: メソッドをこの SKSpriteNode サブクラスに追加します。update を呼び出すすべての更新ループで、1 つのアクションに開始時間がないかどうか、および 2 それらのアクションがまだ実行されているかどうかを確認します。開始時刻がない場合は、現在の時刻を開始時刻として追加し、実行していない場合は配列から削除します。
  5. そのスプライトをエンコード/保存するときは、その配列の情報を使用してそれらの SKAction の状態を判断します。

この例の大きなポイントは、各 SKSpriteNode が独自の SKAction を保持して追跡することです。申し訳ありませんが、Objective-C でコードを作成する時間がありません。また、これがあなたの答えよりも優れているか悪いかを主張したり、暗示したりしようとしているわけではありませんが、質問が求めるように SKActions の状態を保存することにした場合、これをどのように処理するかを説明しています。=)

于 2016-04-12T01:01:11.340 に答える
1

シーケンスは、特定のサブシーケンスが終了すると実行されなくなり、デコード時に再開されないように、いくつかのSKActionサブシーケンスに分解できます。

コード

シーケンスを管理できる軽量でエンコード可能なオブジェクトを作成し、シーケンスをサブシーケンスに分割し、(エンコード時に) 既に実行されたものを記憶します。GitHub のライブラリに実装を記述しました。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] ]]];

ノードがいつエンコードされても、メソッドdoXdoY、およびdoZは 1 回だけ実行されます。

ただし、アニメーションによっては、待機時間が奇妙に見える場合があります。たとえば、アプリケーションが の後に保存され、の前の 1 秒の遅延中に実行されたdoXとします。その後、復元時にアプリケーションは実行されませんが、実行されるまで 11 秒待機します。doYdoZdoXdoYdoZ

おそらく奇妙な遅延を回避するには、シーケンスを依存サブシーケンスのチェーンに分割し、それぞれが次のサブシーケンスをトリガーします。たとえば、分割は次のようになります。

- (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 秒になります。確かに、それは 1 秒 (エンコード前に半分経過していたとしても) ですが、結果はかなり理解できます。エンコード時にシーケンス内で進行中だったアクションはすべて再開されますが、完了すると完了します。

もちろん、このようなメソッドをたくさん作るのは厄介です。代わりに、シーケンス マネージャー オブジェクトを作成します。このオブジェクトは、トリガーされると、シーケンスをサブシーケンスに分割し、ステートフルな方法で実行します。

于 2016-04-12T02:46:53.327 に答える