[iOS 8] SceneKitで3Dゲームを作るー入門と概要編ー

ゲーム用3D描画ライブラリ

SceneKitは3Dを用いるゲーム用のフレームワークです。MacOSX向けにはすでに用意されていましたが今回のiOS8へのバージョンアップでiPhone向けのアプリケーションでも使えるようになりました。

本記事ではまず導入編としてSceneKitのサンプルプロジェクトのソースコードの解説をします。

次に概要としてSceneKitで利用可能なクラス一覧を見ていきます。

具体的なクラスの利用方法等は次回以降の解説で行っていきます。

サンプルプロジェクトへの導入

SceneKitでは3Dゲームに必要なオブジェクト描画、物理演算、ライティング、カメラ等のインターフェイスが提供されています。

まずはXcodeを開いてサンプルプロジェクトを作成してみましょう。

File->New->Project->iOS->Application->Gameを指定してNextボタンをクリックします。

scenekit1

次にプロジェクトの設定を行います。適当なプロジェクト名を付けて、GameTechnologyをSceneKitに設定しましょう。(LanguageはSwift)

scenekit2

作成完了です。

サンプルコード解説

作成されたプロジェクトファイルのGameViewController.swiftを見てみましょう。 もともとの英語コメントを外してコードの一つ一つに日本語のコメントを加えました。

GameViewController.swift

import UIKit
import QuartzCore
import SceneKit

class GameViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // シーンオブジェクトを作成。これ以降シーンオブジェクトのルートノードに
        // 子ノードを追加していくことでシーンにオブジェクトを追加していく。
        // ここではdaeファイル(3Dデータ)の読み込みを行っている。
        let scene = SCNScene(named: "art.scnassets/ship.dae")

        // シーンオブジェクトを撮影するためのノードを作成
        let cameraNode = SCNNode()
        // カメラノードにカメラオブジェクトを追加
        cameraNode.camera = SCNCamera()
        // シーンのルートノードにカメラノードを追加
        scene.rootNode.addChildNode(cameraNode)

        // カメラの位置を設定する。
        cameraNode.position = SCNVector3(x: 0, y: 0, z: 15)

        // シーンに光を与える為のノードを作成
        let lightNode = SCNNode()
        // ライトノードに光を表すライトオブジェクトを追加
        lightNode.light = SCNLight()
        // ライトオブジェクトの光属性を全方位への光を表す属性とする
        lightNode.light.type = SCNLightTypeOmni
        // ライトオブジェクトの位置を設定する
        lightNode.position = SCNVector3(x: 0, y: 10, z: 10)
        // シーンのルートノードにライトノードを追加
        scene.rootNode.addChildNode(lightNode)

        // シーンに環境光を与える為に環境光ノードを作成
        let ambientLightNode = SCNNode()
        // 環境光ノードにライトオブジェクトを追加
        ambientLightNode.light = SCNLight()
        // ライトオブジェクトの光属性を環境光を表す属性とする
        ambientLightNode.light!.type = SCNLightTypeAmbient
        // 環境光の色を設定する
        ambientLightNode.light!.color = UIColor.darkGrayColor()
        // シーンのルートノードに環境光ノードを追加
        scene.rootNode.addChildNode(ambientLightNode)

        // ノード名を指定してshipのノードをシーンから取得する
        let ship = scene.rootNode.childNodeWithName("ship", recursively: true)!

        // shipに対してアニメーションを設定する。ここではy軸を中心とした永続的な回転を設定している。
        ship.runAction(SCNAction.repeatActionForever(SCNAction.rotateByX(0, y: 2, z: 0, duration: 1)))

        // シーンを表示するためのビューへの参照を取得
        let scnView = self.view as SCNView

        // ビューのシーンに今までオブジェクトを追加してきたシーンを代入
        scnView.scene = scene

        // シーンに追加されたカメラを単純に操作できるようにする
        scnView.allowsCameraControl = true

        // ビューのフレーム数等のパフォーマンスに関わる統計情報をビューの下部に表示
        scnView.showsStatistics = true

        // ビューの背景色を黒に指定
        scnView.backgroundColor = UIColor.blackColor()

        // ビューがタップされた時のメソッドを指定してリコグナイザを作成し、ビューのジェスチャに追加する
        let tapGesture = UITapGestureRecognizer(target: self, action: "handleTap:")
        let gestureRecognizers = NSMutableArray()
        gestureRecognizers.addObject(tapGesture)
        if let existingGestureRecognizers = scnView.gestureRecognizers {
            gestureRecognizers.addObjectsFromArray(existingGestureRecognizers)
        }
        scnView.gestureRecognizers = gestureRecognizers
    }

    // ビューがタップされた時の挙動を規定する
    func handleTap(gestureRecognize: UIGestureRecognizer) {

        // シーンが追加されたビューへの参照を取得
        let scnView = self.view as SCNView

        // ビューのどの位置がタップされたかを読み込む
        let p = gestureRecognize.locationInView(scnView)
        // シーンのオブジェクトへのタップ結果をSCNHitTestResultの配列を返すことで取得する
        let hitResults = scnView.hitTest(p, options: nil)

        // タップ結果が一つでもあればアニメーション処理を行う
        if hitResults.count > 0 {

            // タップ結果の初めのオブジェクト(箱型オブジェクト)を取り出す
            let result: AnyObject! = hitResults[0]

            // 箱型オブジェクトのマテリアルを取り出す
            let material = result.node!.geometry.firstMaterial

            // 旧来のUIView AnimationAPIの使い方のようにしてマテリアルを赤く染めるアニメーションを設定する
            SCNTransaction.begin()
            SCNTransaction.setAnimationDuration(0.5)

            // アニメーションが終わった時の挙動を指定する
            SCNTransaction.setCompletionBlock {
                SCNTransaction.begin()
                SCNTransaction.setAnimationDuration(0.5)

                material.emission.contents = UIColor.blackColor()

                SCNTransaction.commit()
            }

            material.emission.contents = UIColor.redColor()

            SCNTransaction.commit()
        }
    }

    override func shouldAutorotate() -> Bool {
        return true
    }

    override func supportedInterfaceOrientations() -> Int {
        if UIDevice.currentDevice().userInterfaceIdiom == .Phone {
            return Int(UIInterfaceOrientationMask.AllButUpsideDown.toRaw())
        } else {
            return Int(UIInterfaceOrientationMask.All.toRaw())
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Release any cached data, images, etc that aren't in use.
    }

}

