Улучшенный скрипт выбора юнитов [RTS]

Улучшенный скрипт выбора и выделения юнитов, для игр типа RTS. Кроме привычной рамки, которую мы рисуем мышкой в стратегиях, чтобы выбрать юнитов. В современных играх, всё чаще встречаются и другие полезные функции. Выбор юнитов по двойному клику, например, если мы делаем двойной клик по танку, определенного типа, то автоматов выделяются все похожие юниты, это очень удобно. Еще одна важная фишка, это если удерживать клавишу, например, левый Ctrl и одновременно выбирать юинтов, хоть рамкой, хоть по одному, суть в том, что при этом юниты добавляются. Это похоже на то, как выбирать файлы в Windows.


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

Как и прежде, с начала мы создаем список типов:

using UnityEngine;
using System.Collections;

public enum UnitType {

	Unit_01,
	Unit_02,
	Unit_03
}
Танки, солдаты, КСМ и тому подобное.

Теперь класс, который вешаем на самого юнита:

using UnityEngine;
using System.Collections;

public class UnitComponent : MonoBehaviour {

	[SerializeField] private UnitType _type; // выбрать тип юнита
	[SerializeField] private GameObject _marker; // дочерний объект юнита, который обозначает, что он был выбран
	public bool isSelected { get; private set; }
	public UnitType Type { get{ return _type; } }

	void Start()
	{
		UnitSelect.AddUnit(this);
	}
	
	void OnDestroy()
	{
		// когда юнит уничтожен
		UnitSelect.UnitDestroyed();
	}

	public void Deselect() // вызов, если выделение юнита снято
	{
		if(_marker) _marker.SetActive(false);
		isSelected = false;
	}

	public void Select() // вызов, если юнит был выбран
	{
		if(_marker) _marker.SetActive(true);
		isSelected = true;
	}
}
Указываем тип данного юнита и его маркер.

Далее, сам скрипт выделения и выбора юнитов:

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

public class UnitSelect : MonoBehaviour {

	private enum Mode {Project3D, Project2D};
	private enum UnitSelected {ScreenArea, AllUnits};
	[SerializeField] private Mode mode; // проект в 3D или 2D
	[SerializeField] private int maxUnits = 100; // сколько всего может быть юнитов, под контролем игрока
	[SerializeField] private Image mainRect; // чем будем рисовать рамку
	[SerializeField] private float mainRectFade = 10; // скорость затухания рамки
	[SerializeField] private UnitSelected clickSelect; // выбор юнитов по двойному клику
	[SerializeField] private KeyCode addKey = KeyCode.LeftControl; // клавиша, которую нужно удерживать, для добавления юнитов

	private float timer;
	private float delay = .25f;
	private Rect rect;
	private bool canDraw, one_click, timer_running;
	private Vector2 startPos, endPos;
	private Color original, clear, curColor;
	private static UnitComponent[] _unit;
	private UnitComponent lastUnit, currentUnit;

	public static List<UnitComponent> selected { get; private set; } // массив выбранных юнитов
	public static int unitCount { get; private set; } // текущее количество юнитов всего

	public static void UnitDestroyed()
	{
		unitCount--;
	}

	public static void AddUnit(UnitComponent comp)
	{
		for(int i = 0; i < _unit.Length; i++)
		{
			if(_unit[i] == null)
			{
				_unit[i] = comp;
				unitCount++;
				break;
			}
		}
	}

	void Awake()
	{
		unitCount = 0;
		_unit = new UnitComponent[maxUnits];
		selected = new List<UnitComponent>();
		original = mainRect.color;
		clear = original;
		clear.a = 0;
		curColor = clear;
		mainRect.color = clear;
	}

