Указатель цели для персонажа 3D или 2D проект

Данная статья будет полезна тем, кто хочет сделать свою стратегическую игру, или же игру где персонаж должен перемещаться в точку, куда игрок, кликнул мышкой. Основная задача, сделать указатель цели и пока объект движется к цели, иконка указателя остается активной, как только пункт назначения достигнут, иконка исчезает. Более того, в 3D игре могут быть различные склоны, холмы и тому подобное, в общем, надо чтобы указатель поворачивался соответствующе, проще говоря «ложился» на плоскость. Навигация персонажа и обход препятствий можно сделать как для 3D, так и 2D проекта.


Прежде всего нужна карта, по которой будет гулять наш персонаж. Важно понимать, что карта навигации создается на плоскости Х и Z в виртуальном пространстве. Для наглядности создайте новый 3D проект и добавьте на сцену стандартный Plane, сбросить позицию и вращение по нулям. Вот по этой плоскости и будет "нарисована" карта навигации. Например, можно еще немного увеличить размер Plane и поместить на него кубы, которые будут препятствием. Объектам, по которым персонаж может перемещаться, иначе говоря "земля", надо назначить отдельный тег (в примере ниже, использован стандартный тег "GameController"), чтобы невозможно было поместить указатель куда-нибудь в недоступное место. После того как поверхность и прочие объекты на ней будут расставленны по местам, всё это стоит сгруппировать и установить галочку Static:

Указатель цели для персонажа 3D или 2D проект

И для вложенных тоже.

Затем, на вкладке Navigation, в разделе Bake, отрегулировать параметры в зависимости от исходной поверхности и нажать кнопку Bake (внизу справа).

Указатель цели для персонажа 3D или 2D проект

Должно получится что-то наподобие этого.

Добавим пустой объект и на него вешаем CurrentNavigation:

using UnityEngine;
using System.Collections;

public class CurrentNavigation : MonoBehaviour {

	public static bool isPointer;
	public static Vector3 target;
	public GameObject targetPointer;

	void Start () 
	{
		targetPointer.SetActive(false);
	}

	void Update () 
	{
		targetPointer.SetActive(isPointer);

		RaycastHit hit;
		Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
		if (Physics.Raycast(ray, out hit))
		{
			if(Input.GetMouseButtonDown(0))
			{
				if(hit.transform.tag == "GameController")
				{
					target = hit.point;
					targetPointer.transform.localRotation = Quaternion.FromToRotation(Vector3.forward, hit.normal);
					targetPointer.transform.position = hit.point;
				}
			}
		}
	}
}

Здесь мы передаем координаты персонажу и устанавливаем указатель (targetPointer).

Кстати, для создания целеуказателя лучше использовать UI, по разным причинам, так как возможно применить динамический текст, либо RenderTexture и прочие вкусности. В любом случае, будет больше возможностей, нежели с обычным спрайтом или мешем. Поэтому, добавим на сцену Canvas, а к нему Image, то есть спрайт указателя. Настроим всё, как показано на скриншотах:

Указатель цели для персонажа 3D или 2D проект
Указатель цели для персонажа 3D или 2D проект

Картинка немного сдвинута по Z, дело в том, что указатель будет разворачиваться по этой оси, то есть, ось будет от поверхности, "смотреть" вверх. Чтобы изображение не сливалось с другими на плоскости, оно чуть сдвинуто. А размер иконки настраивается по Width/Height, как показано выше.

Далее, персонаж. Для теста подойдет любой объект, пусть будет обычная сфера и на нее вешаем скрипт PlayerNavigation:

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(NavMeshAgent))]

public class PlayerNavigation : MonoBehaviour {
	
	private NavMeshAgent nav;

	void Start () 
	{
		nav = GetComponent<NavMeshAgent>();
		CurrentNavigation.target = transform.position;
	}

	void Update () 
	{
		CurrentNavigation.isPointer = nav.hasPath;
		nav.SetDestination(CurrentNavigation.target);
	}
}

Скрипт автоматически добавит NavMeshAgent на объект, где можно настроить скорость движения/вращения и прочее. Внимание! "Лицом" персонажа будет считаться ось Z, иначе говоря, куда она указывает, значит туда "смотрим", когда NavMeshAgent будет разворачивать объект, во время движения. Учитывайте этот момент.

Пара слов о 2D. Проблема в том, что если делать игру на спрайтах и 2D физике, тогда не получился развернуть спрайты, физика работать не будет. А ранее уже говорили, что плоскость XZ, это значит, что спрайты нужно повернуть по Х на 90 градусов. Камеру, так же нужно развернуть и переключить в режим Orthographic, чтобы она смотрела на нужную нам плоскость. Получается, выход в комбинировании 2D спрайта с 3D физикой. Смысл вот в чем, делаем из картинок карту игры, травку, полянку, домики и прочее. После, когда всё будет готово, как и ране делаем поверхность, а там где проход невозможен (стены, забор и т.п.) в этих местах ставим кубы и подгоняем по размеру. Останется только создать карту навигации и всё.

Как бы две игровые карты надо делать, одна для навигации, а другая - графика.

