Выбор и выделение объекта [3D]

Данный скрипт работает только с 3D физикой и может подойти как для стратегических игр, так и для, например, ролевой игры с видом сверху. В прошлый раз мы публиковали материал, где показан пример выбор множества юнитов рамкой, которая рисуется мышкой. Этот вариант больше подойдет для взаимодействия с небольшой группой, наподобие, как в игре Divinity Original Sin. Скрипт работает с двумя указателями, они могут быть разного цвета или с разной анимацией, один будет прицеплен на объект, когда тот попадает под курсор, второй добавляется на объект, если по нему кликнуть. Если кликнуть в пустую область, выделение будет снято.


Для начала нужно создать пару префабов, то есть тех самых указателей, о которых говорилось выше и они же есть на видео. Как именно они будут сделаны не важно, главное чтобы по умолчанию (без регулировки вращения) располагались на плоскости XZ. Допустим это может быть обычный меш квадрат с текстурой:

Выбор и выделение объекта [3D]

Когда префабы готовы, добавляем на сцену скрипт MainSelect:

using UnityEngine;
using System.Collections;

public class MainSelect : MonoBehaviour {

	public LayerMask layerMask; // слои с которыми требуется взаимодействие
	public GameObject _hover; // указатель, если объект под курсором
	public GameObject _select; // указатель, если сделан клик по объекту

	private static GameObject curObj, hoverObj, selectObj;
	private int obj_id, last_id;
	private LayerMask layerMaskInvert;

	void Awake()
	{
		// дополнительная маска нужна, чтобы рейкаст игнорировал выбранные слои и слой по умолчанию Ignore Raycast
		layerMaskInvert = layerMask.value | 1 << 2; // копируем выбранные слои и добавляем к ним еще один
		layerMaskInvert = ~layerMaskInvert; // инвертируем маску
	}

	public static GameObject current
	{
		get { return curObj; }
	}

	void Select() // выбор объекта
	{
		GameObject obj = GetObject();
		if(obj == null) // сброс
		{
			Destroy(selectObj);
			Destroy(hoverObj);
			obj_id = 0;
			curObj = null;
			selectObj = null;
			hoverObj = null;
			return;
		}
		if(!selectObj) selectObj = Instantiate(_select) as GameObject;
		selectObj.transform.parent = obj.transform;
		selectObj.transform.position = GetPosition(obj.transform.position, layerMaskInvert);
		curObj = obj;
		Destroy(hoverObj);
		hoverObj = null;
	}

	void Hover() // вешаем указатель на объект
	{
		GameObject obj = GetObject();
		if(last_id != obj_id && obj)
		{
			if(!hoverObj) hoverObj = Instantiate(_hover) as GameObject;
			hoverObj.transform.position = GetPosition(obj.transform.position, layerMaskInvert);
			hoverObj.transform.parent = obj.transform;
		}
		last_id = obj_id;
	}

	GameObject GetObject() // получаем объект и его хеш код
	{
		GameObject obj = null;
		RaycastHit hit;
		Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
		if(Physics.Raycast(ray, out hit, Mathf.Infinity, layerMask))
		{
			obj_id = hit.transform.gameObject.GetHashCode();
			obj = hit.transform.gameObject;
		}
		return obj;
	}

	Vector3 GetPosition(Vector3 position, LayerMask layers) // находим поверхность под объектом
	{
		Vector3 result = position;
		RaycastHit hit;
		Ray ray = new Ray(position, Vector3.down);
		if(Physics.Raycast(ray, out hit, Mathf.Infinity, layers))
		{
			result = hit.point + hit.normal * 0.05f;
		}
		return result;
	}

	void Update()
	{
		if(Input.GetMouseButtonDown(0))
		{
			Select();
		}
		else
		{
			Hover();
		}
	}
}

Стоит отметить, что функция GetPosition определяет позицию для указателя, работает это так. От позиции объекта, пускаем луч вниз, он игнорирует коллайдер данного объекта и слой Ignore Raycast, на случай, если объект в триггере (разумеется у всех триггеров должен быть данный слой установлен), далее луч попадает в "землю" и возвращает позицию. Таким образом, указатель устанавливается, например, под ногами персонажа.

Публичная переменная current будет ссылаться на текущий выбранный объект. Доступ к ней можно получить из любого другого скрипта и делать какие угодно запросы, допустим, получить имя:

Debug.Log(MainSelect.current.name);

Как-то так, в общем.

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

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

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

Офлайн
welleman 23 ноября 2017
Light,
Спасибо большое за статью. smile
Наконец-то разобрался как рисовать прямоугольник и выделять юнитов ))))))
Офлайн
Light 16 июня 2017
MACTEP_SCHEFFFF, в оригинале ошибок не обнаружено.
Офлайн
NullReferenceException: Object reference not set to an instance of an object
MainSelect.GetObject () (at Assets/Scripts/MainSelect.cs:64)
MainSelect.Hover () (at Assets/Scripts/MainSelect.cs:50)
MainSelect.Update () (at Assets/Scripts/MainSelect.cs:93)
Офлайн
А все, разобрался, зенкю=)
Офлайн
А как мне допустим сделать, что бы при выборе юнита я мог делать какие нибудь доп. действия? Скажем, чтоб он начал выполнять действия из другого скрипта, или чтоб к нему присвоился скрипт? Я новичок так что не обесудьте)
Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.
  • Яндекс.Метрика