Apple Engine

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

iOS で SceneKit を試す(Swift 3) その53 - SceneKit の Point Of View

これまで紹介した SceneKit のカメラは、コードで設定するか、scn ファイル内のカメラで設定していた。

SceneKit の SCNView(SCNSceneRenderer)には pointOfView というプロパティがあり、 これに SCNCamera を設定しているノードを設定するとカメラに設定することができる。

わかりやすい例だと、SCNView で allowsCameraControl を true に設定した時。
画面の操作が行われると pointOfView がコントロール可能なカメラに変更される。

ちなみに iOS 11 では SCNCameraController で allowsCameraControl の振る舞いを変更できる。

 

利用用途

allowsCameraControl = true の際、ダブルタップで初期位置へスムースにアニメーションが行われる。

これと同様に複数のカメラ間での移動や回転のアニメーションを pointOfView を使用して行うことができる。

 

動作例

f:id:x67x6fx74x6f:20170807191038g:plain

 

コードを書いてみる

方法としては、異なる SCNCamera のノードを2つ作成し、pointOfView に設定したものをアニメーションさせる。

いつも通り iOS の Game テンプレートでプロジェクトを作成し、 GameViewController.swift のカメラの記述を削除。

シーン上にカメラを配置し、タッチイベントで pointOfView をアニメーションしている。

 

GameViewController.swift

import UIKit
import QuartzCore
import SceneKit

class GameViewController: UIViewController {
    
    let cameraNode1: SCNNode = SCNNode()
    let cameraNode2: SCNNode = SCNNode()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // create a new scene
        let scene = SCNScene(named: "art.scnassets/ship.scn")!
        
        // create and add a light to the scene
        let lightNode = SCNNode()
        lightNode.light = SCNLight()
        lightNode.light!.type = .omni
        lightNode.position = SCNVector3(x: 0, y: 10, z: 10)
        scene.rootNode.addChildNode(lightNode)
        
        // create and add an ambient light to the scene
        let ambientLightNode = SCNNode()
        ambientLightNode.light = SCNLight()
        ambientLightNode.light!.type = .ambient
        ambientLightNode.light!.color = UIColor.darkGray
        scene.rootNode.addChildNode(ambientLightNode)
        
        // retrieve the ship node
        let ship = scene.rootNode.childNode(withName: "ship", recursively: true)!
        
        // animate the 3d object
        ship.runAction(SCNAction.repeatForever(SCNAction.rotateBy(x: 0, y: 2, z: 0, duration: 1)))
        
        
        let scnView = self.view as! SCNView
        
        // set the scene to the view
        scnView.scene = scene
        
        // allows the user to manipulate the camera
        scnView.allowsCameraControl = true
        
        // show statistics such as fps and timing information
        scnView.showsStatistics = true
        
        // configure the view
        scnView.backgroundColor = UIColor.black
        
        // --- 追加分 ---
        
        cameraNode1.name = "camera1"
        cameraNode1.camera = SCNCamera()
        cameraNode1.position = SCNVector3(x: 0, y: 0, z: 15)
        
        scene.rootNode.addChildNode(cameraNode1)
        
        cameraNode2.name = "camera2"
        cameraNode2.camera = SCNCamera()
        cameraNode2.position = SCNVector3(x: 0, y: 20, z: 0)
        cameraNode2.eulerAngles = SCNVector3(x: Float.pi * -0.5, y: 0, z: 0)
        
        scene.rootNode.addChildNode(cameraNode2)
        
        scnView.pointOfView = cameraNode1
        
        // add a tap gesture recognizer
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
        scnView.addGestureRecognizer(tapGesture)
    }
    
    func handleTap(_ gestureRecognize: UIGestureRecognizer) {
        // retrieve the SCNView

        let scnView = self.view as! SCNView
        
        let cameraNode:SCNNode!
        
        if scnView.pointOfView?.name == "camera1" {
            cameraNode = scnView.scene?.rootNode.childNode(withName: "camera2", recursively: true)
        } else {
            cameraNode = scnView.scene?.rootNode.childNode(withName: "camera1", recursively: true)
        }
        
        SCNTransaction.begin()
        SCNTransaction.animationDuration = 4.0
        
        scnView.pointOfView = cameraNode

        SCNTransaction.commit()
    }
    
    override var shouldAutorotate: Bool {
        return true
    }
    
    override var prefersStatusBarHidden: Bool {
        return true
    }
    
    override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        if UIDevice.current.userInterfaceIdiom == .phone {
            return .allButUpsideDown
        } else {
            return .all
        }
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Release any cached data, images, etc that aren't in use.
    }

}

 

注意点

シーン上にカメラを配置していないとアニメーションは行われず、瞬時にカメラが変わる。

また、iOS シミュレーターではアニメーションが微妙な動きをする場合があるので注意。

 

今回はここまで