iOS で SceneKit を試す(Swift 3) その4 - SceneKit の構造
SceneKit の要 Scene Graph
SceneKit はシーングラフというツリー状の構造で画面構成をつくり3DCGを表示してい る。
シーングラフの起点は SCNScene が起点となり、ルートノードがぶら下がる。
ルートノードから複数のチルドノードを設定する事ができて、各ノードにカメラやライトやオブジェクトを設定できる。
上の図では、チルドノードにカメラとライトとジオメトリしか書かれていないが、 パーティクルなど他の要素ある。
また、チルドノードにチルドノードを追加し、さらに階層を深くもできる。
ゲームテンプレートと同じようなものをつくる
Single View Application のテンプレートから以前作成した SceneKit のゲームテンプレートの状態にコードで書いてみる。
下準備
Xcode を開き、新規プロジェクト作成(command + shift + N)
iOS の「Single View Application」を選択し「Next」ボタンを押す。
Project Name に適当な名前を入れて「Next」ボタンを押す。
プロジェクトが作成されたら Main.storyboard の View のクラスを SCNView に変更。
Storyboard の Document Outline が表示されていない場合は、
左下のアイコンをクリックするか、上部メニューの「Editor」の「Show Document Outline」をクリック。
コードを編集する
Xcode 右のナビゲーターから ViewController.swift を選択し、
import UIKit の下に import SceneKit を書く。
import UIKit import SceneKit
SCNView を設定する
viewDidLoad() の { } 内に以下の命令を書く。
let scnView = self.view as! SCNView scnView.backgroundColor = UIColor.black
定数 scnView を Main.storyboard で変更した SCNView を設定して背景を黒に変更。
command + R でビルドすると透過したいた SCNView の背景が黒になる。
シーンを配置する
定数で SCNScene を初期化し、SCNView の scene に設定。 まだ何もないので SCNView から統計情報を表示させてみる。
先ほど書いた部分を以下のものに変更
let scene = SCNScene() let scnView = self.view as! SCNView scnView.backgroundColor = UIColor.black scnView.scene = scene scnView.showsStatistics = true
カメラと球のジオメトリを配置する
まずは球のジオメトリを配置。 SCNScene() の下に以下のものを書いてみる。
let scene = SCNScene() let sphere:SCNGeometry = SCNSphere(radius: 2) let geometryNode = SCNNode(geometry: sphere) scene.rootNode.addChildNode(geometryNode)
流れ
- ジオメトリを設定する SCNGeometry に球を描画する SCNSphere から半径2の球を設定
- チルドノードに先ほどのジオメトリを設定
- シーンのルートノードに先ほどのチルドノードを設定
ビルドすると近すぎるが球が表示される。
デフォルトのカメラ位置なので、scene.rootNode.addChildNode〜 の下に、以下のコードを追加変更。
let cameraNode = SCNNode() cameraNode.camera = SCNCamera() cameraNode.position = SCNVector3(x: 0, y: 0, z: 10) scene.rootNode.addChildNode(cameraNode)
流れ
- チルドノードを設定
- チルドノードの camera プロパティに、カメラとなる SCNCamera を設定
- そのままだと、xyz 軸が 0 で球が見えないため、チルドノードを SCNVector3 を使い手前 10 まで移動する
- シーンのルートノードに先ほどのチルドノードを設定
ビルドすると適度な位置に表示される。
球のジオメトリにライトで陰を落とす
カメラの上あたりに豆電球を灯してみたいので、 カメラの scene.rootNode.addChildNode〜 の後に以下のコードを追加。
let lightNode = SCNNode() lightNode.light = SCNLight() lightNode.light!.type = .omni lightNode.position = SCNVector3(x: 0, y: 10, z: 10) scene.rootNode.addChildNode(lightNode)
流れ
- チルドノードを設定
- チルドノードの light プロパティに、ライトとなる SCNLight を設定
- ライトの種類を球状のライトに設定
- チルドノードを SCNVector3 をカメラの上へ移動する
- シーンのルートノードに先ほどのチルドノードを設定
表示は完了したが、せっかくなのでマテリアルを設定してみる
球のジオメトリにマテリアルを適応してみる
ジオメトリには firstMaterial というマテリアルを設定する項目がある。 マテリアルの主な見た目を変更する diffuse というのがあるので、 そこを変えて球のジオメトリに色をつけてみる。
以下のコードを let sphere:SCNGeometry〜 の後に追加
let sphere:SCNGeometry = SCNSphere(radius: 2) sphere.firstMaterial?.diffuse.contents = UIColor.blue
マテリアルにテクスチャを貼る
サンプルのテクスチャ画像
適当な画像をプロジェクトに追加して、
先ほどのコードを以下のものに変更。
sphere.firstMaterial?.diffuse.contents = UIImage(named: "earth.jpg")
ビルドするとテクスチャが貼られる。
球を回転させてみる
チルドノードを回転させるアニメーションを行う。 Core Animation など方法はあるが、今回は SCNAction を使用。
以下のコードをジオメトリのチルドノードの後に追加。
let geometryNode = SCNNode(geometry: sphere) geometryNode.runAction(SCNAction.repeatForever(SCNAction.rotateBy(x: 0, y: 2, z: 0, duration: 2)))
流れ
- SCNAction.rotateBy で2秒間に1回するアニメーションを設定
- 先ほどのものを repeatForever で無限ループ
- チルドノードの runAction で設定したアニメーションを実行
ビルドすると回る
画面をコントロールする
ゲームテンプレートでは、画面スワイプで回転、ピンチでズームなどが実装されていたので こちらもやってみます。
追加するのは1行。
scnView の宣言の後に追加を行う。
scnView.allowsCameraControl = true
まとめ
ひとまず、どのように SceneKit が構成されて表示されているが、
なんとなく掴めたかと思われる。
次からはこれらの機能の深堀と、まだ使っていない機能の紹介をする。
UIViewController の全コード
import UIKit import SceneKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() let scene = SCNScene() let sphere:SCNGeometry = SCNSphere(radius: 2) sphere.firstMaterial?.diffuse.contents = UIImage(named: "earth.jpg") let geometryNode = SCNNode(geometry: sphere) geometryNode.runAction(SCNAction.repeatForever(SCNAction.rotateBy(x: 0, y: 2, z: 0, duration: 2))) scene.rootNode.addChildNode(geometryNode) let lightNode = SCNNode() lightNode.light = SCNLight() lightNode.light!.type = .omni lightNode.position = SCNVector3(x: 0, y: 10, z: 10) scene.rootNode.addChildNode(lightNode) let cameraNode = SCNNode() cameraNode.camera = SCNCamera() cameraNode.position = SCNVector3(x: 0, y: 0, z: 10) scene.rootNode.addChildNode(cameraNode) let scnView = self.view as! SCNView scnView.backgroundColor = UIColor.black scnView.scene = scene scnView.showsStatistics = true scnView.allowsCameraControl = true } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } }