post Image
[Swift]ARKitでドラクエっぽいものをつくってみた

Swift その2 Advent Calendar 2017で、本当は「セル上に簡単な広告実装と、複数広告の工夫」的なことを書く予定だったのですが、
ハッカソン参加で忙しくARを触っていたため怠ってしまいました、、、
ということで内容変更してARの記事を載せます。

前書き

メルカリの1dayARハッカソン(Mercari AR Hackathon #0)に出場してきました!

以下、作品(リリースはしていません)
Image uploaded from iOS.png

1dayといっても事前準備とかOKなんですが、、笑
せっかくARKitに触れたので、作ったものを説明しつつコード記録も込めて書いておきます。

前提

ARKit

これを読んでおけば大体なんとなくわかります。
ARKitのまとめ
ARKitを扱う際の心構えとTips
Apple 純正の AR フレームワーク「ARKit」について理解する

SCNNode+Extension

ARには欠かせないSCNNodeをあつかうためのextensionを作成しました。
これがあれば、チェーンでどんどん書いていけるかと。

extension SCNNode { 

    // 重力設定
    public func setPhysics<T>(with object: T, and type: SCNPhysicsBodyType = .kinematic) -> SCNNode where T: SCNGeometry {
        let shape = SCNPhysicsShape(geometry: object, options: nil)
        self.physicsBody = SCNPhysicsBody(type: type, shape: shape)
        return self
    }

    // 座標設定
    public func setPosition(from position: SCNVector3) -> SCNNode {
        self.position = position
        return self
    }

    // 回転設定
    public func setRotation(from rotation: SCNVector4) -> SCNNode {
        self.rotation = rotation
        return self
    }

    // サイズ設定
    public func setScale(from scale: SCNVector3) -> SCNNode {
        self.scale = scale
        return self
    }

    // ノード生成
    public func addNode(to scene: inout SCNScene) {
        scene.rootNode.addChildNode(self)
    }

    // ノード削除
    public func removeNode(to scene: inout SCNScene) {
        scene.rootNode.removeFromParentNode()
    }

}

SCNParticleSystem+Extension

簡単なアニメーション演出が行えるパーティクル用のextensionを作成しました。

extension SCNParticleSystem {

    // ノード衝突時に死亡判定(寿命的なもの)をつける
    public func setDiedOnCollision<N>(for nodes: [N]) -> SCNParticleSystem where N: SCNNode {
        self.colliderNodes = nodes
        self.particleDiesOnCollision = true
        return self
    }

    // ノードの生成
    public func initNode() -> SCNNode {
        let node = SCNNode()
        node.addParticleSystem(self)
        return node
    }
}

上記いずれもそのうち説明に出てくるので、使用例は割愛。

ARクエスト

ハッカソンのアイデアが浮かばず、、、ドラクエのAR版的なものを作りました笑
今回はUnityとかつかわず、すべてARKitのみで作成しています。

歩き回るとモンスターがランダムで出てくるので、それをタップなりスワイプなりで倒していく感じになります。
細々した機能はたくさんあるのですが、全部はかけないので抜粋して掲載していきます。

①開始画面/職業選択画面

スクリーンショット 2017-12-17 17.36.24.png

スクリーンショット 2017-12-17 17.36.44.png

この部分は普通のStoryboardUIViewで作成しています。
職業を選択できるようにしました。フォントもドラクエ風のものを適応しています。

②戦闘画面

Image uploaded from iOS (4).png

モンスターを出現させるために、平面認識が開始されます。

認識の仕方は等々は以下を参考にしています。
ARKitで豆腐作り
[Swift4]ARKitで球体をランダムに描画する
公式ドキュメントを追いながらARKitを試してみよう

⑤モンスターの出現

Image uploaded from iOS (3).png

歩いている方向にモンスターが出現するように設定しています。
モンスターに関しては、フリーの3Dモデル(daeファイル)を使用しています。
(3Dモデルはぐぐればたくさん出てきます。)

以下、方法例として

// モンスター定義
enum MonsterType: UInt32 {
    case dragon
    case spider
    case wolf

    // ファイル名
    private var fileName: String {
        switch self {
        case .dragon: return "dragon"
        case .spider: return "spider"
        case .wolf: return "wolf"
        }
    }

    // daeファイルの読み込み
    private var bundleURL: URL? {
        return Bundle.main.url(forResource: fileName, withExtension: "dae")
    }

    // モンスターの生成
    var node: SCNNode? {

        guard
            let url = bundleURL,
            let scene = try? SCNScene(url: url, options: nil)
            else { return nil }

        // daeの3Dモデルは複数パーツで構成されているので、すべてaddChildNodeする必要がある
        let node = SCNNode()
        scene.rootNode.childNodes.map { node.addChildNode($0) } 
        return node
    }
}

enumUInt32なのはモンスターの種類をランダムで出現させるたい時に
enumrawValueで行えるからです。

使用例


// 生成される画面
private var sceneView: ARSCNView!

// モンスターの呼び出し(今回はくもを呼び出している)
func encountMonster(with position: SCNVector3) {

        // 冒頭で書いたノードのextensionを使っている
        MonsterType.spider.node?
            .setPosition(from: SCNVector3(position.x, position.y, position.z+1.5))
            .setRotation(from: SCNVector4(1, 0, 0, -0.5 * Float.pi))
            .setScale(from: SCNVector3(0.005, 0.005, 0.005))
            .addNode(to: &sceneView.scene)

}

カメラを傾け続けると、平面認識は常におこなわれるため、アップデートされた座標モンスターの出現場所に設定するようにしています。

Image uploaded from iOS (2).png

// ARKit のライフサイクルの1つ
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
        let position = SCNVector3Make(node.position.x,
                                      node.position.y,
                                      node.position.z-0.2) // 少し前に出すために-0.2している

        encountMonster(with: position)
}

