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())
}
}