Визуализация пути для 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

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