Система подсказок для 2D, 3D и UI

Иногда в играх, для уточнения каких-нибудь деталей интерфейса, или описания игровых объектов, просто незаменимы всплывающие подсказки/тултипы, при наведении курсора. Реализацией подобной задачи, мы и займемся. При этом, нам нужно учитывать несколько важных пунктов: подсказки не должны выходить за границы экрана; текст тултипа должен сохранять стандартные теги редактора; должна быть возможность менять локаль подсказок. Кроме этого, такая система подсказок, должна работать не только с двухмерными или трехмерными объектами, но и элементами UI. Весь текст тултипа у нас будет храниться в одном текстовом файле, и для того, чтобы переключить язык подсказок, нужно будет просто подгрузить другой файл.


Чтобы сделать разные локали в игре, создаем в проекте папку Resources, а в ней еще одну Tooltip, затем, в эту папку можно добавлять текстовые файлы, для русской локали, например, файл Russian.txt и т.п.

Внимание! Текст файла должен иметь следующий вид:

ключ-1
текст-1 текст-1 текст-1

ключ-2
текст-2 текст-2 текст-2

ключ-3
текст-3 текст-3 текст-3


Именно в током порядке: строка с ключом, ниже строка с текстом тултипа и т.д. Обратите внимание, что текст тултипа должен быть написать в одну строку, для переноса строк используется специальный тег!

Ключ для тултипа мы придумываем и пишем в триггере:

using System.Collections;
using UnityEngine;

public class TooltipTrigger : MonoBehaviour {

	[SerializeField] private string key; // ключ тултипа
	public string Key {get{return key;}}
}

Это скрипт-триггер, который мы цепляем на объекты, чтобы показывать подсказки.

Далее, нам нужно получить доступ к UI объектам. Ищем на сцене объект EventSystem:

Система подсказок для 2D, 3D и UI

Удаляем стандартный скрипт Standalone Input Module и заменяем на свой:

using UnityEngine.EventSystems;
using UnityEngine;

public class StandaloneModule : StandaloneInputModule {

	public PointerEventData GetPointerEventData(int id)
	{
		return GetLastPointerEventData(id);
	}
}

Должно получиться следующие:


Теперь можно сделать шаблон тултипа. Это обычный UI Image с фиксированный шириной, а дочерним ему делаем UI Text. Получается родитель у нас картинка, и дочерний текст. Так вот на родителя вешаем:

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

public class TooltipComponent : MonoBehaviour {

	[SerializeField] private float border = 5; // отступ текста
	[SerializeField] private Text content; // дочерний текст
	[SerializeField] private RectTransform rect; // родитель
	public float Border {get{return border;}}
	public Text Content {get{return content;}}
	public RectTransform Rect {get{return rect;}}

	void OnValidate()
	{
		if(content != null)
		{
			border = Mathf.Abs(border);
			content.rectTransform.anchorMin = Vector2.zero;
			content.rectTransform.anchorMax = Vector2.one;
			content.rectTransform.offsetMax = -new Vector2(border, border);
			content.rectTransform.offsetMin = new Vector2(border, border);
		}
	}
}

Это и есть наш тултип.

На Canvas цепляем управляющий класс:

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

public class TooltipSystem : MonoBehaviour {

	private enum ProjectMode {Project3D, Project2D};
	[SerializeField] private ProjectMode project;
	[SerializeField] private TooltipComponent tooltip; // шаблон тултипа
	[SerializeField] private float cursorBorder = 10; // отступ от курсора
	[SerializeField] private string folder = "Tooltip"; // папка в Resources с локалями
	private List<Data> data;
	private TooltipTrigger trigger;
	private string locale;
	private static TooltipSystem _inst;
	private bool showTooltip;

	struct Data
	{
		public string hint, content;
	}

	void OnValidate()
	{
		cursorBorder = Mathf.Abs(cursorBorder);
	}