	void Draw()
	{
		endPos = Input.mousePosition;
		if(startPos == endPos || !canDraw) return;

		curColor = original;

		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)
		);

		mainRect.rectTransform.sizeDelta = new Vector2(rect.width, rect.height);

		mainRect.rectTransform.position = new Vector2(rect.x + mainRect.rectTransform.sizeDelta.x/2, 
			Mathf.Max(endPos.y, startPos.y) - mainRect.rectTransform.sizeDelta.y/2);
	}

	void Deselect()
	{
		foreach(UnitComponent target in selected)
		{
			if(target != null) target.Deselect();
		}
	}

	void SetSelected()
	{
		foreach(UnitComponent target in _unit)
		{
			if(target != null)
			{
				Vector2 pos = Camera.main.WorldToScreenPoint(target.transform.position);
				pos = new Vector2(pos.x, Screen.height - pos.y);

				if(rect.Contains(pos))
				{
					target.Select();
					selected.Add(target);
				}
			}
		}
	}

	void Controls()
	{
		if(Input.GetMouseButtonDown(0))
		{
			if(!one_click)
			{
				// one click
				one_click = true;
				timer = Time.time;

				if(!HitUnit())
				{
					startPos = Input.mousePosition;
					canDraw = true;
					lastUnit = null;
				}
				else
				{
					lastUnit = currentUnit;
				}
			} 
			else
			{
				// double click
				one_click = false;
				canDraw = false;

				if(HitUnit() && lastUnit != null)
				{
					if(lastUnit.GetInstanceID() == currentUnit.GetInstanceID())
					{
						DoubleClickSelected();
					}
				}
			}
		}

		if(one_click)
		{
			if((Time.time - timer) > delay)
			{
				one_click = false;
			}
		}

		if(Input.GetMouseButtonUp(0) && canDraw)
		{
			curColor = clear;
			canDraw = false;
			SetSelected();
		}
	}

	void DoubleClickSelected()
	{
		if(clickSelect == UnitSelected.ScreenArea) // выбрать по двойному клику только тех, кто находится в поле видимости камеры
		{
			Rect area = new Rect(0, 0, Screen.width, Screen.height);

			foreach(UnitComponent target in _unit)
			{
				if(target != null && !target.isSelected)
				{
					Vector2 pos = Camera.main.WorldToScreenPoint(target.transform.position);
					pos = new Vector2(pos.x, Screen.height - pos.y);

					if(area.Contains(pos) && lastUnit.Type == target.Type)
					{
						target.Select();
						selected.Add(target);
					}
				}
			}
		}
		else
		{
			foreach(UnitComponent target in _unit)
			{
				if(target != null && !target.isSelected)
				{
					if(lastUnit.Type == target.Type)
					{
						target.Select();
						selected.Add(target);
					}
				}
			}
		}
	}

	void Update()
	{
		Controls();

		Draw();

		mainRect.color = Color.Lerp(mainRect.color, curColor, mainRectFade * Time.deltaTime);
	}

	bool HitUnit() // клик по юниту или нет
	{
		if(!Input.GetKey(addKey))
		{
			Deselect();
			selected.Clear();	
		}

		rect = new Rect();
		currentUnit = null;

		if(mode == Mode.Project3D)
		{
			RaycastHit hit;
			Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

			if(Physics.Raycast(ray, out hit))
			{
				currentUnit = hit.collider.GetComponent<UnitComponent>();
			}
		}
		else
		{
			RaycastHit2D hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Input.mousePosition), Vector2.zero);

			if(hit.transform != null)
			{
				currentUnit = hit.collider.GetComponent<UnitComponent>();
			}
		}

		if(currentUnit != null)
		{
			currentUnit.Select();
			selected.Add(currentUnit);
			return true;
		}

		return false;
	}
}
Работает как с 2D так и с 3D проектами. Выбор юнитов по двойному клику разбит на две опции, в первом случае выбираются юниты только в рамках экрана, в другом будут выбраны все доступные на карте. По умолчанию режим добавления включается, если удерживать клавишу "LeftControl", но можно назначит любую другую разумеется. Режим добавления работает с любым методом выбора юнитов: рамкой, кликом или двойным кликом.

Чтобы получить общие число юнитов на карте:

int count = UnitSelect.unitCount;

Обработка выбранных юнитов:

for(int i = 0; i < UnitSelect.selected.Count; i++)
{
	Debug.Log("делаем что-то с юнитом --> " + UnitSelect.selected[i].gameObject.name);
}

Скачать скрипты:

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