おもちゃラボ

Unityで遊びを作ってます

【Unity】ARKitで作るARモグラ叩き その1

これからUnityとARKitを使ってARもぐら叩きの作り方を紹介していこうと思います。全3回の予定です。Unityの操作になれていたら、ARKitを知らなくても1
記事あたり20分程度で試せると思います。

完成したARもぐら叩きはこんな感じになります。

f:id:nn_hokuson:20181025200107g:plain:w450

この記事はUnityを使ってARKitの使い方を解説していますが、
SwiftでARKitを学ぶには拙書「ARKit超入門」がオススメです。
コチラもチェックしていただければ嬉しいです!

今回と次回以降の記事はこちらです。

  • ARKitで作るARモグラ叩き その1
  • ARKitで作るARモグラ叩き その2
  • ARKitで作るARモグラ叩き その3

    今回の記事で作るもの

    今回はタップしたところにARで穴が3つ出現するところまで作りたいと思います。

    f:id:nn_hokuson:20181025200359j:plain:w450

    ARで穴を出現させるの流れは次の通りです。

    UnityでARKitを使う準備

    UnityでARKitを使うためにはARKit Pluginが必要になります。次のURLからダウンロードしてください。

    https://bitbucket.org/Unity-Technologies/unity-arkit-plugin/get/arkit2.0_beta.zip

    ダウンロードできたらAssets/UnityARKitPlugin/Examples/UnityARKitSceneを開きます。このシーンが一番シンプルなARKitのシーンになります。これを拡張して作っていきましょう。

    f:id:nn_hokuson:20181022192348p:plain:w300

    まずはヒエラルキーウインドウから今回作るARモグラ叩きでは不要なオブジェクトを削除します。次のオブジェクトを削除してください。

    • RandomCube
    • HitCubeParent
    • GeneratePlanes
    • ARKitControl
    • AR3DOFCameraManager

    ヒエラルキーウインドウが次のようになればOKです。

    f:id:nn_hokuson:20181022192422p:plain:w300

    続いて、ARモグラ叩き必要になるAssetをインポートします。次のURLからアセットをダウンロードしてインポートしてください。モグラ叩きの穴とモグラのアセットが含まれています。

    https://app.box.com/s/oycszpquu6ggb8pge4a10oxkrjldjkf3

    f:id:nn_hokuson:20181022192445p:plain:w600

    ちなみに・・・Unity Asset Storeでモグラ叩きのフルバージョンのアセットを販売しています。こちらも是非(^^;)


    モグラが出現する穴を作る

    ではまずはモグラが出現する穴を作っていきます。

    穴を配置する

    プロジェクトウインドウからシーンビューに「hole」をドラッグして、そこにholetexをアタッチしてください。

    f:id:nn_hokuson:20181022194732j:plain:w530

    モデルのテカリが気になる方は「hole」を選択した状態で、インスペクタに表示されるシェーダのSmoothnessを「0」に設定してください。

    f:id:nn_hokuson:20181022192906p:plain:w300

    Tagを設定する

    穴はモグラを出現させる目印にするため、Tagの設定を行っておきます。
    「hole」を選択して、インスペクタのTagから「Add Tag」をクリックしてください。

    f:id:nn_hokuson:20181022192917p:plain:w350

    「+ボタン」を押して「Hole」という名前でTagを作成してください。

    f:id:nn_hokuson:20181022192946p:plain:w350

    作成できたら、インスペクタに戻ってTagを「Hole」に変更します。

    f:id:nn_hokuson:20181022193020p:plain:w350

    穴のPrefabを作る

    今作成した穴のPrefabを作りましょう。ヒエラルキーウインドウからプロジェクトウィンドウにholeをドラッグ&ドロップしてください。Prefabの名前はholePrefabに変更しておきます。

    f:id:nn_hokuson:20181022193218j:plain

    Prefabができたら、2つほど穴を増やしておきましょう。プロジェクトウィンドウからholePrefabをドラッグ&ドロップしてください。

    f:id:nn_hokuson:20181022193236p:plain:w450

    今作成した3つの穴を一つのオブジェクトにまとめます。ヒエラルキーウインドウで「Create」→「Create Empty」を選択して空のオブジェクトを作ります。名前は「Holes」に変更しました。
    ここに先ほどの穴のオブジェクトをドラッグして子要素にしてください。

    f:id:nn_hokuson:20181022193247p:plain:w300

    最後にこの「Holes」オブジェクトもPrefabにしておきます。ヒエラルキーウインドウからプロジェクトウィンドウにドラッグ&ドロップしてください。

    f:id:nn_hokuson:20181022193349j:plain

    穴をタップで配置する

    アプリを実行したとき、画面をタップした場所にARで穴を表示するようにしましょう。

    まずはHolePlacerという名前でC#のスクリプトを作り(プロジェクトウィンドウで右クリックしてCreate→C# Script)、次のプログラムを入力してください。

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.XR.iOS;
    
    public class HolePlacer : MonoBehaviour
    {
        bool isGenerated = false;
        public GameObject holesPrefab;
    
        void HitTest(ARPoint point)
        {
            List<ARHitTestResult> hitResults = UnityARSessionNativeInterface
                .GetARSessionNativeInterface()
                .HitTest(point, ARHitTestResultType.ARHitTestResultTypeExistingPlaneUsingExtent);
    
            // 平面とあたっていた場合
            if (hitResults.Count > 0)
            {
                GameObject holes = Instantiate(holesPrefab);
                holes.transform.position = UnityARMatrixOps.GetPosition(hitResults[0].worldTransform);
                holes.transform.rotation = UnityARMatrixOps.GetRotation(hitResults[0].worldTransform);
    
                this.isGenerated = true;
            }            
        }
    
        // Update is called once per frame
        void Update()
        {
            if (!isGenerated && Input.touchCount > 0 )
            {
                var touch = Input.GetTouch(0);
                if (touch.phase == TouchPhase.Began)
                {
                    var screenPosition = Camera.main.ScreenToViewportPoint(touch.position);
                    ARPoint point = new ARPoint
                    {
                        x = screenPosition.x,
                        y = screenPosition.y
                    };
    
                    // 平面との当たり判定
                    HitTest(point);
                }
            }
        }
    }
    

    タップした場所にARのオブジェクトを表示するプログラムです。プログラムの詳しい内容は↓の記事でも解説しているので参考にしてください。

    nn-hokuson.hatenablog.com

    簡単に説明すると、ScreenToViewportPointでタップした座標をスクリーン座標に変換し、ARKitのHitTestメソッドを使って平面との当たり判定を行っています。平面と当たっていた場合にはそこに穴のオブジェクトを作成しています。また、穴を生成したあとは再度生成しないように、isGenerated変数で制御しています。

    作成したスクリプトをゲームオブジェクトにアタッチしましょう。ヒエラルキーウインドウで新たに空のゲームオブジェクト(Create→Create Empty)を生成し、Gameという名前に変更します。

    f:id:nn_hokuson:20181022193400p:plain:w300

    作成したGameオブジェクトにHolePlaceスクリプトをドラッグ&ドロップでアタッチします。

    f:id:nn_hokuson:20181022193521j:plain

    最後にHolePlacerスクリプトの「Hole Prefab」の欄に「Holes」オブジェクトをドラッグします。これにより、Holesプレハブの実態がスクリプト中のHolePrefab変数に代入されます。

    f:id:nn_hokuson:20181022193721j:plain

    実行してみる

    ココまで出来たら、実機に転送して実行してみましょう。
    「File」→「BUild Settings」を選択してBuild Settingsウインドウを開き、Scenes In BuildでUnityARKitSceneが選択されていることを確認してください。

    また、PlatformをiOSに変更していない場合は、iOSを選択してから「Switch Platform」をクリックして変更しておきましょう。

    f:id:nn_hokuson:20181022193945j:plain:w450

    「Build And Run」ボタンをクリックして実機に転送してください。画面をタッチしたら、そこに穴が表示されれば成功です!

    次回はARでモグラを表示して、穴から頭を出すようにします!お楽しみに!
    nn-hokuson.hatenablog.com

    f:id:nn_hokuson:20181025201438j:plain:w350

    参考書

    ARKitの使い方をSwiftで学びたい方はコチラがおすすめです!
    booth.pm

    Unityの基本的な使い方からC#の文法、ゲームの作り方まで解説しています!

  • Unityで始めるARKit入門 環境マッピング編

    ARKit2.0からは環境マッピングの機能が追加されました。環境マッピングを使うと金属などのマテリアルへの映り込みが実現できるようになります。これにより、より一層フォトリアルで映像に馴染むARの表現が可能になります。

    f:id:nn_hokuson:20181023192640j:plain:w550

    ここではARKitのPluginについてくるEnviroment Texturingのサンプルが微妙にわかりにくかったので、もう少し簡単に環境マッピングができる方法を紹介していきます。

    環境マッッピングとは

    前回の記事で書いたようにARKit1.5からは光源推定の機能が追加され自然に見えるARを表示することができるようになりました。

    nn-hokuson.hatenablog.com

    環境マッピングを使えば、光源推定だけでは表現できないより一層リアルなARを実現できます。環境マッピングは、ゲームで使用する空間の周りに仮想的な球(または立方体)を置き、そこに設定された環境マップ用の画像をもとに、環境光を決めるという仕組みです。

    f:id:nn_hokuson:20181023193218j:plain:w500

     ARKitではこの仮想的な環境に貼り付けるテクスチャを、カメラ画像からリアルタイムで生成する機能があります。これを利用すると、カメラで映した映像をもとにして周辺の色合いを判断し、3Dのオブジェクトの映り込みに利用できるようになります。

    動的な環境マップを使うことで、より映像に馴染んだARの表示ができるようになります。

    タッチしたところにモデルを配置する

    まずは環境マップの設定を行う前にタッチしたところに3Dモデルが表示されるようにしましょう。これがないと、正しく環境マップが設定できてるか分かりませんしね・・・

    基本的には前回の記事の内容と同じなので、簡単にまとめます。まず、TouchController.csを作ってGameオブジェクトにアタッチし、インスペクタで生成したいPrefabを登録します。

    nn-hokuson.hatenablog.com

    このとき、Prefabモデルのマテリアルを金属っぽくなるように設定しておきます。プロジェクトウィンドウで右クリック、「Create」→「Material」を選択してMetalというマテリアルを作ります。

    Metalのインスペクタで、金属っぽくなるようにMetalicを0.7程度、Smoothnessを0.8程度に設定しましょう。

    f:id:nn_hokuson:20180927192304p:plain:w350

    設定できたらこれをモデルにアタッチします。

    f:id:nn_hokuson:20180927192336p:plain:w250

    実行してみて正しくモデルが表示されるか試してみましょう。ここでは環境マッピングの設定をしていないので、まだ黒っぽいだけのモデルが表示されます。

    環境マップを設定する

    環境マッピングの設定するにはヒエラルキーウインドウでARCameraManagerを選択して、インスペクタのEnvironment Texturingを「Unity AR Environment Texturing Automatic」に設定します。

    f:id:nn_hokuson:20180927192523p:plain:w400

    Environment Texturingには次の3つを設定可能です。ここでは自動的にProbeが配置されるように「Automatic」を選択しました。

    設定値 役割
    Unity AR Environment Texturing None 環境マップを使用しない
    Unity AR Environment Texturing Manual 環境マップのProbeを自分で設定
    Unity AR Environment Texturing Automatic 環境マップのProbeを自動で設定

     
    Swift+ARKitであれば、これだけで設定完了なのですが、UnityではProbeを手動で配置する必要があります。

    えっ、さっきAutomaticを選んだのに・・!という方へ。ARKit内では自動で配置してくれますが、Unityには「ココにProbeを配置しますよ」というイベントが来るだけです。環境マッピングをするためにはこのイベントを受け取って自分でProbeを配置する必要があります。どこがAutomaticやねん!って感じですね。

     
    このへんはSwiftとARKitでアプリを作るほうが楽だなぁ、と感じるところです。
    Swiftを使ってARKitを試すには拙書の「ARKit超入門」がオススメです!

    booth.pm

     
    ということで、そのスクリプトを作成します。プロジェクトウィンドウで右クリックして、「Create」→「C# Script」を選択します。EnvmapController.csという名前で保存できたら次のスクリプトを入力してください。

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.XR.iOS;
    using Collections.Hybrid.Generic;
    
    public class EnvmapController : MonoBehaviour {
    
        private LinkedListDictionary<string, GameObject> probeAnchorMap;
    
        void Start () 
        {
            probeAnchorMap = new LinkedListDictionary<string, GameObject> ();
            UnityARSessionNativeInterface.AREnvironmentProbeAnchorAddedEvent += EnvironmentProbeAnchorAdded;
            UnityARSessionNativeInterface.AREnvironmentProbeAnchorRemovedEvent += EnvironmentProbeAnchorRemoved;
            UnityARSessionNativeInterface.AREnvironmentProbeAnchorUpdatedEvent += EnvironmentProbeAnchorUpdated;
        }
    
        void UpdateTexture(AREnvironmentProbeAnchor anchorData, GameObject go)
        {
            go.transform.position = UnityARMatrixOps.GetPosition(anchorData.transform);
            go.transform.rotation = UnityARMatrixOps.GetRotation(anchorData.transform);
            go.GetComponent<ReflectionProbe>().size = anchorData.Extent;
            go.GetComponent<ReflectionProbe>().customBakedTexture = anchorData.Cubemap;
        }
    
        void EnvironmentProbeAnchorUpdated (AREnvironmentProbeAnchor anchorData)
        {
            if (probeAnchorMap.ContainsKey (anchorData.identifier)) {
                GameObject go = probeAnchorMap[anchorData.identifier];
                UpdateTexture(anchorData, go);
            }
        }
    
        void EnvironmentProbeAnchorRemoved (AREnvironmentProbeAnchor anchorData)
        {
            if (probeAnchorMap.ContainsKey (anchorData.identifier)) {
                GameObject go = probeAnchorMap [anchorData.identifier];
                Destroy (go);
                probeAnchorMap.Remove (anchorData.identifier);
            }
        }
    
        void EnvironmentProbeAnchorAdded (AREnvironmentProbeAnchor anchorData)
        {
            // Reflection Probeの生成
            GameObject go = new GameObject("Probe");        
            go.AddComponent<ReflectionProbe>();
            go.GetComponent<ReflectionProbe>().mode = UnityEngine.Rendering.ReflectionProbeMode.Custom;
    
            if (go != null) 
            {        
                go.transform.position = UnityARMatrixOps.GetPosition (anchorData.transform);
                go.transform.rotation = UnityARMatrixOps.GetRotation (anchorData.transform);
                probeAnchorMap [anchorData.identifier] = go;
                UpdateTexture(anchorData, go);
            }
        }
    }
    

    スクリプトが入力できたら、こちらもGameオブジェクトにアタッチしておきましょう。これで環境マップを使う準備は完了です。

    実行してみる

    Unityでビルドして実機に転送して実行してみてください。次のように、カメラで撮影した画像がモデルに写り込んでいれば成功です!

    f:id:nn_hokuson:20181023192640j:plain:w400

    booth.pm

    Unityで始めるARKit入門 ARKit Remote編

    UnityのARKit Pluginには、作成したアプリを毎回実機に転送しなくてもデバッグできるARKitRemoteという仕組みが用意されています。今回はこのARKitRemoteを使用する方法を紹介します

    今回の目次は次のとおりです。

    ARKitRemoteとは

    ARKitRemoteとはUnity ARKit Pluginに付属するアプリケーションで、これを使えば実機に転送することなくUnityエディタ上でARのデバッグをすることが出来ます。

    このARKitRemoteは、実機側にRemote用のアプリを入れておき、ここからiPhoneで撮影した映像をリアルタイムで転送します。そして、Unityエディタ側でこの映像を受け取り、ARの計算した結果をエディタ上に表示するという仕組みです。

    f:id:nn_hokuson:20181017185233p:plain:w550

    UnityからARKitRemoteを利用するためには次の手順が必要になります。

    1. 実機にARKitRemoteのアプリをインストールする
    2. Unityエディタ側からアプリに接続する

    実機にARKitRemoteのアプリをインストールする

    まずはARKitRemoteのアプリをビルドして実機にインストールします。Build Settingsを開き、「UnityARKitPlugin/ARKitRemote/UnityARKitRemote」にチェックを入れてください。

    また、Development Buildにチェックを入れます。これを忘れると動作しないので注意が必要です!

    f:id:nn_hokuson:20181017183125j:plain:w400

    ビルドして実機にインストールできたら起動してください。実機側で次のような画面が表示されたらOKです。

    f:id:nn_hokuson:20181017183202p:plain:w200

    ❗❗右下にDevelopment Buildと表示されていることを確認してください❗❗
    されていない場合はBuild Settingsでチェックし忘れてないか再確認してください。

    Unityエディタ側からアプリに接続する

    次にアプリを立ち上げた状態で、ARKitのプログラムをUnityエディタで実行してみましょう。ここではUnityARKitPlugin/ARKitRemote/EditorTestSceneを起動しました。

    すると次のような緑の画面がUnityエディタに表示されます。

    f:id:nn_hokuson:20181017183437p:plain:w400

    次にConsoleウインドウの「Editor▼」から使用する端末を選択します。PCとスマートフォンが同一のLANに接続している場合は、Wifi経由でも接続できますが、とりあえずUSB経由での接続をおすすめします。

    f:id:nn_hokuson:20181017183448p:plain:w600

    正しく接続できると次のようにUnityエディタの実行画面の上側に「Start Remote ARKit Session」というボタンが表示されるのでクリックしてください。

    f:id:nn_hokuson:20181017183458p:plain:w200

    UnityエディタにiPhoneで撮影した画像が表示されます。また、EditorTestSceneを実行した場合は、Unityエディタの画面をクリックすると、その場所に立方体が表示されます。

    f:id:nn_hokuson:20181017185556j:plain:w500

    SwiftでARKitを学ぶ

    ARKitRemoteは変更結果をすぐにUnityエディタ上で確認できるので非常に便利でした。
    ただ、ARKitRemoteではうまく動かないARの機能もあります。そのため、ARKitの機能を個別に知るにはSwiftでの学習がオススメです。次の参考書を書きましたので、是非参考にしてみてください。

    booth.pm