Apple Engine

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

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 だよー。

ちなみにシミュレーターではドローコールが増え続けるので注意。

f:id:x67x6fx74x6f:20170613193335p:plain

 

また、複製にはもう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())

    }
}