Apple Engine

Apple, iPhone, iOS, その周辺のことについて

iOS で SceneKit を試す(Swift 3) その76 - パーティクルの障害物判定と新しいエミッターの派生

SceneKit の パーティクルシステムは他のゲームエンジン同様に、パーティクルの障害物判定と新しいパーティクルのエミッター派生させることができる。

パーティクルの処理自体軽いわけではないので多用は禁物。

 

今回の流れ

  • 雨のパーティクルを落とし床に衝突
  • 衝突の際にパーティクルの削除
  • 削除した場所から新しいパーティクルのエミッター(パーティクルシステム)を派生させる

 

プロジェクトファイルを作る

いつも通り、Xcode の SceneKit の Game テンプレートを作成。

GameViewController.swift を開き、viewDidLoad() を編集する。
今回 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)))

 

準備完了。

 

床のジオメトリを追加する

物理シミュレーションを行うため、PhysicsBody で Static に設定。 viewDidLoad() に以下のコードを書く。

let floor = SCNFloor()
floor.firstMaterial?.diffuse.contents = UIColor.darkGray
floor.reflectivity = 0.0

let floorNode = SCNNode(geometry: floor)
floorNode.physicsBody = SCNPhysicsBody(type: .static, shape: nil)
scene.rootNode.addChildNode(floorNode)

 

SCNP ファイルを作成、編集する

2つの SCNP ファイルを作成する

Command + N から Resource の項目の「SceneKit Particle System File」を選択し「Next」ボタンを押下。
テンプレートの選択が出るので「Rain」を選択し、ここでは Rain.scnp で保存。

さらにサブエミッターとして「Rain」のテンプレートでもうひとつ scnp ファイルを作成。
名前を Splash.scnp で保存。

 

Splash.scnp 編集する

以下に変更すると5つのパーティクルが Z 軸方向に飛ぶようになる。

 

パラメーター名 設定値
Birth rate 5
Direction X:0 Y:0 Z:1
Speading angle 50°
Shape Point
Life span 1
Linner velocity 2
Stretch factor 0.1

 

f:id:x67x6fx74x6f:20170822182744p:plain

 

コードを書いてパーティクルを表示してみる

行うことは簡単で SCNParticleSystem(named:〜 でパーティクルを2つ設定する。
元となるパーティクルシステムをノードに適応。

SCNParticleSystem のプロパティ、colliderNodes で障害物を設定して、 particleDiesOnCollision を true にし設定した障害物に衝突するとパーティクルを消す。

そして、systemSpawnedOnCollision でぶつかった際に、派生させるパーティクルを設定する。

 

以下コード。

let emitter1 = SCNParticleSystem(named: "Rain.scnp", inDirectory: "")
let emitter2 = SCNParticleSystem(named: "Splash.scnp", inDirectory: "")
emitter2?.loops = false

let particleNode = SCNNode()
particleNode.position = SCNVector3(0,30,0)
particleNode.addParticleSystem(emitter1!)

emitter1?.colliderNodes = [floorNode]
emitter1?.particleDiesOnCollision = true

emitter1?.systemSpawnedOnCollision = emitter2

scene.rootNode.addChildNode(particleNode)

emitter2?.loops と emitter1?.particleDiesOnCollision をコードで書いているが、
Rain.scnp や Splash.scnp を Scene Editor で開き Attributes Inspector からでも設定変更が可能。

 

わかりづらいが地面に落ちると新しいパーティクルが作成されている。

f:id:x67x6fx74x6f:20170822183259g:plain

 

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: 5, z: 15)
        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 floor = SCNFloor()
        floor.firstMaterial?.diffuse.contents = UIColor.darkGray
        floor.reflectivity = 0.0
        
        let floorNode = SCNNode(geometry: floor)
        floorNode.physicsBody = SCNPhysicsBody(type: .static, shape: nil)
        scene.rootNode.addChildNode(floorNode)
        
        
        // パーティクルの設定
        let emitter1 = SCNParticleSystem(named: "Rain.scnp", inDirectory: "")
        let emitter2 = SCNParticleSystem(named: "Splash.scnp", inDirectory: "")
        emitter2?.loops = false
        
        let particleNode = SCNNode()
        particleNode.position = SCNVector3(0,30,0)
        particleNode.addParticleSystem(emitter1!)
        
        emitter1?.colliderNodes = [floorNode]
        emitter1?.particleDiesOnCollision = true
        
        emitter1?.systemSpawnedOnCollision = emitter2
        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()
    }
}

  

今回はここまで。