見出し画像

ARトラの作り方

寅年ということで、Unityを使ってARのトラを作ってみたので、作り方を紹介したいと思います。仕上がりはこんな感じです。

このトラは、部屋の状態を察知し、指定した目的地まで障害物を避けて到達してくれます。遠くに離れていても、呼べばなんとか自分のもとに着てくれるので、なかなか可愛いやつです。

使用した環境:
Unity 2021.1.5f1
MacBook Pro
iPhone 13 Pro Max
※LiDARスキャナを搭載したiOSデバイスが必要です

仕組みの大まかな説明

iPhoneのLiDARスキャナを使用します。LiDARスキャナの入力をARMeshManagerを使ってメッシュに変換し、NavMeshSurfaceでそのメッシュからNavMeshをBakeし、トラが障害物を避けて目的地にたどり着くようにします。

作り方

作り方を説明していきます。

1.URPのプロジェクトを作り、AR Foundationを有効にする

まずは、プロジェクトを作ってAR Foundationを有効にします。
今回はShader Graphを使うので、URPのプロジェクトを作ります。

作り方はこちらの記事が網羅的に書かれていたので参考にして下さい。

2.LiDARを有効にする

AR Mesh Managerを使ってLiDARスキャナから得られた情報をもとにメッシュを再構築します。

方法はこちらの記事をご覧ください。


3.トラを配置する

トラは、AR Foundationで平面を検出し(Plane Detect)、タップしたときにRaycastして平面にぶつかったらその上に置きます。(後ほどコード全体を紹介します)

Plane Detectの方法は次の記事がわかりやすかったです。

トラはこちらを使用しました。


4.NavMeshSurfaceで動的にNavMeshをBakeする

NavMeshSurfaceを使用して動的にNavMeshをBakeします。
こちらの記事がわかりやすかったです。

なお、上の記事では、BakeするためにBake()メソッドを使用していましたが、現在のバージョンではBuildNavMesh()を使用します。(後ほどコード全体を紹介します)

GetComponent<NavMeshSurface>().BuildNavMesh();

また、NavMeshSurfaceコンポーネントの設定には、Advanced Override Voxcel Sizeという項目があります。

これは、NavMesh のベイクの入力ジオメトリをどれだけ正確に処理するかを制御するための項目ですが、今回はこの値を0.084にしました。これは、エージェントの半径ごとに5.95ボクセルを認識するもので、かなり細かい方だと思います。このようにしないとLiDARで認識したMeshにトラが引っかかってしまうようです。

5.トラに目的地を知らせる

トラの目的地は、画面にタップした時にRaycastして平面にぶつかった位置にしています。これはトラを置く時と同じですが、1回目のタップではトラを置き、2回目以降は目的地を指定しています。

ということで、Raycast、NavMeshSurfaceのBakeのコードも含めてコード全体は次のようになりました。

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

public class PlaneDetection : MonoBehaviour
{
    ARRaycastManager raycastManager;
    [SerializeField] GameObject target;
    bool firstTouch = true;
    public Transform goal;
    // Start is called before the first frame update
    void Start()
    {
      raycastManager = GetComponent<ARRaycastManager>();   
    }

    // Update is called once per frame
    void Update()
    {
      if(Input.touchCount == 0 || Input.GetTouch(0).phase != TouchPhase.Ended || target == null){
        return;
      }

      var hits = new List<ARRaycastHit>();
      if(raycastManager.Raycast(Input.GetTouch(0).position, hits, TrackableType.PlaneWithinPolygon)){
        var hitPose = hits[0].pose;
        if(firstTouch) { // 初回はトラを出現させる
          Instantiate(target, hitPose.position, hitPose.rotation);
        } else { // 2回目以降は、NavMeshをBakeしたあとにトラに目的地を知らせる
          Bake();
          SetTigerGoal(hitPose.position);
        }        
      }
      firstTouch = false;
    }

    void Bake() {
      NavMeshSurface sur = GameObject.FindGameObjectWithTag("navsur").GetComponent<NavMeshSurface>();
      sur.BuildNavMesh();
    }

    void SetTigerGoal(Vector3 position) {
      GameObject tiger = GameObject.FindGameObjectWithTag("tiger");
      tiger.GetComponent<NavMeshAgent>().SetDestination(position);
    }
}

最後に


noteやTwitterではiOS(Swift)、Unityで作った作品や記事をアップしています。今回の記事が面白いと思った方はぜひフォローしてみて下さい!


この記事が気に入ったらサポートをしてみませんか?