iOS で SceneKit を試す(Swift 3) その75 - SceneKit Particle System File と パーティクルシステム の emitterShape
SceneKit Particle System File (scnp) は Particle System 専用のファイル。
Scene Editor 上で、Particle System の確認ができる。
scnp ファイルは Particle System のファイルではあるが、scn では参照ができないため、コード場で行う。
scnp ファイルを Scene Editor で見た場合
パーティクルシステムの情報しかないので、Scene Graph View は存在せず、Inspector のも少なくなっている。
Scene Editor の下の部分は scn ファイルと異なっている。
ファイルの作成
Xcode のメニューバー、File > New > File… か、Command + N から「SceneKit Particle System File」を選ぶ。
テンプレートの選択が出るので必要なものを選択すると、scnp ファイルと使用する画像ファイルができあがる。
テンプレートの種類
以下のテンプレートが用意されている。
- Bokeh
- Confetti
- Rain
- Reactor
- Smoke
- Stars
Bokeh
円状のパーティクルの輪郭がボケながら消えてゆくテンプレート。
Attributes Inspector (Command + Option + 4) の Image の Color を変更すると色が変わる。
bokeh.png は 4x4 のアニメーションを並べたテクスチャで、例えば Initial frame の 0 にするとアニメーションが行われないため、ボケは行われず透過で消える。
Confetti
紙吹雪の動きを模したテンプレート。
テンプレートのままだと Scene Editor で表示されないため、 コード上で設定を行う。
Fire
炎を模したテンプレート
Rain
雨粒が下に落ちる雨を模したテンプレート。
重力の物理アニメーションで落としているの PhysicsWorld の重力値を変更する場合は注意。
Reactor
バーナーのような炎を模したテンプレート
Smoke
煙を模したアニメーション。
Stars
小さな星が奥から手前に移動し、宇宙空間を慰労しているかのように見せるテンプレート。
薄い黄色から薄い青にアニメーションさせているため、 Scene Editor で若干ズームアウトすると見た目が変わる。
任意の Shape にパーティクルを適応してみる。
普通にシーンにパーティクルを適応しても scn のパーティクルシステムとかわらないので emitterShape を使用してみる。
ひとまず、Xcode で SceneKit の Game テンプレート作成。
SCNP の作成
Command + N から「SceneKit Particle System File」を選美、Fire テンプレートを選択。
ここでは、ファイル名を Fire.scnp にしてプロジェクトに追加する。
GameViewController.swift の設定
GameViewController.swift を開く。
ship.scn を使用しないので、19行目を以下に変更。
let scene = SCNScene()
ship.scn を使用しなくなり、宇宙船のアニメーションはいらないので、 44、47行目を削除
let ship = scene.rootNode.childNode(withName: "ship", recursively: true)!
ship.runAction(SCNAction.repeatForever(SCNAction.rotateBy(x: 0, y: 2, z: 0, duration: 1)))
パーティクルの設定
GameViewController.swift の viewDidLoad() でビルトインジオメトリ SCNText でシェイプを作成。
そのシェイプからエミッターとしてパーティクルを吐き出す。
// シェイプ let textShape = SCNText(string: "つぶ", extrusionDepth: 1) textShape.flatness = 0.0 // パーティクルシステム let particle = SCNParticleSystem(named: "Fire.scnp", inDirectory: "") particle?.emitterShape = textShape let particleShapePosition = particle?.emitterShape?.boundingSphere.center // ノードにパーティクルシステムをピボット変更後して紐付ける let particleNode = SCNNode() particleNode.pivot = SCNMatrix4MakeTranslation(particleShapePosition!.x, particleShapePosition!.y, 0) particleNode.addParticleSystem(particle!) scene.rootNode.addChildNode(particleNode)
そのままでは大きいので、27行目の カメラの Z軸を 50 に変更
cameraNode.position = SCNVector3(x: 0, y: 0, z: 50)
ビルドすると以下のような感じになる
GameViewController.swift のコード
import UIKit import QuartzCore import SceneKit class GameViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let scene = SCNScene() let cameraNode = SCNNode() cameraNode.camera = SCNCamera() cameraNode.position = SCNVector3(x: 0, y: 0, z: 50) scene.rootNode.addChildNode(cameraNode) let lightNode = SCNNode() lightNode.light = SCNLight() lightNode.light!.type = .omni lightNode.position = SCNVector3(x: 0, y: 10, z: 10) scene.rootNode.addChildNode(lightNode) let ambientLightNode = SCNNode() ambientLightNode.light = SCNLight() ambientLightNode.light!.type = .ambient ambientLightNode.light!.color = UIColor.darkGray scene.rootNode.addChildNode(ambientLightNode) let scnView = self.view as! SCNView scnView.scene = scene scnView.allowsCameraControl = true scnView.showsStatistics = true scnView.backgroundColor = UIColor.black // シェイプ let textShape = SCNText(string: "つぶ", extrusionDepth: 1) textShape.flatness = 0.0 // パーティクルシステム let particle = SCNParticleSystem(named: "Fire.scnp", inDirectory: "") particle?.emitterShape = textShape let particleShapePosition = particle?.emitterShape?.boundingSphere.center // ノードにパーティクルシステムをピボット変更後して紐付ける let particleNode = SCNNode() particleNode.pivot = SCNMatrix4MakeTranslation(particleShapePosition!.x, particleShapePosition!.y, 0) particleNode.addParticleSystem(particle!) scene.rootNode.addChildNode(particleNode) } override var shouldAutorotate: Bool { return true } override var prefersStatusBarHidden: Bool { return true } override var supportedInterfaceOrientations: UIInterfaceOrientationMask { if UIDevice.current.userInterfaceIdiom == .phone { return .allButUpsideDown } else { return .all } } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } }
今回はここまで。