[UIKit × SpriteKit] パーティクルを使ったサンプル

SpriteKitは簡単に作って遊べる、パーティクルって面白いということを伝える記事になります。

# SpriteKitとは

* アニメーションや物理ボディを利用できるフレームワーク。
* グラフィック処理が速く、ゲーム制作で主に使われる。2D、物理演算に関わるクラスを含んでいる。
* Appleが提供している
* iOS7 / Mac OS 10.9以降に対応
* cocos2dと異なり、Android対応不可
* UIKit × SpriteKitのコラボもできる

# 画面表示のイメージ

左はいつも見慣れているUIKitの場合、右はSpriteKitの場合です。こうやってみるとUIKitとSpriteKitは似てますね。
![スクリーンショット 2016-05-21 15.56.12.png](https://qiita-image-store.s3.amazonaws.com/0/100736/74d843bd-97ff-4118-ea14-8c0567d21426.png)

# 今回登場するSpriteKitの要素

## SKNode

SpriteKitコンテンツのほとんどの根底にあるクラス。このクラス自体は何か視覚的なコンテンツを提供するわけではなく、ベースラインの挙動を提供するのが主な役割。NSObject的なもの。

## SKScene

シーンを表す。ゲームの1画面に相当する。

## SKView

SpriteKitを表示する専用のView。UIView的なもの。

## Particle

型のレンダリング技術では再現が難しい、「高度に無秩序」なもの。爆発した時の演出等に使われる。

|パーティクル名|画像|説明|
|:--|:--|:--|
|Bokeh|![スクリーンショット 2016-05-20 1.06.03.png](https://qiita-image-store.s3.amazonaws.com/0/100736/5444fdd7-3410-f9c3-8bd1-2d2c2a4fec63.png)|ボケ。ぼかし。ライフサイクルが終わって消えるまで、輝いてかすんでいく六角形のパーティクル。|
| Fire |![スクリーンショット 2016-05-21 10.52.50.png](https://qiita-image-store.s3.amazonaws.com/0/100736/ab45f35f-5196-431a-880c-3ef2919b535c.png)|暖炉やキャンプファイヤーで見られるような小さな火のエフェクト。|
| Fireflies |![スクリーンショット 2016-05-20 1.06.18.png](https://qiita-image-store.s3.amazonaws.com/0/100736/52673697-7290-c449-871b-40af0c134f38.png)| Fireflies=ホタル。ライフサイクルが終わって消えるまで、ランダムに短い距離を動きながら輝いてかすんでいく短命で黄色のパーティクル。|
| Magic |![スクリーンショット 2016-05-20 1.06.25.png](https://qiita-image-store.s3.amazonaws.com/0/100736/33e46694-9fb0-7a33-50ba-06c8841ece84.png)|ライフサイクルが終わって消えるまで、ランダムに短い距離を動きながら輝いてかすんでいく短命で緑のパーティクル。|
| Rain |![スクリーンショット 2016-05-20 1.06.33.png](https://qiita-image-store.s3.amazonaws.com/0/100736/ec8cdfc7-3074-7a6b-9899-6c6d4fc20089.png)|スクリーンの上から下まで移動していく、形が変化しないパーティクル。各々のパーティクルは異なるスピードで動く。|
| Smoke |![スクリーンショット 2016-05-19 23.27.24.png](https://qiita-image-store.s3.amazonaws.com/0/100736/1945d97e-75a4-8d24-2946-960676e542f3.png)|スクリーンの下から上まで移動していく、大きな黒いパーティクル。動いていくにつれてゆっくりとフェードアウトしていく。※デフォルトは黒です。|
| Snow |![スクリーンショット 2016-05-20 1.06.40.png](https://qiita-image-store.s3.amazonaws.com/0/100736/4195daec-7088-2e72-1189-128e511d2c8c.png)|スクリーンの上から下まで移動していく、白くて放散する丸いパーティクル。各々のパーティクルは異なるサイズであり、異なるスピードで移動していく。|
| Spark |![スクリーンショット 2016-05-20 1.06.49.png](https://qiita-image-store.s3.amazonaws.com/0/100736/7daebf7d-8f9f-bcac-ebcf-2d5091212236.png)|エミッターを中心として360度方向へ短命で黄金の光を放つパーティクル。|

# 作ってみる

## 完成物

起動するとTableViewが表示されます。各セルをタップするとその名前に合ったパーティクルが表示されます。
![スクリーンショット 2016-05-21 15.06.06.png](https://qiita-image-store.s3.amazonaws.com/0/100736/8cafd57b-f7c2-2078-b86c-68e7b9e36dd2.png)![スクリーンショット 2016-05-21 16.25.50.png](https://qiita-image-store.s3.amazonaws.com/0/100736/127e9473-bfbf-2236-b047-6b3efd2a7d89.png)

画面遷移した時にTableViewが上下してしまうというバグがあるのはわかってます。。。そのうち直します。

## コード

ここに上げてます。
https://github.com/akatsuki174/SpriteKitParticleSample

## 動作環境

Xcode: 7.3.1
Swift: 2.2

## プロジェクト作成

いつもは通り、Single View Applicationを選択して作成します。ちなみにSpriteKitだけを扱う場合はGameというものを選択すればOKです。

## パーティクルファイル追加

File -> New -> FileからSpriteKit Particle Fileというものを選択してください。
![スクリーンショット 2016-05-08 13.51.25.png](https://qiita-image-store.s3.amazonaws.com/0/100736/0c4c067c-2450-84d0-73fd-db7b91017ebc.png)
8種類の中から選択することができます。今回はFireしてみます。
![スクリーンショット 2016-05-21 12.56.23.png](https://qiita-image-store.s3.amazonaws.com/0/100736/26dcf63d-4d20-9a9a-628c-1389d3f90db2.png)
これでNextをクリックして名前を付ければとりあえずパーティクルファイルの完成です。
![スクリーンショット 2016-05-21 12.59.34.png](https://qiita-image-store.s3.amazonaws.com/0/100736/11c77eb7-6d28-5f0c-1655-7b4536b19386.png)

## パーティクルの設定値をいじる

すでに[SpriteKitで使用できるパーティクルのパラメータ一覧](http://qiita.com/Raugh/items/614c30aba3c701b9cef3)、[【SpriteKit】爆発や雪などの表現ができる「Particle File」について](http://qiita.com/Koi-fumi/items/55f5e68ef89a48a057b4)という記事が上がっているので説明は省略します。いろいろいじって遊んでみると面白いと思います。

## パーティクルを表示させる

まずspriteを表示しているVCの中身です。

```swift:ContentsViewController.swift
class ContentsViewController: UIViewController {
    var text: String?
    
    @IBOutlet weak var skView: SKView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = UIColor.grayColor()
        self.setupParticle()
    }
    
    func setupParticle() {
        //skView.allowsTransparency = true // 今回は不要。普通はtrueにする。
        //skView.userInteractionEnabled = false // 今回は不要。必要であればfalseにする。
        let scene = ParticleScene.unarchiveFromFile("ParticleScene") as! ParticleScene
        skView.presentScene(scene)
    }
    
    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
        let scene = self.skView.scene as! ParticleScene
        if let t = text {
            scene.show(t)
        }
    }
}

extension SKNode {
    class func unarchiveFromFile(file : NSString) -> SKNode? {
        if let path = NSBundle.mainBundle().pathForResource(file as String, ofType: "sks") {
            let sceneData = try! NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe)
            let archiver = NSKeyedUnarchiver(forReadingWithData: sceneData)
            archiver.setClass(self.classForKeyedUnarchiver(), forClassName: "SKScene")
            let myScene = archiver.decodeObjectForKey(NSKeyedArchiveRootObjectKey) as! ParticleScene
            archiver.finishDecoding()
            return myScene
        } else {
            return nil
        }
    }
}
```

■unarchiveFromFile(file : NSString)
　引数で指定されたファイル名のsksファイルをバンドルからロードしています。

■setupParticle()
　今回はskView.allowsTransparency = trueの指定をしていません。そのためskViewの背景は黒になっています。UIKitを使っていて背景を透明にしておきたい時はtrueに設定しておいてください。
　また、skView.userInteractionEnabled = falseの指定も今回はしていません。UIKit側でイベントを取りたいときはfalseにしておいてください。

```swift:ParticleScene.swiftの一部
class ParticleScene: SKScene {
    func show(type: String) {
        if (self.children.count == 0) {
            guard let path = NSBundle.mainBundle().pathForResource("\(type)Particle", ofType: "sks") else {
                return
            }
            let particle = NSKeyedUnarchiver.unarchiveObjectWithFile(path) as! SKEmitterNode
            particle.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
            self.addChild(particle)
        }
    }
}
```

■position
　今回あまり意識することはないですが、SpriteKitでいう座標(0,0)は左下なので注意してください。

■addChild
　sceneにパーティクルが追加されます。

基本はこれで完成です。パーティクルを表示して遊ぶだけの簡単なアプリを作ることが出来ました。

# おまけ：あれ？パーティクルが表示されない！

いろいろいじっているとこんな自体に遭遇することがあると思います。
![スクリーンショット 2016-05-21 12.33.01.png](https://qiita-image-store.s3.amazonaws.com/0/100736/1e2c908a-b892-3649-b3d2-29dfd4fa27d3.png)

## 再現方法

1. パーティクルファイルを開き、左ペインで値をいじる
2. 別のファイルに移動
3. 再び1のファイルに戻る

## 原因

Xcodeのバグらしいです。[stackoverflow](http://stackoverflow.com/questions/28141300/spritekit-particle-emitter-not-appearing-in-xcode-editor)に上がってました。

## 解決方法

先ほどのstackoverflowに書かれている通りの方法で解決できます。一番簡単なのは該当のファイルを開いた状態でcommand + control + wを実行することです。これで、別のファイルに移動→再び該当のファイルに戻るという動作をすればパーティクルが復活しています。

# 参考になる資料

* [公式ドキュメント](https://developer.apple.com/spritekit/)
* [SpriteKit-Demo](https://github.com/ryusukefuda/SpriteKit-Demo)
* [[iOS] SpriteKitとUIKitを組み合わせて、ちょっとリッチなウォークスルー画面 を作りたい](http://dev.classmethod.jp/smartphone/iphone/spritekitwithuikit/)