おもちゃラボ

Unityで遊びを作ってます

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

前回は画面をタップしたところに穴を表示するプログラムを作りました。今回はARのモグラを生成して上下するようにするところまでを作ります。完成イメージはこんな感じです。

f:id:nn_hokuson:20181101195113j:plain

関連記事はこちらです。前回はタッチしたところにARで穴を表示するところまでを解説しています。

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


    今回もUnityとARKit Pluginを使ってすすめていきます。
    手前味噌ですが・・・Swiftを使ってARKitを学びたい方はこちらの書籍がオススメです


    モグラのPrefabを作る

    まずはプロジェクトウィンドウからシーンビューにmoleをドラッグします。そのmoleにmoletexをアタッチしてテクスチャを設定しましょう。

    f:id:nn_hokuson:20181031221451j:plain:w500

    今回もモグラのテカリが気になる方は、moletexマテリアルのインスペクタからSmoothnessを「0」に設定してください。

    f:id:nn_hokuson:20181031221607p:plain:w320

    次にモグラを叩いたときの判定をするために、コライダをアタッチしておきます。ヒエラルキーウインドウでmoleを選択した状態で、インスペクタの「Add Component」から「Capsule Collider」を選択してください。

    f:id:nn_hokuson:20181031221622p:plain:w500

    Capsule Colliderがモグラにフィットするようにパラメータを次のように調節してください。

    f:id:nn_hokuson:20181031221640p:plain:w320

    ここまでで、一旦モグラのPrefabを作っておきましょう。ヒエラルキーウインドウからプロジェクトウィンドウにmoleをドラッグ&ドロップしてください。ファイル名はmolePrefabに変更しておきます。

    f:id:nn_hokuson:20181031222020j:plain

    ヒエラルキーウインドウにあるモグラは「右クリック」→「Delete」で消しておきます。

    穴の場所にモグラを生成する

    今作成したモグラのPrefabを穴の位置に生成するスクリプトを作ります。

    プロジェクトウィンドウのHolePlacerスクリプトに、GenerateMolesメソッドとその呼び出し部分のプログラムを追加してください。

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.XR.iOS;
    
    public class HolePlacer : MonoBehaviour
    {
        bool isGenerated = false;
        public GameObject holesPrefab;
        public GameObject molePrefab;
    
        public void GenerateMoles(GameObject holes)
        {
            foreach (Transform t in holes.transform)
            {
                GameObject child = t.gameObject;
                if(child.tag == "Hole")
                {
                    Vector3 pos = child.transform.position;
                    Instantiate(molePrefab, pos, molePrefab.transform.rotation);
                }
            }
        }
        
        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);
    
                // モグラを生成する
                GenerateMoles(holes);
                
                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);
                }
            }
        }
    }
    

    前回のプログラムにGenerateMolesメソッドを追加しています。このメソッドは引数に穴(3つの穴を子要素にもつオブジェクト)のオブジェクトを受け取ります。その子要素を調べて「Hole」というタグを持っていれば、その位置にモグラを生成しています。また、HitTestメソッドの中でこのGenerateMolesメソッドを呼び出しています。

    プログラムが追加できたら、GameオブジェクトにアタッチしたHolePlacerスクリプトのMole Prefabの欄にmolePrefabの実体をセットしてください。

    f:id:nn_hokuson:20181031222311j:plain

    ここまでで実行してみましょう。画面をタップすると穴が配置され、その上にモグラが表示されるかを確かめてください。

    f:id:nn_hokuson:20181101195727j:plain:w500

    モグラを動かす

    次にモグラが穴から出たり入ったりするようにしましょう。MoleControllerというスクリプトを作成して、次のプログラムを入力して下さい。

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class MoleController : MonoBehaviour {
    
        Vector3 groundLevel;
        Vector3 undergroundLevel;
        public GameObject effect;
    
        bool isOnGround = true;
        float time = 0;
    
        void Up()
        {
            transform.position = groundLevel;
            this.isOnGround = true;
        }
    
        void Down()
        {
            transform.position = this.undergroundLevel;
            this.isOnGround = false;
        }
    
        void Start () 
        {
            this.groundLevel = transform.position;
            this.undergroundLevel = this.groundLevel - new Vector3(0, 0.2f, 0);
    
            // 地中に隠す
            Down();
    	}
    	
    	void Update () 
        {
            this.time += Time.deltaTime;
    
            if( this.time > 2.0f )
            {
                this.time = 0;
                if( this.isOnGround) 
                {
                    Down();
                }
                else 
                {
                    Up();
                }
            }
        }
    }
    

    このスクリプトでは、time変数を使って時間をカウントして、2秒ごとにモグラを上下させています。プログラムの分かりやすさを優先して、ここではアニメーションの設定はまだしていません。なのでモグラは地上と地中の間を瞬間移動します。次の記事でDOTweenを使ったアニメーションの方法を紹介します。

    作成したMoleControllerスクリプトをモグラのPrefabにドラッグ&ドロップしてアタッチします。

    f:id:nn_hokuson:20181031222631p:plain

    実機にインストールして、モグラが上下するか確認してみましょう。

    f:id:nn_hokuson:20181101200047g:plain:w500

    モグラは上下していますが、穴の下にいるモグラまで見えてしまっていますね。これはARKitが現実世界にあるものとARオブジェクトの前後関係を認識できないのが原因です。この地中のモグラが見えてしまう対策をしていきます。

    オクルージョン対策

    現実世界にあるものとの前後関係がわからない問題は、昔からARにはつきものの問題です。ARKitではカメラ映像の上にARオブジェクトを強制的にべた書きするため、このように前後関係が崩れてしまいます。

    Depthカメラを使えば、画像の奥行きがわかるため、多少の前後判別はできるようになります。ただ、現状ではリアルタイムで計算するにはコストが高く、根本的に解決されたわけではありません。

    そこでARKitを使ってオクルージョンを実現するには手動で、オクルージョン用の平面を設定する必要があります。この平面を設定することで、それよりも奥にあるオブジェクトは描画されなくなります。

    f:id:nn_hokuson:20181101202430p:plain:w550

    まずはヒエラルキーウインドウで「Create」→「3D Object」→「Plane」を選択して穴の真下に設定します。
    また、今作成した平面をHolesの子要素にしておきます。

    f:id:nn_hokuson:20181031222809j:plain:w600

    次に平面にオクルージョン用のマテリアルを設定します。UnityARKitPlugin/Common/MaterialsにあるocclusionPlaneMaterialを平面にドラッグしてください。これにより、この平面より下にあるオブジェクトは描画されないようになります。

    f:id:nn_hokuson:20181031223100j:plain:w500

    HolesPrefabにも変更を反映するため、ヒエラルキーウインドウでHolesを選んでからインスペクタの「Apply」ボタンを押してください。

    f:id:nn_hokuson:20181031223158p:plain:w320

    オクルージョンに使ったシェーダを少しだけ見ておきましょう。UnityARKitPlugin/Example/Common/Shaders/MobileOcclusion.shaderです。ポイントは次の3行です。

    ZWrite On
    ZTest LEqual
    ColorMask 0
    

    ZWriteをOnにしてデプステストは行いつつ、ColorMaskに0を設定することで描画は行わないようにしています。つまりデプステスト的にはそこにオブジェクトがあるけど、見た目にはなにもないというおばけ状態ですね(笑)
     
     
    f:id:nn_hokuson:20181101202845p:plain:w600
     
    これにより背後のオブジェクトがデプステストにかかって描画されなくなります。シェーダについては次の記事を参照下さい。

    nn-hokuson.hatenablog.com

    実行してみる

    オクルージョン用の平面を使用したことで、穴の下にいるモグラは描画されなくなりました。

    f:id:nn_hokuson:20181101200103g:plain:w500

    今回はココまでにしましょう。次回は最終回です。最後の仕上げにモグラのアニメーションとパーティクルの発生、GUIの設定などをしていきますよ!

    nn-hokuson.hatenablog.com

    参考書

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

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