6
4

More than 3 years have passed since last update.

AR Foundationで検出した平面上を動くエージェントをNavMeshComponentsで作り、鬼ごっこしてみる

Last updated at Posted at 2021-04-22

AR FoundationをRuntime NavMeshと組み合せた例をなかなか見つけることができず、自分でやってみると少し引っかかる点があったため共有します。

なお、今回のプロジェクトはMITライセンスで公開しています。
https://github.com/Machikof/ARNavMeshSample

概要

AR Foudationで検知した平面をタップすると、その上にオブジェクト(エージェント)が生成され、自分(端末)を追いかけるようになります。
ARNavMeshSampleのコピー.gif

追いかける先は自分でなくともいいので、ARコンテンツを開発する上でかなり応用の効く技術だと思います。

環境

  • Unity 2019.4.8(2020.3でも動作確認済)
  • AR Foundation 4.1.1
  • ARKit XR Plugin 4.1.1
  • ARCore XR Plugin 4.1.1
  • AR Foundation Editor Remote 4.11.0(※今回はデバッグ用に用意しましたが、なくても大丈夫です)

実装

1. AR Sessionを構築する

下の記事を参考にしながら構築していきます。

画像のように、シーン上にAR SessionとAR Default PlaneとAR Session Origin、そしてその子にAR Cameraを追加します。
スクリーンショット 2021-04-21 4.49.45.png

スクリーンショット 2021-04-21 4.57.05.png
AR Session OriginにAR Plane ManagerとAR Raycast Managerを追加し、AR Plane ManagerのDetection ModeをHorizontalにします。

2. NavMeshComponentsを構築

UnityTechnologiesから提供されているNavMeshComponentsを入れます。

プロジェクトを落としたら、NavMeshComponents-master/Assetsの中のGizmosとNavMeshComponentsをコピーし、自分のプロジェクトのAssets直下に置きます。

スクリーンショット 2021-04-22 11.25.35.png

シーン上に「NavMeshSurface」というオブジェクトを設置し、同名のコンポーネントを追加します。
インスペクタの値はそのままで構いません。
スクリーンショット 2021-04-22 11.35.33.png

次に、実行中にNavMeshをベイクするスクリプトを用意します。

BuildNavMesh.cs
using System.Collections;
using UnityEngine;
using UnityEngine.AI;

[RequireComponent(typeof(NavMeshSurface))]
public class BuildNavMesh : MonoBehaviour
{
    [Header("ベイク更新時間"), SerializeField] float bakeUpdateTime;
    private NavMeshSurface _surface;

    void Start()
    {
        _surface = GetComponent<NavMeshSurface>();
        StartCoroutine(BakeUpdate());
    }

    IEnumerator BakeUpdate()
    {
        while(true)
        {
            _surface.BuildNavMesh();

            yield return new WaitForSeconds(bakeUpdateTime);
        }
    }
}

今回は簡単のためにコルーチンを使いましたが、負荷が気になる場合はベイクを一度きりにした方がいいでしょう。
このスクリプトをNavMeshSurfeceにアタッチし、bakeUpdateTimeには3や5など適当な値を入れます。

3. エージェントを用意する

いよいよ端末を追いかけるエージェントを作っていきます。
以下がエージェント側のスクリプトです。

MoveAgent.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

public class MoveAgent : MonoBehaviour
{
    [Header("ARカメラ"), SerializeField] GameObject camera;
    NavMeshAgent agent;
    Vector3 targetPos;

    void Start()
    {
        agent = GetComponent<NavMeshAgent>();
        targetPos = new Vector3(camera.transform.position.x, transform.position.y, camera.transform.position.z);
    }

    void Update()
    {
        ApproachToCamera();
    }

    void ApproachToCamera()
    {
        if (agent.pathStatus != NavMeshPathStatus.PathInvalid)
        {
            Debug.Log("NavMesh is ready");
            agent.destination = targetPos;
        }
        else Debug.Log("NavMesh is not ready");
    }
}

targetPosにはXZ平面上に射影したカメラ位置を入れ、ApproachToCamera()で目的地に設定します。

今回はCapsuleを使いますが、任意のキャラクターアセットを使っても問題ありません。
卓上で動かすことを想定しているので、Scaleは0.1倍です。
NavMeshAgentと上のスクリプトを追加し、インスペクタのARカメラにはシーン上のAR Cameraをアタッチします。
スクリーンショット 2021-04-22 13.06.03.png

ここで、動かす環境(机の上など)によってはNavMeshの精度が足りない場合があります(自分はここで引っかかりました)。
その場合、以下のようにRadiusを小さくするといいでしょう。

スクリーンショット 2021-04-22 13.11.03.png

4. 実行中にエージェントを生成する

あとは画面をタップした位置にエージェントを生成するスクリプトを書きます。
ググればすぐに出てきますが、こちらも一応載せておきます。

CreateAgent.cs
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;

[RequireComponent(typeof(ARRaycastManager))]
public class CreateAgent : MonoBehaviour
{
    [Header("出現させるオブジェクト"), SerializeField] GameObject objectPrefab;
    ARRaycastManager raycastManager;
    List<ARRaycastHit> hitResults = new List<ARRaycastHit>();
    bool isSet = false;

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

    void Update()
    {
        // エディタ上
#if UNITY_EDITOR
        if (Input.GetMouseButtonDown(0))
        {
            // レイと平面が交差時
            if (raycastManager.Raycast(Input.GetTouch(0).position, hitResults, TrackableType.All))
            {
                SetObject(hitResults[0].pose.position);
            }

        }
        // 端末上での動作
#else
        if(Input.touchCount > 0)
        {
            var touch = Input.GetTouch(0);
            if(touch.phase == TouchPhase.Began)
            {
                // レイと平面が交差時
                if (raycastManager.Raycast(Input.GetTouch(0).position, hitResults, TrackableType.All))
                {
                    SetObject(hitResults[0].pose.position);
                }
            }
        }

#endif
    }

    void SetObject(Vector3 position)
    {
        if (!isSet)
        {
            Instantiate(objectPrefab, position, Quaternion.identity);
            isSet = true;
        }
        else
        {
            Debug.Log("Object is here now");
        }
    }
}

ARFoundation Remoteでのデバッグを円滑にするため、エディタと端末で処理を分けています。
書き方はステップアップUnityのモバイルARの章を参考にしています。

ARは特に動作確認のためにビルドする手間がかかるため、こうした一手間で作業がグッと楽になります。

参考資料

サイト

書籍

ステップアップUnity
https://www.amazon.co.jp/dp/B08W8L2LGJ/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1

6
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4