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から
LocalNavMeshBuilder
とNavMeshSourceTag
を持ってきて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を出現させて移動させる。
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;
}
}
ちゃんと床や壁を認識して移動している。