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


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

SpriteKitとは

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

画面表示のイメージ

左はいつも見慣れているUIKitの場合、右はSpriteKitの場合です。こうやってみるとUIKitとSpriteKitは似てますね。

今回登場するSpriteKitの要素

SKNode

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

SKScene

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

SKView

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

Particle

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

パーティクル名 画像 説明
Bokeh ボケ。ぼかし。ライフサイクルが終わって消えるまで、輝いてかすんでいく六角形のパーティクル。
Fire 暖炉やキャンプファイヤーで見られるような小さな火のエフェクト。
Fireflies Fireflies=ホタル。ライフサイクルが終わって消えるまで、ランダムに短い距離を動きながら輝いてかすんでいく短命で黄色のパーティクル。
Magic ライフサイクルが終わって消えるまで、ランダムに短い距離を動きながら輝いてかすんでいく短命で緑のパーティクル。
Rain スクリーンの上から下まで移動していく、形が変化しないパーティクル。各々のパーティクルは異なるスピードで動く。
Smoke スクリーンの下から上まで移動していく、大きな黒いパーティクル。動いていくにつれてゆっくりとフェードアウトしていく。※デフォルトは黒です。
Snow スクリーンの上から下まで移動していく、白くて放散する丸いパーティクル。各々のパーティクルは異なるサイズであり、異なるスピードで移動していく。
Spark エミッターを中心として360度方向へ短命で黄金の光を放つパーティクル。

作ってみる

完成物

起動するとTableViewが表示されます。各セルをタップするとその名前に合ったパーティクルが表示されます。

画面遷移した時に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というものを選択してください。

8種類の中から選択することができます。今回はFireしてみます。

これでNextをクリックして名前を付ければとりあえずパーティクルファイルの完成です。

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

すでにSpriteKitで使用できるパーティクルのパラメータ一覧【SpriteKit】爆発や雪などの表現ができる「Particle File」についてという記事が上がっているので説明は省略します。いろいろいじって遊んでみると面白いと思います。

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

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

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にしておいてください。

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にパーティクルが追加されます。

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

おまけ:あれ?パーティクルが表示されない!

いろいろいじっているとこんな自体に遭遇することがあると思います。

再現方法

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

原因

Xcodeのバグらしいです。stackoverflowに上がってました。

解決方法

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

参考になる資料