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 クラスのオーディオ設定を見てゆく。