HoloLensのSpartial MappingでNavMeshを生成してランダムにAgentを出現・移動させる

(2017-07-02)

Unity 5.6.2f1
HoloToolkit v1.5.7.0

Unity 5.6から動的にNavMeshを生成できるようになったので HoloLensのSpartial MappingしたものをNavMeshにしてAgentを動かしてみる。

Unityで動的にNavMeshを生成する - sambaiz-net

Spartial MappingしたものをNavMeshにするのは以下の記事のスクリプトを使った。

HoloLens の空間マップで NavMesh を使ってみる - たるこすの日記

Unity-Technologies/NavMeshComponentsから LocalNavMeshBuilderNavMeshSourceTagを持ってきてLocalNavMeshBuilderのObjectを置いておき、 Spartial MappingしたものにNavMeshSourceTagを付けられればExampleと同様にNavMeshにできる。 そこで、このスクリプトではSpatialMappingSourceを取得し、イベントハンドラでNavMeshSourceTagが追加されるようにしている。

using HoloToolkit.Unity.SpatialMapping;
using UnityEngine;
using HoloToolkit.Unity;

public class SpatialMappingNavMesh : MonoBehaviour
{
    public GameObject SpatialMapping;

    private void Awake()
    {
        var spatialMappingSources = SpatialMapping.GetComponents<SpatialMappingSource>();
        foreach (var source in spatialMappingSources)
        {
            source.SurfaceAdded += SpatialMappingSource_SurfaceAdded;
            source.SurfaceUpdated += SpatialMappingSource_SurfaceUpdated;
        }
    }

    private void SpatialMappingSource_SurfaceAdded(object sender, DataEventArgs<SpatialMappingSource.SurfaceObject> e)
    {
        e.Data.Object.AddComponent<NavMeshSourceTag>();
    }

    private void SpatialMappingSource_SurfaceUpdated(object sender, DataEventArgs<SpatialMappingSource.SurfaceUpdate> e)
    {
        var navMeshSourceTag = e.Data.New.Object.GetComponent<NavMeshSourceTag>();
        if (navMeshSourceTag == null)
        {
            e.Data.New.Object.AddComponent<NavMeshSourceTag>();
        }
    }
}

NavMeshのランダムな場所を取得するには、適当なPointを取り、 NavMesh.SamplePositionで そこから最も近いNavMeshのPointを取る。

bool RandomPoint(Vector3 center, float range, out Vector3 result) {
    for (int i = 0; i < 30; i++) {
        Vector3 randomPoint = center + Random.insideUnitSphere * range;
        NavMeshHit hit;
        if (NavMesh.SamplePosition(randomPoint, out hit, 1.0f, NavMesh.AllAreas)) {
            result = hit.position;
            return true;
        }
    }
    result = Vector3.zero;
    return false;
}

動かすAgentはこんな感じ。こけないようにFreeze Rotationしている。

Agentの設定

このAgentを出現させて移動させる。

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

public class RandomSpawn : MonoBehaviour {

    public GameObject player;
    public GameObject agent;
    public GameObject counter;

    private List<GameObject> spawnedAgents = new List<GameObject>();
    private float interval = 0.0f;

    static int MAX_SPAWN_NUM = 10;
    static float SPAWN_RANGE = 10.0f;

	// Use this for initialization
	void Start () {
        counter.GetComponent<TextMesh>().text = spawnedAgents.Count + "";
    }

    // Update is called once per frame
    void Update () {

        interval += Time.deltaTime;
        if(interval > 5.0f)
        {
            if (spawnedAgents.Count < MAX_SPAWN_NUM)
            {
                Spawn();
            }
            Move();
            interval = 0.0f;
        }
    }

    void Spawn()
    {
        Vector3 spawnPoint;
        if (GetRandomPosition(player.transform.position, SPAWN_RANGE, out spawnPoint))
        {
            var obj = Instantiate(agent, spawnPoint, Quaternion.identity);
            counter.GetComponent<TextMesh>().text = spawnedAgents.Count + "";
            spawnedAgents.Add(obj);
        }
    }

    void Move()
    {
        foreach(var agent in spawnedAgents)
        {
            Vector3 next;
            if(GetRandomPosition(agent.transform.position, SPAWN_RANGE, out next)){
                agent.GetComponent<NavMeshAgent>().destination = next;
            }
        }
        
    }

    bool GetRandomPosition(Vector3 center, float range, out Vector3 result)
    {
        for (int i = 0; i < 30; i++)
        {
            Vector3 randomPoint = center + UnityEngine.Random.insideUnitSphere * range;
            NavMeshHit hit;
            if (NavMesh.SamplePosition(randomPoint, out hit, 1.0f, NavMesh.AllAreas))
            {
                result = hit.position;
                return true;
            }
        }
        result = Vector3.zero;
        return false;
    }
}

ちゃんと床や壁を認識して移動している。

移動するAgent