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("タップされました")
}
}