WWDC 2017 の SceneKit サンプル Fox 2 を調べる その26
GameController クラスのセットアップ関数を見てゆく。 今回は敵キャラとパーティクルの設定
setupEnemies()
敵の設定。
こちらは以前に紹介しているので割愛。
loadParticleSystems(atPath path: String)
パーティクルセットアップの際に .scn(シーンファイル) や .scnp(パーティクルシステムファイル) から読み込みを容易にするための関数。ファイルパスを指定し呼び出すとパーティクルシステムが入った配列を返す。
引数 path でファイルパスを文字列でとって、ファイル名とディレクトリを使用し、拡張子がパーティクルシステムファイルの場合はそのまま1つのパーティクルが入った配列として返し、シーンファイルの場合はしシーンを全て調べ、見つかったパーティクルを全て配列に入れ返す。
func loadParticleSystems(atPath path: String) -> [SCNParticleSystem] {
let url = URL(fileURLWithPath: path)
let directory = url.deletingLastPathComponent()
let fileName = url.lastPathComponent
let ext: String = url.pathExtension
if ext == "scnp" {
return [SCNParticleSystem(named: fileName, inDirectory: directory.relativePath)!]
} else {
var particles = [SCNParticleSystem]()
let scene = SCNScene(named: fileName, inDirectory: directory.relativePath, options: nil)
scene!.rootNode.enumerateHierarchy({(_ node: SCNNode, _ _: UnsafeMutablePointer<ObjCBool>) -> Void in
if node.particleSystems != nil {
particles += node.particleSystems!
}
})
return particles
}
}
setupParticleSystem()
パーティクルのセットアップ関数。
particleSystems に ParticleKind の enum を添字として、上の loadParticleSystems 関数からパーティクルを格納する。
func setupParticleSystem() {
particleSystems[ParticleKind.collect.rawValue] = loadParticleSystems(atPath: "Art.scnassets/particles/collect.scnp")
particleSystems[ParticleKind.collectBig.rawValue] = loadParticleSystems(atPath: "Art.scnassets/particles/key_apparition.scn")
particleSystems[ParticleKind.enemyExplosion.rawValue] = loadParticleSystems(atPath: "Art.scnassets/particles/enemy_explosion.scn")
particleSystems[ParticleKind.keyApparition.rawValue] = loadParticleSystems(atPath: "Art.scnassets/particles/key_apparition.scn")
particleSystems[ParticleKind.unlockDoor.rawValue] = loadParticleSystems(atPath: "Art.scnassets/particles/unlock_door.scn")
}
func setupPlatforms()
閉じ込められている仲間の小屋の前の溶岩にある動く橋(mobilePlatform)のアニメーションとパーティクル(particles_platform)の設定。
SCNAction で橋を動かし、パーティクルの orientationDirection を Y 軸 1 の方向に向かせるようにしている。
func setupPlatforms() {
let PLATFORM_MOVE_OFFSET = Float(1.5)
let PLATFORM_MOVE_SPEED = Float(0.5)
var alternate: Float = 1
scene!.rootNode.enumerateHierarchy({(_ node: SCNNode, _ _: UnsafeMutablePointer<ObjCBool>) -> Void in
if node.name == "mobilePlatform" && !node.childNodes.isEmpty {
node.simdPosition = simd_float3(
node.simdPosition.x - (alternate * PLATFORM_MOVE_OFFSET / 2.0), node.simdPosition.y, node.simdPosition.z)
let moveAction = SCNAction.move(by: SCNVector3(alternate * PLATFORM_MOVE_OFFSET, 0, 0),
duration: TimeInterval(1 / PLATFORM_MOVE_SPEED))
moveAction.timingMode = .easeInEaseOut
node.runAction(SCNAction.repeatForever(SCNAction.sequence([moveAction, moveAction.reversed()])))
alternate = -alternate
node.enumerateChildNodes({ (_ child: SCNNode, _ _: UnsafeMutablePointer<ObjCBool>) in
if child.name == "particles_platform" {
child.particleSystems?[0].orientationDirection = SCNVector3(0, 1, 0)
}
})
}
})
}
mobilePlatform

particles_platform

次回は GameController クラスのオーディオ設定を見てゆく。