iOS で SceneKit を試す(Swift 3) その10 - ノードをコピーして端末負荷を下げる
前回のサンプルでは重大なミスをしており、
タップする度にルートノードに新規の球体のノードが追加されている。
そのためドローコールがタップ毎に増えており、
簡素なジオメトリとはいえ、あまり端末に優しくない。
修正してみる
SCNNode には clone() というメソッドがあり、
一度作成したノードをコピーして使用することができる。
画面に描画されるためコピーしても全体のポリゴン数は増えるが、
ドローコールが増えないため、ハードウェアには優しいものとなる。
ひとまずタップの動作をなくしてみる
func handleTap(_ gestureRecognize: UIGestureRecognizer) の
カッコの中をひとまず空にしてみる
func handleTap(_ gestureRecognize: UIGestureRecognizer) { }
グローバル変数としてノードを設定してみる
今回は viewDidLoad で球体を設定し、handleTap の中で球体をコピーするので、
まずはグローバル変数として球体のノードを設定。
viewDidLoad の上あたりに以下のコードを入れる。
var ballNode:SCNNode!
球体のジオメトリを設定してみる
viewDidLoad の中の
scnView.allowsCameraControl = true の下あたりで球を設定する。
せっかくなので少し待ってジオメトリ消すアニメーションを入れてみたり。
// ジオメトリ let ball = SCNSphere(radius: 0.5) ballNode = SCNNode(geometry: ball) ballNode.position.y = 4 // アニメーション ballNode.runAction(SCNAction.sequence([ SCNAction.wait(duration: 22, withRange:4), SCNAction.fadeOut(duration: 1), SCNAction.removeFromParentNode() ])) // 追加分 let physicsBall = SCNPhysicsShape(node: ballNode, options: nil) ballNode.physicsBody = SCNPhysicsBody(type: .dynamic, shape: physicsBall)
タップの中身をつくる
以下を handleTap の中へコピー。
球体のノードを渡す際 ballNode.clone() で複製してい addChildNode に渡している。
let scnView = self.view as! SCNView scnView.scene?.rootNode.addChildNode(ballNode.clone())
そして実機でビルド。
タップしまくって球体増やしてもドローコールが 4 だよー。
ちなみにシミュレーターではドローコールが増え続けるので注意。
また、複製にはもう1つ flattenedClone() というものがあり
複雑に階層化したチルドノードを最適化するやつがあるので
いつか紹介しようとは思っている。
今回はここまで
修正した ViewController.swift の全コード
import UIKit import SceneKit class ViewController: UIViewController { var ballNode:SCNNode! override func viewDidLoad() { super.viewDidLoad() // シーン設定 let scene = GameScene() // SCNView 設定 let scnView = self.view as! SCNView scnView.backgroundColor = UIColor.black scnView.scene = scene scnView.showsStatistics = true scnView.allowsCameraControl = true // ジオメトリ let ball = SCNSphere(radius: 0.5) ballNode = SCNNode(geometry: ball) ballNode.position.y = 4 ballNode.runAction(SCNAction.sequence([ SCNAction.wait(duration: 22, withRange:4), SCNAction.fadeOut(duration: 1), SCNAction.removeFromParentNode() ])) // 追加分 let physicsBall = SCNPhysicsShape(node: ballNode, options: nil) ballNode.physicsBody = SCNPhysicsBody(type: .dynamic, shape: physicsBall) // タップジェスチャー let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:))) scnView.addGestureRecognizer(tapGesture) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } func handleTap(_ gestureRecognize: UIGestureRecognizer) { let scnView = self.view as! SCNView scnView.scene?.rootNode.addChildNode(ballNode.clone()) } }