Для наглядности, есть готовый проект по теме, где можно всё посмотреть и пощупать.

Скачать проект:

У вас нет доступа!

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

Офлайн
rgrnrok 19 мая 2015
Большое спасибо за статью, то что надо
Офлайн
Max Carter 17 августа 2015
Подправьте и перезалейте пожалуйста проект.
При импорте пакета ни анимации, ни сцены не понимает, видит как пустой файл. Испытывал на разных версиях Юнити.

Спасибо за статью.
Офлайн
Light 17 августа 2015
Цитата: Max Carter
Подправьте и перезалейте пожалуйста проект.

Протестировано на версии 5.1.2, проблем не обнаружено, на всякий случай обновил пакет.
Офлайн
Max Carter 19 августа 2015
Light,
Спасибо огромное! Хорошего, вам, вечера и вдохновения
Офлайн
Vitremer 6 июля 2016
Спасибо за статью - только несколько вопросов:
1. Возможно в первом скрипте CurrentNavigation стоит передвинуть условие if(Input.GetMouseButtonDown(0))
над условием и строками
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit))
В этом случае рейкаст не будет обсчитываться каждый кадр, а только когда мы нажмем клавишу.
2. Я похоже плохо разбираюсь пока в свойствах статичных полей и свойств(простите за каламбур), но каким образом вы обращаетесь из второго скрипта к первому сторокой
CurrentNavigation.target = transform.position;
и строкой
CurrentNavigation.isPointer = nav.hasPath;
?
То есть, если у не статичного класса имеются статичные свойства, мы можем обращаться к ним из другого класса, не беря ссылку на обьект строкой CurrentNavigation curNav = GetComponent<CurrentNavigation>();
?
3. Ну и последнее - без обид, но можно исправить слова в абзаце сразу после первого скрипта: "В любом случаи" на "В любом случае" и в том же предложении слово "мешом" на "мешем"))
Офлайн
Light 6 июля 2016
Цитата: Vitremer
стоит передвинуть условие

Рейкаст может использоваться не только для одной конкретной цели, поэтому было сделано так. Но кому не нужно, конечно стоит изменить.

Цитата: Vitremer
статичных полей

Подробнее здесь http://professorweb.ru/my/csharp/charp_theory/level5/5_12.php
Офлайн
Vitremer 6 июля 2016
Light,
Насчет такого свойства статичных полей не знал - спасибо буду юзать в будущем.
Офлайн
Доброго времени суток. Столкнулся с проблемой. Юнит не желает двигаться(при этом он реагирует на указанную точку, т.е смотрит в указанную сторону). Навмэш есть, все скрипты и компоненты висят как нужно. Ошибок в консоли нет.
Можете подсказать в чем загвоздка? Спасибо.
Офлайн
Light 25 сентября 2016
Ленин-Гриб, вероятно что-то было изменено, в самом демо проблем нет.
Офлайн
Light,
В самом демо тоже проблем не испытываю. Но при попытке переноса на другой проект или сцену возникает описанная ситуация.(Тестировал на чистом проекте, без сторонних скриптов и изменений)
P.S
Ладно. Буду разбираться. Спасибо большое за быстрый отзыв и ваши уроки. Очень хороший ресурс
Офлайн
Light 25 сентября 2016
Ленин-Гриб, собери пакет unitypackage, где юнит не хочет двигаться, посмотрим.
Офлайн
Light,
Все разобрался сам. Был какой-то глюк юнити. Сбрасывал тэг на террейне при запуске. Из-за этого рейкаст не находил "пригодной" поверхности. Проверил на ноуте. Все работает.
Прошу прощения за беспокойство.
Офлайн
Wildfowl 25 марта 2017
Здравствуйте,вопрос такой-почему при запуске игры экран становиться просто белым?
Офлайн
Light 26 марта 2017
Wildfowl, причина явно не в "указателе цели".
Офлайн
Wildfowl 26 марта 2017
А Target Pointer откуда берётся?
Офлайн
Wildfowl 26 марта 2017
Прощу прощения,вопрос другой мне в скрипте PlayerNavigation пишут двусмысленность между PLayerNavigation.nav,я пока новичок поэтому не понимаю в чём проблема,поможете?
Офлайн
Wildfowl 26 марта 2017
Я понял проблему не обращайте на меня внимания,извините за беспокоиство
Офлайн
Скрипт нуждается в поправке, в новой версии юнити такая схема(PlayerNavigation) не полностью работает, а точнее, изменен вызов некоторых объектов

using UnityEngine;

[RequireComponent(typeof(UnityEngine.AI.NavMeshAgent))]

public class PlayerNavigation : MonoBehaviour {

private UnityEngine.AI.NavMeshAgent nav;
void Start()
{
nav = GetComponent<UnityEngine.AI.NavMeshAgent>();
CurrentNavigations.target = transform.position;
}

void Update()
{
CurrentNavigations.isPointer = nav.hasPath;
nav.SetDestination(CurrentNavigations.target);
}

}
Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.
  • Дешевый хостинг
  • Яндекс.Метрика