Выделение объектов / юнитов, как в стратегии

Выделение нескольких юнитов, как в стратегии или похожей игре, с помощью рамки, которую рисуем мышкой. Как это сделать? Сегодня решим данную проблему. На самом деле, у нас несколько вопросов, требующих решения. Во-первых, рисование прямоугольника или квадрата, путем перетаскивания курсора по диагонали, т.е. между стартовой и конечной позицией. Затем, нужно трансформировать позиции объектов/юнитов в пространство экрана, так как, рамка рисуется в нем. Следующий шаг, определить - находится юнит в рамке или нет. И чтобы иметь возможность обращаться к объектам, давая какие-нибудь например, команды, необходимо все выделенные в данный момент объекты, добавить в массив.


Без лишней болтологии, начнем. В начале, создаем скин для GUI, выбираем какую нибудь папку проекта, правый клик > Create > GUI Skin. В нем мы сможем настроить элемент Box, а сам скин незабываем добавить в скрипт.

Теперь, создаем C# скрипт SelectObjects:

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

public class SelectObjects : MonoBehaviour {

	public static List<GameObject> unit; // массив всех юнитов, которых мы можем выделить
	public static List<GameObject> unitSelected; // массив выделенных юнитов

	public GUISkin skin;
	private Rect rect;
	private bool draw;
	private Vector2 startPos;
	private Vector2 endPos;

	void Awake () 
	{
		unit = new List<GameObject>();
		unitSelected = new List<GameObject>();
	}

	// проверка, добавлен объект или нет
	bool CheckUnit (GameObject unit) 
	{
		bool result = false;
		foreach(GameObject u in unitSelected)
		{
			if(u == unit) result = true;
		}
		return result;
	}

	void Select()
	{
		if(unitSelected.Count > 0)
		{
			for(int j = 0; j < unitSelected.Count; j++)
			{
				// делаем что-либо с выделенными объектами
				unitSelected[j].GetComponent<MeshRenderer>().material.color = Color.red;
			}
		}
	}

	void Deselect()
	{
		if(unitSelected.Count > 0)
		{
			for(int j = 0; j < unitSelected.Count; j++)
			{
				// отменяем то, что делали с объектоми
				unitSelected[j].GetComponent<MeshRenderer>().material.color = Color.white;
			}
		}
	}
	
	void OnGUI ()
	{
		GUI.skin = skin;
		GUI.depth = 99;

		if(Input.GetMouseButtonDown(0))
		{
			Deselect();
			startPos = Input.mousePosition;
			draw = true;
		}

		if (Input.GetMouseButtonUp(0)) 
		{
			draw = false;
			Select();
		}
		
		if(draw)
		{
			unitSelected.Clear();
			endPos = Input.mousePosition;
			if(startPos == endPos) return;

			rect = new Rect(Mathf.Min(endPos.x, startPos.x),
			                Screen.height - Mathf.Max(endPos.y, startPos.y),
			                Mathf.Max(endPos.x, startPos.x) - Mathf.Min(endPos.x, startPos.x),
			                Mathf.Max(endPos.y, startPos.y) - Mathf.Min(endPos.y, startPos.y)
			                );
			
			GUI.Box(rect, "");

			for(int j = 0; j < unit.Count; j++)
			{
				// трансформируем позицию объекта из мирового пространства, в пространство экрана
				Vector2 tmp = new Vector2(Camera.main.WorldToScreenPoint(unit[j].transform.position).x, Screen.height - Camera.main.WorldToScreenPoint(unit[j].transform.position).y);

				if(rect.Contains(tmp)) // проверка, находится-ли текущий объект в рамке
				{
					if(unitSelected.Count == 0)
					{
						unitSelected.Add(unit[j]);
					}
					else if(!CheckUnit(unit[j]))
					{
						unitSelected.Add(unit[j]);
					}
				}
			}
		}
	}
}

Как видно, в ключевых местах есть комментарий, поэтому разобраться что и как не должно составить особого труда.

Внимание! Чтобы всё работало, все активные объекты должны быть добавлены в массив unit. Соответственно в вашем скрипте, который есть на юнитах, в функцию Start необходимо добавить строчку:

SelectObjects.unit.Add(gameObject);

Для большей наглядности, вы можете скачать готовый проект по теме:

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

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

Офлайн
welleman 25 ноября 2017
Отличная статья.
1 момент мне не нравится. В методе Deselect() выбранные юниты окрашиваются в белый цвет, это оч круто, но они же по прежнему остаются в unitSelected листе.
Мне кажется логичнее было бы его (unitSelected) чистить.

void Deselect() {
unitSelected.Clear();
for (int i = 0; i < units.Count; i++) {
units[i].GetComponent<MeshRenderer>().material.color = Color.white;
}
}
Офлайн
Golden50k 18 августа 2017
Отличный сайт, спасибо за всё, сильно выручаете!
Офлайн
На сайте так много интересного.
Офлайн
Спасибо за материал, очень хорошие комментарии и легко читается!
Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.
  • Яндекс.Метрика