Визуализация пути для Nav Mesh Agent

При использовании карты навигации, например, в игре с видом сверху, когда персонаж передвигается по клику. Может потребоваться возможность визуализировать путь для конкретного NavMeshAgent. То есть получается, что если назначить путь агенту, то сначала будет «нарисован» путь, а затем по нему, начнет движение сам агент. Путь для NavMeshAgent, от стартовой и до конечной позиции, состоит из массива ключевых точек, то бишь координат. Наша задача, получить данный массив и на его основе расставить объекты, обозначающие ключевую точку. Затем, соединить точки линиями, в качестве линий, так же будет выступать заранее подготовленный объект.


Итак, перед тем как продолжить, у вас уже должен быть готов персонаж с настроенным компонентом NavMeshAgent, плюс, созданная карта навигации для агента, простой пример создания карты см. здесь. Если всё это есть, тогда приступаем к созданию объектов, из которых и будет создаваться путь.

Для начала сделаем линию, добавляем на сцену пустой объект и обнуляем позицию. Затем добавляем стандартный Quad, делаем его дочерним к пустому объекту и тоже обнуляем позицию. Разворачиваем Quad по оси Х на 90, чтоб при создании наш квадрат отображался в плоскости ХZ. Толщину линии можно отрегулировать изменяя Scale по оси Y, но не меняйте масштаб по оси Х - это важно! Потому как, этот параметр будет регулироваться через скрипт и для корректных расчетов, нужно чтобы исходное значение было по умолчанию.

Например может получится вот так:

Визуализация пути для Nav Mesh Agent

Материал выбираем по вкусу, готовый объект перетаскиваем в папку с префабами и удаляем со сцены исходный.

Префаб для ключевых точек, делается точно так же, но уже можно свободно регулировать масштаб.

Далее, цепляем, допустим на камеру, скрипт DrawPath:

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

public class DrawPath : MonoBehaviour {

	public NavMeshAgent agent; // агент с которого рисуем путь

	public GameObject point; // префаб для Waypoints
	public GameObject line; // префаб для линий, между Waypoints

	public float distance = 1; // минимальная дистанция между точками, чтобы не рисовать те, которые слишком близко
	public float height = 0.01f; // коррекция позиции по высоте

	private List<GameObject> points;
	private Vector3 agentPoint;
	private Vector3 lastPoint;
	private List<GameObject> lines;

	void Awake () 
	{
		points = new List<GameObject>();
		lines = new List<GameObject>();
		UpdatePath();
	}

	void ClearArray() // удаление объектов и очистка массивов
	{
		foreach(GameObject obj in points)
		{
			Destroy(obj);
		}
		foreach(GameObject obj in lines)
		{
			Destroy(obj);
		}
		lines = new List<GameObject>();
		points = new List<GameObject>();
	}

	bool IsDistance(Vector3 distancePoint) // проверка дистанции между Waypoints
	{
		bool result = false;
		float dis = Vector3.Distance(lastPoint, distancePoint);
		if(dis > distance) result = true;
		lastPoint = distancePoint;
		return result;
	}

	void UpdatePath() // рисуем путь
	{
		lastPoint = agent.transform.position + Vector3.forward * 100f; // чтобы создать точку в текущей позиции

		ClearArray();

		for(int j = 0; j < agent.path.corners.Length; j++)
		{
			if(IsDistance(agent.path.corners[j]))
			{
				GameObject p = Instantiate(point) as GameObject;
				p.transform.position = agent.path.corners[j] + Vector3.up * height; // создаем точку и корректируем позицию 
				points.Add(p);
			}
		}

		for(int j = 0; j < points.Count; j++)
		{
			if(j + 1 < points.Count)
			{
				Vector3 center = (points[j].transform.position + points[j+1].transform.position) / 2; // находим центр между точками
				Vector3 vec = points[j].transform.position - points[j+1].transform.position; // находим вектор от точки А, к точке Б
				float dis = Vector3.Distance(points[j].transform.position, points[j+1].transform.position); // находим дистанцию между А и Б

				GameObject p = Instantiate(line) as GameObject;
				p.transform.position = center - Vector3.up * height;
				p.transform.rotation = Quaternion.FromToRotation(Vector3.right, vec.normalized); // разворот по вектору
				p.transform.localScale = new Vector3(dis, 1, 1); // растягиваем по Х
				lines.Add(p);
			}
		}
	}

	void Update () 
	{
		RaycastHit hit;
		Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
		
		if (Physics.Raycast(ray, out hit))
		{
			if(Input.GetMouseButtonDown(0))
			{
				agent.SetDestination(hit.point); // назначаем точку следования
			}
		}

		if(agentPoint != agent.path.corners[agent.path.corners.Length-1]) UpdatePath(); // рисуем путь если был изменена конечная точка назначения
		agentPoint = agent.path.corners[agent.path.corners.Length-1]; // запоминаем текущую конечную точку назначения

		if(agent.path.corners.Length == 1 && points.Count > 1) UpdatePath(); // рисуем путь, после прибытия в точку назначения
	}
}

После настройки переменных, можно приступать к тестированию.

Стоит отметить что в текущем варианте, "рисование" пути заточено под плоскую поверхность, потому как это лишь пример. Если говорить о неровной поверхности, подъемах и спусках. То тогда в качестве линии стоит использовать что-то объемное, цилиндр допустим, подогнав его длину по Х, как у стандартного квадрата. Плюс, накладывать эти объекты через дополнительную камеру, чтоб они всегда были поверх остальных. В общем это уже детали, главное что есть основа.

Скачать демо:

Внимание! Посетители, находящиеся в группе Гости, не могут скачивать файлы.

Комментариев 8

Офлайн
DracoN 19 августа 2016
А можно ли как нибудь получить длинну нарисованной линии?
Офлайн
Light 20 августа 2016
DracoN, в коде комментарий: "находим дистанцию между А и Б".
Офлайн
DracoN 20 августа 2016
Light,
Спасибо, пропустил.
Офлайн
DracoN 26 августа 2016
Никак не могу понять, есть ли возможность просто нарисовать путь без перемещения самого объекта. Что мои эксперименты ни к чему хорошему не привели(
Офлайн
Light 27 августа 2016
DracoN, соединить все точки линией можно с помощью Line Renderer.
Офлайн
Dragon 3 ноября 2016
Отличная статья - давно искал что нибудь на эту тему! Спасибо!
Офлайн
Bruian 1 февраля 2017
Только начал осваивать Unity и C#, так что просьба не закидывать помидорами. Если в Demo вместо Plane поставить Terrain и сделать возвышенности, персонаж их проходит на ура, а вот линии его движения проваливаются в текстуры возвышенностей. Как это исправить?
Офлайн
Light 2 февраля 2017
Bruian, нужно по другому "рисовать" путь. Возможно в будущих публикациях, решим этот вопрос.
Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.
  • Яндекс.Метрика