	public static void SwitchLocale(string value) // метод смена локали, например, Russian или English (текстовые файлы в Resources)
	{
		_inst.SwitchLocale_inst(value);
	}

	void SwitchLocale_inst(string value)
	{
		locale = value;

		PlayerPrefs.SetString("TooltipLocale", value);

		TextAsset binary = Resources.Load<TextAsset>(folder + "/" + locale);

		if(binary == null) return;

		data = new List<Data>();

		string[] lines = binary.text.Split(new string[]{System.Environment.NewLine}, System.StringSplitOptions.RemoveEmptyEntries);

		for(int i = 0; i < lines.Length - 1; i++)
		{
			Data part = new Data();
			part.hint = lines[i];
			part.content = lines[i + 1].Replace("<br>", "\n");
			data.Add(part);
		}
	}

	void Awake()
	{
		_inst = this;
		locale = PlayerPrefs.GetString("TooltipLocale", "Russian");
		tooltip.gameObject.SetActive(false);
		SwitchLocale_inst(locale);
	}

	void LateUpdate()
	{
		Pointer();
	}

	void SetTrigger(TooltipTrigger value)
	{
		if(trigger == null || trigger.Key.CompareTo(value.Key) != 0)
		{
			trigger = value;

			for(int i = 0; i < data.Count; i++)
			{
				if(trigger.Key.CompareTo(data[i].hint) == 0)
				{
					tooltip.Content.text = data[i].content;
					tooltip.Rect.sizeDelta = new Vector2(tooltip.Rect.sizeDelta.x, tooltip.Content.preferredHeight + tooltip.Border * 2);
					break;
				}
			}
		}
		else if(value != null)
		{
			showTooltip = true;
		}
	}

	void Pointer()
	{
		showTooltip = false;

		if(EventSystem.current.IsPointerOverGameObject())
		{
			var module = EventSystem.current.currentInputModule as StandaloneModule;
			SetTrigger(module.GetPointerEventData(-1).pointerEnter.GetComponent<TooltipTrigger>());
		}
		else
		{
			if(project == ProjectMode.Project3D)
			{
				RaycastHit hit;
				Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

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

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

		if(tooltip.gameObject.activeSelf && !showTooltip)
		{
			tooltip.gameObject.SetActive(false);
		}
		else if(!tooltip.gameObject.activeSelf && showTooltip)
		{
			tooltip.gameObject.SetActive(true);
		}

		TooltipPosition();
	}

	void TooltipPosition()
	{
		if(!showTooltip) return;
		Vector2 mouse = Input.mousePosition;
		float x = mouse.x + cursorBorder + tooltip.Rect.sizeDelta.x;
		x = (x > Screen.width) ? mouse.x - cursorBorder - tooltip.Rect.sizeDelta.x / 2 : mouse.x + cursorBorder + tooltip.Rect.sizeDelta.x / 2;
		float y = mouse.y - cursorBorder - tooltip.Rect.sizeDelta.y;
		y = (y < 0) ? mouse.y + cursorBorder + tooltip.Rect.sizeDelta.y / 2 : mouse.y - cursorBorder - tooltip.Rect.sizeDelta.y / 2;
		tooltip.Rect.position = new Vector3(x, y, tooltip.Rect.position.z);
	}
}

Для того чтобы переключить язык тултипа, вызываем метод:

TooltipSystem.SwitchLocale("English");

Это значит, что в ресурсах уже должен лежать файл English.txt.

Текстовые теги:

<b></b> - жирный текст
<i></i> - текст курсивом
<size=20></size> - размер текста
<color=#00ffffff></color> - цвет текста
<br> - перенос строки


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

У вас нет доступа!
Тестировалось на: Unity 2017.2.0

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

Офлайн
SlavaXD 30 ноября 2017
бро а нет урока по RayCast 2D?
Офлайн
Light 1 декабря 2017
SlavaXD, а здесь разве нет 2д рейкаста? По моему есть.
Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.
  • Дешевый хостинг
  • Яндекс.Метрика