2015年7月7日火曜日

Spriteがタッチされたら処理を行う

やりたいこと

spriteをボタンとして扱いたい。

やり方

SKSpriteNodeはUIButton的な機能は持ってないので、touchBegan()の中でtouch箇所がボタン用スプライトの位置かを判定し、処理をすることになる。ちょっとめんどくせえ。
スプライトにはあらかじめ任意のnameを付けておけば複数のボタンに対処できる。

Swift4版

以下はボタン用スプライトをタッチしたらParticleを表示するもの。
ボタンにはあらかじめ"fireButton"というnameが設定されている。

//fireButtonがタッチされたらparticle処理して終了
if let touch = touches.first as UITouch? {
    let location = touch.location(in: self)
    //本とかだと.firstでやるように書かれてるが、途中から反応しなくなることがあるので、
    //.lastにしたほうがいい。原因は不明。これで支障ないのでこれでいい。
    if self.nodes(at: location).last?.name == "fireButton" {
        //パーティクル
        let particleSpark = SKEmitterNode(fileNamed: "SparkParticle.sks")
        particleSpark.position = CGPoint(x: 0, y: 0)
        particleSpark.numParticlesToEmit = 400
        self.scene?.addChild(particleSpark)
        
        return
    }

}

注意点

Particleをいっぱい表示させてたところ、途中からボタンが反応しなくなった。
参考書などでは
if self.nodes(at: location).first?.name == "fireButton" { }
となってたのだが、どうやら.firstの中身がParticleになってしまい、nameが付いていないのではじかれてしまったのだ。
これは.last?にすることで回避できたが、なぜ途中から変わっちゃうんだろう? Particleって勝手に消えてくれるはずだよね? 画面見てもわからないが、不可視要素としてボタンに引っかかってるんだろうか?

Swift2.1版(古いけど参考までに残しとく)

Spriteがタッチされたら何か反応示す場合、Spriteに名前を付けておけば、addChildしてある親SceneのtouchBeganメソッドから、nodeAtPointメソッドでタッチされたノードを得ることができる。
そしてそれの.nameを調べればどのSpriteがタッチされたか判断できる。

以下は、「powerGauge」という名前をつけたSpriteがタッチされたら、バカみたいなメッセージをprintlnするというもの。
touchedNode?.nameをprintltすると「Optional("powerGauge")」というメッセージがコンソールに出るが、if文で判定する際は「powerGauge」だけでいい。

    //タッチ検出
    override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
        let touch = touches.first as! UITouch //Swift1.2から.first
        let location = touch.locationInNode(self)
        
        let str = selectSpriteForTouch(location)
        println("タッチ開始 \(str)")
    }
    
    
    override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
        let touch = touches.first as! UITouch //Swift1.2から.first
        let location = touch.locationInNode(self)
        
        let str = selectSpriteForTouch(location)
        println("タッチ終了 \(str)")
    }
    
    override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
        let touch = touches.first as! UITouch
        let location = touch.locationInNode(self)
        
        if let touchedNode = self.nodeAtPoint(location) as? SKSpriteNode {
            touchedNode.position = location
        }
    }

    //タッチしたSpriteNodenameを得る
    func selectSpriteForTouch(location:CGPoint) -> String {
        let touchedNode = self.nodeAtPoint(location) as? SKSpriteNode
        println("タッチされたSprite: \(touchedNode?.name)")
        let str:String = (touchedNode?.name ?? "にる")
        if touchedNode?.name == "powerGauge" {
            println("パワーゲージだよ~~~ん")
        }
        return str
    }

touchesMoved中の
if let toucheNode = self.nodeAtPoint(location) asSKSpriteNode {
            toucheNode.position = location
        }
は、Swift独特の書き方で、右辺の値がnilじゃなかったらtouchedNodeに値を代入して { }内の処理を行うというもの。
値がnilだったらif文の { }が飛ばされるので、nilによる例外エラーとかが起きない仕組みだ。

selectSpriteForTouch中の
let str:String = (touchedNode?.name ?? "にる")
は、Swiftが原則的にnilを変数に入れられないため、touchedNode.nameがnilだった場合は「にる」をstrに入れよというデフォルト値の設定。

optional関係はまだ不慣れなため、もっといい書き方があると思うけど。

Spriteのカスタムクラスを作った場合でも、いちいちdelegateとか使わなくてもタッチされたSpriteを特定できるので、コードが簡単になる。
(delegate使ったほうが楽な場合もあると思うけどね)

2015年7月2日木曜日

SpriteのゲームはSpriteKitレベルエディタで作ろう

UIViewを使うアプリの画面デザインをするのには、InterfaceBuilderから続くエディタを使うけど、SpriteKitを使うアプリには最初そういうのがなかった(と思う)。
でもXcode6くらいからSpriteKitエディタが付いて、WYSIWYGなグラフィカルな画面デザインができるようになった。
(エディタの正式な名前がよくわからなかったのだが、SpriteKit Level Editorというらしい)

SpriteKitを使ってプロジェクトを作り、拡張子に.sksが付いてるファイルを開けばエディタが開く。
以下のはもう部品を配置したものだが、一般的なSprite(Color Sprite)、照明効果のLight、UILabelのSprite版、パーティクルのEmitterなど、ひと通りのものが右下からドラッグして配置できる。
デフォルトだと画面サイズがなぜか正方形になっちゃうんで(各種iPhone、iPad全てに対応させる概念からだと思うが、まだよくわからん)、必要であればインスペクタのSizeで変えてやるといい。

細かい使い方はUIView用のInterfaceBuilderに準じてるからわかるだろう。
コードからこのエディタで作った画面は、SpriteKitでプロジェクトを作った時のテンプレートとしてGameViewControllerに書かれてくるunarchiveFromFileメソッドで表示している。

実際のゲームの内容はGameSceneの中に書くのだが、エディタに配置したSpriteはInterfaceBuilderみたいにクラスにcontrol+ドラッグ(もしくは右ボタン+ドラッグ)でプロパティを作ることができないので、簡単なコードで認識させる。

あらかじめエディタの方で各SpriteにNameを付けておき、コードの中で以下のようにすればInterfaceBuilderのOutletに相当するものが得られる。
   let barLong = childNodeWithName("barLong")
   var powerGauge = childNodeWithName("powerGauge") as! SKSpriteNode
上のように単にOutlet接続しただけでは、クラスはSKNodeになるようだ。
そこで、SKSpriteNodeのいろんなメソッド、プロパティを使う際は下のように「as!」を使ってSKSpriteNodeにキャストしてやる。

このように、SKNodeのようなスーパークラスを派生クラスSKSpriteNodeにキャストすることを、ダウンキャストと言う。


まだ使い方でわかってないことも多いので、わかったら追記する。

SpriteKit Level Editorについて書かれたページ
[iOS 8] SpriteKit でミニゲームをつくる #5 画面パーツ配置
An iOS 8 Swift Sprite Kit Level Editor Game Tutorial

2015年7月1日水曜日

文字と数字の変換

数字文字列 → 数字


Objective-C

    NSString *str = @"100";
    int a = [str intValue];

Swift

    let str = "100"
    let a:Int = str.toInt()!


数字 → 文字列


Objective-C

    int a = 100;
    NSString *str = [NSString stringWithFormat:@"%d",a];

Swift

    let a = 100
    let str1 = "\(a)"
    let str2 = a.description
str1の方はObjective-CのstringWithFormat的な書き方。
str2の方がSwift的で楽だね。