Apple Engine

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

iOS で SceneKit を試す(Swift 3) その4 - SceneKit の構造

SceneKit の要 Scene Graph

SceneKit はシーングラフというツリー状の構造で画面構成をつくり3DCGを表示してい る。

シーングラフの起点は SCNScene が起点となり、ルートノードがぶら下がる。
ルートノードから複数のチルドノードを設定する事ができて、各ノードにカメラやライトやオブジェクトを設定できる。

f:id:x67x6fx74x6f:20170530185528p:plain

上の図では、チルドノードにカメラとライトとジオメトリしか書かれていないが、 パーティクルなど他の要素ある。

また、チルドノードにチルドノードを追加し、さらに階層を深くもできる。

 

ゲームテンプレートと同じようなものをつくる

Single View Application のテンプレートから以前作成した SceneKit のゲームテンプレートの状態にコードで書いてみる。

 

下準備

Xcode を開き、新規プロジェクト作成(command + shift + N)

iOS の「Single View Application」を選択し「Next」ボタンを押す。

f:id:x67x6fx74x6f:20170530185701p:plain

Project Name に適当な名前を入れて「Next」ボタンを押す。

 

プロジェクトが作成されたら Main.storyboard の View のクラスを SCNView に変更。

f:id:x67x6fx74x6f:20170530185634p:plain

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

 

f:id:x67x6fx74x6f:20170530190203p:plain

 

カメラと球のジオメトリを配置する

まずは球のジオメトリを配置。 SCNScene() の下に以下のものを書いてみる。

let scene = SCNScene()

let sphere:SCNGeometry = SCNSphere(radius: 2)
let geometryNode = SCNNode(geometry: sphere)
scene.rootNode.addChildNode(geometryNode)

 

流れ

  • ジオメトリを設定する SCNGeometry に球を描画する SCNSphere から半径2の球を設定
  • チルドノードに先ほどのジオメトリを設定
  • シーンのルートノードに先ほどのチルドノードを設定

 

ビルドすると近すぎるが球が表示される。

f:id:x67x6fx74x6f:20170530190305p:plain

 

デフォルトのカメラ位置なので、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 まで移動する
  • シーンのルートノードに先ほどのチルドノードを設定

 

ビルドすると適度な位置に表示される。

f:id:x67x6fx74x6f:20170530190916p:plain  

球のジオメトリにライトで陰を落とす

カメラの上あたりに豆電球を灯してみたいので、 カメラの 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 をカメラの上へ移動する
  • シーンのルートノードに先ほどのチルドノードを設定

 

f:id:x67x6fx74x6f:20170530191601p:plain

 

表示は完了したが、せっかくなのでマテリアルを設定してみる

 

球のジオメトリにマテリアルを適応してみる

ジオメトリには firstMaterial というマテリアルを設定する項目がある。 マテリアルの主な見た目を変更する diffuse というのがあるので、 そこを変えて球のジオメトリに色をつけてみる。

以下のコードを let sphere:SCNGeometry〜 の後に追加

let sphere:SCNGeometry = SCNSphere(radius: 2)

sphere.firstMaterial?.diffuse.contents = UIColor.blue

 

f:id:x67x6fx74x6f:20170530190452p:plain

 

マテリアルにテクスチャを貼る

f:id:x67x6fx74x6f:20080922100958j:plain

サンプルのテクスチャ画像

 

適当な画像をプロジェクトに追加して、
先ほどのコードを以下のものに変更。

sphere.firstMaterial?.diffuse.contents = UIImage(named: "earth.jpg")

 

ビルドするとテクスチャが貼られる。

f:id:x67x6fx74x6f:20170530190518p:plain

 

球を回転させてみる

チルドノードを回転させるアニメーションを行う。 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 で設定したアニメーションを実行

 

ビルドすると回る

f:id:x67x6fx74x6f:20170531115919g:plain

 

画面をコントロールする

ゲームテンプレートでは、画面スワイプで回転、ピンチでズームなどが実装されていたので こちらもやってみます。

追加するのは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()
    }
}