こちらのコードを実際に動かしてみます。

scenekit4 scenekit4

箱型オブジェクトがタップされると赤くなり、下の方に統計情報が表示されていることが分かります。

フレームワークで利用可能なクラス一覧

公式リファレンスに記載された各クラスの役割を大雑把に見てみましょう。

特に断りがない場合、クラスはNSObjectを継承します。子階層のクラスは親階層のクラスを継承します。

  • SCNAction: SCNNodeの振る舞いを表します。
  • SCNAnimationEvent: 指定された時間の中でのアニメーション処理を扱うクラスです。
  • SCNCamera: ノードに取り付け可能なカメラオブジェクトです。
  • SCNConstraint: ノード間の位置関係について束縛条件を表す抽象的なクラスです。このクラス自体は用いず、サブクラスを用いて具体的な束縛条件を指定します。
    • SCNIKConstraint: 逆運動学(参考リンク参照)に基づいた束縛条件を表すクラスです。
    • SCNLookAtConstraint: 束縛されたノードの方向が他方のノードに向けての方向を向くように自動的に調整してくれる束縛条件を表すクラスです。
    • SCNTransformConstraint: ノード間の位置関係についての束縛条件を表すクラスです。
  • SCNGeometry: ノードに取付可能な立体オブジェクトを表します。
    • SCNBox: 箱型の立体オブジェクトです。
    • SCNCapsule: 上下を半球に挟まれた円柱型の立体オブジェクトです。
    • SCNCone: 円錐型の立体オブジェクトです。
    • SCNCylinder: 円柱型の立体オブジェクトです。
    • SCNFloor: 無限の広さを持つ平面を表すオブジェクトです。
    • SCNPlane: 有限長の長方形を表すオブジェクトです。
    • SCNPyramid: ピラミッド型の立体オブジェクトです。
    • SCNShape: 二次元平面上のパスから作成される形を表すオブジェクトです。
    • SCNSphere: 球型の立体オブジェクトです。
    • SCNText: 二次元平面上でレンダリングされるテキストを表すオブジェクトです。
    • SCNTorus: ドーナツに似たトーラス型の立体オブジェクトです。
    • SCNTube: 円柱型オブジェクトの真ん中に穴が開いたチューブ型の立体オブジェクトです。
  • SCNGeometryElement: 三次元上の頂点がどのように連結して表面を構成するか記述します。カスタムオブジェクトを作成するときに用います。
  • SCNGeometrySource: 三次元上のの頂点が形成する表面のデータを表します。各表面のデータは三次元上の数個の頂点から作成されます。
  • SCNHitTestResult: SCNView上の点に3Dオブジェクトがあるかどうか判定する為に提供されるメソッドの結果を示すオブジェクトです。
  • SCNLevelOfDetail: 立体オブジェクト描画をオブジェクトの指定半径範囲内にいるかどうかで荒くしたり細かくしたりするためのオブジェクトです。
  • SCNLight: ノードに取付可能な光源を表すオブジェクトです。
  • SCNMaterial: 立体オブジェクトの表面につける光や影の効果を管理するクラスです。
  • SCNMaterialProperty: 立体オブジェクト表面につける効果につけるためのコンテンツ(色やテクスチャイメージなど)を表します。
  • SCNMorpher: 立体オブジェクトのモーフィングを行うためのクラスです。
  • SCNNode: シーングラフのノードを表します。各ノードには立体オブジェクトや光オブジェクト、カメラオブジェクトを取り付けられます。
  • SCNParticlePropertyController: ParticleSystemによってレンダリングされるパーティクル効果をCoreAnimationのインターフェイスを用いて調整する為のクラスです。
  • SCNParticleSystem: パーティクル効果を作成し、アニメーションさせ、描画する為のクラスです。パーティクルの挙動もこのクラスで指定できます。
  • SCNPhysicsBehavior: 物理シミュレーションによって一体、もしくは多体のオブジェクトの挙動を定義するための抽象的なクラスです。このクラス自体は用いず、サブクラスを用いて具体的な挙動を定義します。
    • SCNPhysicsBallSocketJoint: 2つのオブジェクトを軸でつなぎあわせた際の自由な回転挙動を定義します。三方向に関してどのような回転も許容されます。
    • SCNPhysicsHingeJoint: 2つのオブジェクトを軸でつなぎあわせた際の一つの回転軸のみに関しての回転挙動を定義します。一つの回転軸に関しての回転のみ許容されます。
    • SCNPhysicsSliderJoint: 2つのオブジェクトについてスライドさせる挙動を定義します。
    • SCNPhysicsVehicle: 車に車輪がついた場合に挙動を定義します。
  • SCNPhysicsBody: ノードに対して物理シミュレーションを適用する為に用意されたオブジェクトです。
  • SCNPhysicsContact: 2つのオブジェクトが接触する場合の挙動を記述するオブジェクトです。
  • SCNPhysicsField: 重力場や磁場など、オブジェクトに対して影響を与えうる場を定義します。
  • SCNPhysicsShape: PhysicsBody同士の衝突判定に用いられる剛体を表すオブジェクトです。
  • SCNPhysicsVehicleWheel: SCNPhysicsVehicleの挙動に用いる際に使われる車輪オブジェクトです。
  • SCNPhysicsWorld: シーン中の様々な物理挙動のシミュレートをしているオブジェクトです。ユーザは直接このインスタンスを生成しません。
  • SCNProgram: OpenGLを用いてレンダリングをカスタマイズできます。
  • SCNRenderer: SceneKitのシーンをOpenGL向けにレンダリングするクラスです。
  • SCNScene: 三次元空間のシーンを表すクラスです。
  • SCNSceneSource: シーンに紐ついたコンテンツをファイルからロードするタスクを管理するクラスです。
  • SCNSkinner: ノード間の階層の一部にアクセスして骨格のアニメーションを扱うために提供されるクラスです。
  • SCNTechnique: OpenGLのカスタムシェーダを使ったパスを利用したレンダリングを記述するためのクラスです。
  • SCNTransaction: シーンに対するバッチ処理のトランザクションを扱うクラスです。
  • SCNView: UIViewのサブクラスで、SceneKitのシーンを画面に反映します。

次回以降

今回はSceneKitの概要と導入に関して大雑把に説明しました。次回以降は各クラスの機能について詳細に見ていきます。

参考サイト