⑥技の設定

タップやスワイプいろんな技が使えるようにできたらと思い、パーティクル(SCNParticleSystem)を使って演出してみました。

イメージは、スクショですが以下

Image uploaded from iOS (1).png

タップ判定の取得

// 画面
private var sceneView: ARSCNView!

@IBAction private func sceneViewTapped(_ recognizer: UITapGestureRecognizer) {
        let tapPoint = recognizer.location(in: sceneView)
        let results = sceneView.hitTest(tapPoint, types: .existingPlaneUsingExtent)

        guard let result = results.first else { return }

        // タップした座標
        print(result.worldTransform.columns.3.x)
        print(result.worldTransform.columns.3.y)
        print(result.worldTransform.columns.3.z)

        // タップしたに存在の、一番上に存在するノードを取得
        // 今回はタップしたところにモンスターがいることを検知したいので
        let targetNode = sceneView.hitTest(tapPoint, options: nil).first?.node

        // do something
}
// 魔法の定義
enum SpellType {
    case fire
    case wind

    private var name: String {
        switch self {
        case .fire: return "Fire.scnp"
        case .wind: return "Smoke.scnp"
        }
    }

    // 魔法の呼び出し
    public func getParticle(with inDirectory: String? = nil) -> SCNParticleSystem? {
        return SCNParticleSystem(named: name, inDirectory: inDirectory)
    }

}

使用例

// 画面
private var sceneView: ARSCNView!

// 魔法呼び出し(今回は炎)
func onFire(with position: SCNVector3) {
    SpellType.fire.getParticle()?
            .initNode()
            .setPosition(from: position)
            .addNode(to: &sceneView.scene)
}

パーティクルの作り方に関しては
SceneKit Particle System File と パーティクルシステム の emitterShape
パーティクルの障害物判定と新しいエミッターの派生

に詳しく載っているので、お好みのものを作成していただけると!

あとがき

Unityの方が良いってのをよく聞くんですが、ARKitだけでも十分良いものが作れるような気がしました。
部分的にしかコードを載せていないので、動作しなかったらすいません笑

その他参考サイト


『 Swift 』Article List
Category List

Eye Catch Image
Read More

Androidに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

AWSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Bitcoinに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

CentOSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

dockerに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

GitHubに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Goに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Javaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

JavaScriptに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Laravelに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Pythonに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Rubyに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Scalaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Swiftに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Unityに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Vue.jsに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Wordpressに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

機械学習に関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。