Перемещение UI объектов мышкой

Например, в игре есть какие-либо элементы графического интерфейса пользователя: полоска здоровья, окошко системных сообщений, окно чата и тому подобное. И вы хотите дать игроку возможность, разместить все эти элементы так, как ему хочется. Возможность настройки интерфейса, короче говоря. Либо же, взаимодействие с объектами UI может быть частью игрового процесса. В случаи, когда реализуется перемещение обычных 2D или 3D объектов, особых проблем не возникает и примеров, как это сделать достаточно много, один из них есть и у нас. Но если требуется что-то подобное делать с UI объектами, могут возникнуть некоторые трудности. Попробуем решить этот вопрос.

Примечание! Данный метод, предназначен для Canvas в режиме Overlay. То есть, добавляем новый GameObject -> UI -> Canvas, смотрим чтобы у компонента в Render Mode был выбран режим Screen Space - Overlay. Кроме того, все объекты UI с которыми возможно взаимодействие, должны быть дочерними этому. Плюс на все дочерние объекты нужно добавить компонент Event Trigger, ничего с ним делать не нужно, просто добавить и всё. Скрипт цепляется на родителя, то есть Canvas объект.

Приступим. Создаем новый C# скрипт с каким угодно именем.

Подключаем необходимые библиотеки:

using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using UnityEngine.EventSystems;


Нам понадобится несколько переменных. Клавиша для переключения в режим редактирования и обратно. Еще массив, со всеми объектами UI, с которыми мы предстоит работать. И еще одна переменная с текущим объектом, то бишь, который в данный момент выбран для перемещения:

public KeyCode keyCode = KeyCode.Escape;
private GameObject current;
private GameObject[] all;
private bool isLock = false;

Далее, на старте сцены, заполняем массив:

void Start ()
{
	GetAllGUI();
}

void GetAllGUI()
{
	all = new GameObject[transform.childCount];
	for(int j = 0; j < transform.childCount; j++)
	{
		all[j] = transform.GetChild(j).gameObject;
	}
}

Функция, которая добавляет события в обработчик Event Trigger. Событий всего два. Первое, если ЛКМ нажата на каком либо объекте UI, то назначаем в переменную current этот объект. Второе событие, если опять же, нажата ЛКМ и мышка движется, передаем координаты в current:

void AddTriggerEvent()
{
	for(int j = 0; j < all.Length; j++)
	{
		EventTrigger et = null;
		
		GameObject obj = all[j];
		
		if(obj.GetComponent<EventTrigger>())
		{
			et = obj.GetComponent<EventTrigger>();
			
			var t = new EventTrigger.TriggerEvent();
			t.AddListener(data => {
				data.Use();
				current = obj;
			});
			et.delegates.Add(new EventTrigger.Entry{callback = t, eventID = EventTriggerType.PointerDown});
			
			t = new EventTrigger.TriggerEvent();
			t.AddListener(data => {
				var ev = (PointerEventData)data;
				ev.Use();
				current.transform.position = ev.position;
			});
			et.delegates.Add(new EventTrigger.Entry{callback = t, eventID = EventTriggerType.Drag});
		}
	}
}


Теперь, сделаем функцию, которая очищает обработчик, в противном случае двигать UI можно будет всегда, а нам этого не нужно:

void ClearTriggerEvent()
{
	for(int j = 0; j < all.Length; j++)
	{
		EventTrigger et = null;
		
		GameObject obj = all[j];
		
		if(obj.GetComponent<EventTrigger>())
		{
			et = obj.GetComponent<EventTrigger>();
			et.delegates.Clear();
		}
	}
}

Итак, есть еще одна проблема. У нас могут быть объекты типа: кнопки, слайдеры, переключатели и т.п. Все они что-то делают, и если мы будем нажимать на них, чтобы передвинуть в режиме редактирования, тогда сработает назначенное событие. Значит, надо выключить соответствующие компоненты, когда заходим в режим редактирования, затем, включить обратно:

void Lock()
{
	AddTriggerEvent();
	isLock = true;
	for(int j = 0; j < all.Length; j++)
	{
		if(all[j].GetComponent<Button>()) all[j].GetComponent<Button>().interactable = false;
		if(all[j].GetComponent<InputField>()) all[j].GetComponent<InputField>().interactable = false;
		if(all[j].GetComponent<Slider>()) all[j].GetComponent<Slider>().interactable = false;
		if(all[j].GetComponent<Scrollbar>()) all[j].GetComponent<Scrollbar>().interactable = false;
		if(all[j].GetComponent<Toggle>()) all[j].GetComponent<Toggle>().interactable = false;
		if(all[j].GetComponent<Selectable>()) all[j].GetComponent<Selectable>().interactable = false;
	}
}

void UnLock()
{
	ClearTriggerEvent();
	isLock = false;
	for(int j = 0; j < all.Length; j++)
	{
		if(all[j].GetComponent<Button>()) all[j].GetComponent<Button>().interactable = true;
		if(all[j].GetComponent<InputField>()) all[j].GetComponent<InputField>().interactable = true;
		if(all[j].GetComponent<Slider>()) all[j].GetComponent<Slider>().interactable = true;
		if(all[j].GetComponent<Scrollbar>()) all[j].GetComponent<Scrollbar>().interactable = true;
		if(all[j].GetComponent<Toggle>()) all[j].GetComponent<Toggle>().interactable = true;
		if(all[j].GetComponent<Selectable>()) all[j].GetComponent<Selectable>().interactable = true;
	}
}

Ну и ловим нажатие клавиши:

void Update ()
{
	if(Input.GetKeyDown(keyCode) && !isLock) Lock();
	else if(Input.GetKeyDown(keyCode) && isLock) UnLock();
}

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