iOS で SceneKit を試す(Swift 3) その5 - シーンエディタを使用しない空のテンプレートをつくる
今後、コードサンプルで使用するためのものを作成する。
シーングラフのルートノードに追加するもの。
- オムニライト (配置した位置から全ての方向を照らす光源)
- アンビエントライト (位置に関係なく画面全体を照らす環境光。ジオメトリに陰は落ちない)
- カメラ
また、シーンにはタップした際の処理を入れる。
プロジェクトファイルを作る
前回同様にシングルページを作成し、UIViewController の Storyboard で設定している UIView を SCNView に変更する。
Storyboard の修正が完了されたら、SCNScene のサブクラスを作成する。
新規作成 (command + N) から Swift File を選択し「Next」をクリック。
今回は「GameScene.swift」と名前をつけて「Next」をクリック。
GameScene.swift を修正する
import Foundation を import SceneKit に変更して以下の空のクラスを作る。
import SceneKit class GameScene: SCNScene { }
ViewController.swift を修正する
まず、import UIKit の下に import SceneKit を書く。
import UIKit import SceneKit
override func viewDidLoad() の中身に以下のものを追加。
シーンを先ほど設定したものにしているだけで、以前のものと変わらない。
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 }
ビルドすると画面下に統計情報が表示される。
もう一度 GameScene.swift を修正する
初期化時に自前でつくった setUpScene() を呼び、setUpScene() でシーングラプを作成する。
初期化(イニシャライズ)
init() でオーバーライドして初期化しようと思うが、
SCNScene では必須のイニシャライザがあるのでそれも書く。
クラスの中に以下のものを書く。
class GameScene: SCNScene { override init() { super.init() self.setUpScene() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } }
シーングラフ
init() で setUpScene() の関数を呼んでいるので、それも書く。
シーングラフは上記のものだが、試しにビルドした際なのも表示されないの球を置いてみる。
問題なく表示されるのであれば球は消しても構わない。
func setUpScene() { // 球 let sphere:SCNGeometry = SCNSphere(radius: 2) let geometryNode = SCNNode(geometry: sphere) self.rootNode.addChildNode(geometryNode) // オムニ ライト let lightNode = SCNNode() lightNode.light = SCNLight() lightNode.light!.type = .omni lightNode.position = SCNVector3(x: 0, y: 10, z: 10) self.rootNode.addChildNode(lightNode) // アンビエント ライト let ambientLightNode = SCNNode() ambientLightNode.light = SCNLight() ambientLightNode.light!.type = .ambient ambientLightNode.light!.color = UIColor.darkGray self.rootNode.addChildNode(ambientLightNode) // カメラ let cameraNode = SCNNode() cameraNode.camera = SCNCamera() cameraNode.position = SCNVector3(x: 0, y: 0, z: 10) self.rootNode.addChildNode(cameraNode) }
もう一度 ViewController.swift を修正する
画面タップ時の処理を付加する。
viewDidLoad() の SCNView で設定の下に以下のものを書く
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:))) scnView.addGestureRecognizer(tapGesture)
ジェスチャーレコグナイザーの呼び出し先の関数を作成する。
didReceiveMemoryWarning() の後に以下の関数を作成する
func handleTap(_ gestureRecognize: UIGestureRecognizer) { print("タップされました") }
問題がなければ、画面をタップするたびにデバッグログに「タップされました」が表示されるはず。
本来は、画面タップした際 SCNView の中から HitTest を呼ぶ。
Unity などと同様にタップからシーン内にレイキャスト(見えない光の線)を飛ばし、ぶつかったチルドノードを調べていく形となる。
とりあえず、テンプレートは完成したので、
今回はここまで。
全コード
GameScene.swift
import SceneKit class GameScene: SCNScene { override init() { super.init() self.setUpScene() } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } func setUpScene() { // オムニ ライト let lightNode = SCNNode() lightNode.light = SCNLight() lightNode.light!.type = .omni lightNode.position = SCNVector3(x: 0, y: 10, z: 10) self.rootNode.addChildNode(lightNode) // アンビエント ライト let ambientLightNode = SCNNode() ambientLightNode.light = SCNLight() ambientLightNode.light!.type = .ambient ambientLightNode.light!.color = UIColor.darkGray self.rootNode.addChildNode(ambientLightNode) // カメラ let cameraNode = SCNNode() cameraNode.camera = SCNCamera() cameraNode.position = SCNVector3(x: 0, y: 0, z: 10) self.rootNode.addChildNode(cameraNode) } }
ViewController.swift
import UIKit import SceneKit class ViewController: UIViewController { 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 tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:))) scnView.addGestureRecognizer(tapGesture) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } func handleTap(_ gestureRecognize: UIGestureRecognizer) { print("タップされました") } }