iPhone 12 Pro(LiDAR) + ARFoundationで ARゲームことはじめ

f:id:kaminashi-developer:20201127042610p:plain

こんにちは、カミナシ・エンジニアの浦岡です。

iPhone12 Pro に搭載されているLiDARセンサーを活用したくARゲームの作成にチャレンジしようと思います。

今回、以下のような簡易的なゲームの作成を目標にします。

f:id:kaminashi-developer:20201127042833p:plain
ゲームの流れ

①現実世界のスキャン

ゲームの開発環境にはUnityのARFoundationを使用します。

UnityEditor上でARFoundationのARMeshManagerを設定することで、LiDAR(ARKit/RealityKit)でスキャンした現実世界のメッシュ情報をUnityEngine側に反映させることができます。

ここでゲームのリアリティを高めるためにOcclusionを有効にしたいと思います。 この設定を有効にすることで、下の画像のように仮想コンテンツが現実の物体に遮蔽されるようになります。

f:id:kaminashi-developer:20201127043659j:plain
Occlusion有効

(境界のディテールがまだ荒かったりしますが、今後の進化に期待したいポイントですよね)

ここまでが設定された状態のサンプルが以下のScenes/Meshing/OcclusionMeshing にあるので、以降これをベースに進めます。

ARFoundation Sample github.com

②バーチャルなキャラを出現させる

次に、メインキャラクターとして弊社マスコットの「ごーとん」をゲーム内で使用できるようにします。

  • 「ごーとん」3DオブジェクトをUnityにインポートして、Prefab化。

  • そのPrefabにColliderとRigidbodyを設定します。

  • さらに当たり判定時の識別用にtagを付与するようにします。

f:id:kaminashi-developer:20201127044507p:plain
「ごーとん」の3Dオブジェクトをインポート

「ごーとん」の出現イベントを定義します。 (今回は簡易的にするために、プレイヤーがタップした位置に「ごーとん」を出現させます。)

シーン配下のAR Session Origin > AR Cameraを選択して、 [Add Component]でスクリプトを追加。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARSubsystems;

namespace UnityEngine.XR.ARFoundation.Samples
{
   [RequireComponent(typeof(Camera), typeof(ARRaycastManager))]
   public class SetGoat : MonoBehaviour
   {
      
       [SerializeField]
       Rigidbody m_GoatnPrefab;

       public Rigidbody goatnPrefab
       {
           get => m_GoatnPrefab;
           set => m_GoatnPrefab = value;
       }
    
       private ARRaycastManager raycastManager;
       private List<ARRaycastHit> hitResults = new List<ARRaycastHit>();

       void Awake()
       {
           raycastManager = GetComponent<ARRaycastManager>();
       }

       void Update()
       {
           if (ARGameManager.Instance.GetState() != ARGameState.Scanning) {
               return;
           }

           if (m_GoatnPrefab == null)
               return;

           if (Input.touchCount < 1)
               return;
          
          
           var touch = Input.touches[0];

           if (touch.phase == TouchPhase.Began
               && raycastManager.Raycast(touch.position, hitResults, TrackableType.Planes))
           {
               var spawnPosition = hitResults[0].pose.position + new Vector3(0, 2, 0);
               Instantiate(m_GoatnPrefab, spawnPosition, Quaternion.identity);
               ARGameManager.Instance.ChangeState(ARGameState.Capturing);
           }
       }
   }
}

ゲームの状態を保持するためのクラスを作成しておき、ゲーム状態がScanningで、画面がタップされた場合にRaycastして平面を取得。その座標にごーとんを配置します。 メッシュにめり込んだ状態で出現しないようにy軸方向にオフセットをつけています。 出現後、ゲームの状態をCapturingに更新します。

③ボールを当てて捕獲

最後に、捕獲用のボールです ボールオブジェクトはサンプルで使われているもの(Projectile)を流用します。 このPrefabに当たり判定用のスクリプトを追加。

ボールが 「ごーとん」 とぶつかった場合の処理を実装できます。

f:id:kaminashi-developer:20201127050031p:plain
ボールオブジェクト

ボールの投下処理もサンプルを流用します。 AR Session Origin > AR Camera の ProjectileLauncher

using UnityEngine;

namespace UnityEngine.XR.ARFoundation.Samples
{
   [RequireComponent(typeof(Camera))]
   public class ProjectileLauncher : MonoBehaviour
   {
       [SerializeField]
       Rigidbody m_ProjectilePrefab;

       public Rigidbody projectilePrefab
       {
           get => m_ProjectilePrefab;
           set => m_ProjectilePrefab = value;
       }

       [SerializeField]
       float m_InitialSpeed = 25;

       public float initialSpeed
       {
           get => m_InitialSpeed;
           set => m_InitialSpeed = value;
       }

       void Update()
       {
           if (ARGameManager.Instance.GetState() != ARGameState.Capturing) {
               return;
           }

           if (m_ProjectilePrefab == null)
               return;

           if (Input.touchCount < 1)
               return;
          
           var touch = Input.touches[0];
           if (touch.phase == TouchPhase.Began)
           {
               var ray = GetComponent<Camera>().ScreenPointToRay(touch.position);
               var instant = Instantiate(m_ProjectilePrefab, ray.origin, Quaternion.identity);
               var rigidbody = instant.GetComponent<Rigidbody>();
               rigidbody.velocity = ray.direction * m_InitialSpeed;
           }
       }
   }
}

ここまでで、Unityでbuild、さらにXCodeで実機ビルドした状態が以下です。

youtu.be

簡単な流れはできましたがゲーム性が皆無なので全体的に作り込みが必要ですね

感想

AR的な要素は画面上の設定でほぼ完了するので、ARゲームというよりUnityゲームを作っている感じです。動作確認するのに毎回実機ビルドが面倒なので、作り込む場合は一旦Unity側でゲーム完成させるか、ARFoundation editor remote(有料)などデバッグツールが必須かもしれません。

個人的に、ゲーム以外での実用面含めてARな世界を待望しています。 いつかカミナシのプロダクトでもAR使ってみたいのでそれまでゲームで勉強するぞ👾👾👾