2015年5月27日水曜日

SKAction runBlock:について

Objective-Cの場合

SpriteにActionを設定するやり方の一つに、block構文を用いる runBlock: メソッドがある。

繰り返しActionを実行する場合、Actionの都度block構文中の処理を全部やってくれるのかと思ってたが、そうじゃないようだ。

たとえば以下の様な場合、設定したmoveByX:100 y:0 duration:1は繰り返し実行してくれるが、NSLogが表示されるのは一度きり。

SKAction *action = [SKAction runBlock:^{
        NSLog(@"hogehoge");
        SKAction *ac = [SKAction moveByX:100 y:0 duration:1];
        [_sprite runAction:[SKAction repeatActionForever:ac]];
    }];
[_sprite runAction:action];

変数使ったり乱数を埋め込んだりしても、一番最初に出た値がずっと保持されるようだ。
Actionのたびに移動量をランダムで変化させるとかどうやるんだ?
どうもこのActionってやつは、決まりきった処理をさせることはできても、インタラクティブな処理(状況に応じてActionの内容や動きを変える)なんてのはできないっぽいな。
それが必要な場合はActionの外のコードで状況をチェックして、再度Actionを設定するとかそういう処理が必要っぽい。なんだよ、使えねえな。


なお、block中で
[_sprite runAction:[SKAction repeatActionForever:ac]];
とやってやらないと、うまくいかない。
blockの外の
[_sprite runAction:action];
の位置で
[_sprite runAction:[SKAction repeatActionForever:action]];
とやってやると、block中の処理は繰り返されるようになるが、それ以外の処理を受け付けなくなったり、いいことない。
実際のActionを繰り返すのでなく、blockの定義自体を繰り返すことになっているんだろう。
(最後XcodeからもiPad実機からも終了できなくなり、リセットするに至った(ノД`))
なかなかむずかしい。



同様にblock構文が使える
customActionWithDuration: actionBlock:
を使ったら、こちらも一度しか呼ばれないし、Durationの数値を変えるとblock中の変数の値が、途中からとんでもなくでかい数値になったりしてますますわかんない。
これは別の記事に書く。

Swift4の場合

SKAction.run { } もしくは SKAction.run ({ }) を使ってクロージャの中のコードを実行させることができる。
クロージャの中にSpriteのrun()を書く必要があるのだが、以下のようにしてrepeatForeverとして繰り返し実行させ、中の値を乱数で毎回変化させたい場合、なかなかうまくいかない。

【注意】深刻なトラブルを起こすコード

まずは本筋と関係ないのだが、以下のコードを実行したところ、かなりこわいことになったので注意。
Objective-C + iPadの時にもなったし、iOS11 + Swift4のiPhone8でも再現したので、再現性もある。

以下のコードを実機で実行すると
  1. アプリの画面が固まり
  2. Xcodeを強制終了させなければならなくなり
  3. 実機確認しようとしてもできなくなり
  4. iPhoneのスリープボタンを押したら画面がブラックアウトし画面キャプチャー操作で音が鳴るので、起動はしてるらしい)
  5. iPhone強制再起動(iPhone8だと音量上、下を一度ずつ押した後、スリープボタン長押し)でようやく元に戻る
という事態に。iTunesから復元する事態には至らずに済んだがだいぶ困った。

クロージャ中の細かなコード云々より、repeatForeverの使い方がいけないんだろうな。(普通はアプリが落ちる程度で済むんだけど)

let spriteNode = SKSpriteNode(imageNamed: "イメージ名")
let furafura = SKAction.run {
        let x = CGFloat(arc4random_uniform(100)) - 50
        let y = CGFloat(arc4random_uniform(100)) - 50
        print("x \(x), y \(y)")
        SKAction.moveBy(x: x, y: y, duration: 0.5)
}
spriteNode.run(SKAction.repeatForever(furafura))

以下はアプリが落ちるだけで平気

落ちる例その1

以下のようにすると毎回乱数が発生し、print()も毎回違った値が表示される。
しかし画面の表示が止まってしまい、最後にXcodeに「iPhoneとの接続が切れたので、再接続してください」みたいなダイアログが出て落ちてしまう。
どうも乱数の部分だけが変更されるのでなく、次々と新たなクロージャが作られて実行される無限ループに陥っているようだ。まあrepeatForeverしてるんだから当然だけど。

let furafura = SKAction.run {
    let action = SKAction.repeatForever(
        SKAction.run({
            let x = CGFloat(arc4random_uniform(100)) - 50
            let y = CGFloat(arc4random_uniform(100)) - 50
            print("x \(x), y \(y)")
            SKAction.moveBy(x: x, y: y, duration: 0.5)
        })
    )
    spriteNode.run(action)
}
spriteNode.run(furafura)

落ちる例その2

以下の2例はrepeatForeverをクロージャの外に出したものだが、print()の実行も一度だけで、画面も固まり、しばらくするとメモリの問題で落ちる。

2の1

let furafura = SKAction.repeatForever(
    SKAction.run {
        let action = SKAction.run({
            let x = CGFloat(arc4random_uniform(100)) - 50
            let y = CGFloat(arc4random_uniform(100)) - 50
            print("x \(x), y \(y)")
            SKAction.moveBy(x: x, y: y, duration: 0.5)
        })
        spriteNode.run(action)
    }
)
spriteNode.run(furafura)

2の2

let furafura = SKAction.run {
    let action = SKAction.run({
        let x = CGFloat(arc4random_uniform(100)) - 50
        let y = CGFloat(arc4random_uniform(100)) - 50
        print("x \(x), y \(y)")
        SKAction.moveBy(x: x, y: y, duration: 0.5)
    })
    spriteNode.run(action)
}
spriteNode.run(SKAction.repeatForever(furafura))

動かない例

以下のようにしてrepeatForeverとして繰り返し実行させ、中の値を乱数で毎回変化させようとしても、値が変わってくれない。print()を入れてみたら、一度しか実行されていない。
どうやらクロージャを定義した時に得られた乱数で確定されているようだ。
let furafura = SKAction.run {
    SKAction.repeatForever(
        SKAction.run({
            let x = CGFloat(arc4random_uniform(100)) - 50
            let y = CGFloat(arc4random_uniform(100)) - 50
            print("x \(x), y \(y)")
            SKAction.moveBy(x: x, y: y, duration: 0.5)
        })
    )
}
spriteNode.run(furafura)


0 件のコメント:

コメントを投稿