Всплывающая подсказка [Tooltip]

Всплывающая подсказка для Unity, которая может показывать какой-нибудь текст, если курсор наводится на UI объект, или другой объект с коллайдером и скриптом, содержащим текст подсказки. Делать Tooltip с фиксированной позицией и размерами, мы не станем, так как это достаточно просто. Наш вариант будет привязан к позиции курсора, в рамках конкретного объекта. Кроме всего прочего, визуально это будет не только текст с фоном, к этой конструкции приделаем еще и стрелку, позиция которой относительно фона, будет рассчитываться с учетом расположения в нижней или верхней части. Размер Tooltip-а по ширине имеет ограничения, а вот высота регулируется в зависимости от длины текста.


Сделаем шаблон. Добавляем на сцену Canvas и удаляем компонент Graphic Raycaster, так как нам не нужно взаимодействие курсора с Tooltip-ом. Теперь в Canvas добавим изображение для фона, назем его Box, просто обычный квадрат. Добавим стрелку Arrow, это спрайт треугольника, который по умолчанию повернут так, чтобы указывать вниз. Добавим текстовый UI под именем Text.



Делаем Text и Arrow - дочерними Box.

Устанавливаем пресеты Rect Transform.

Для Box и Arrow:

Всплывающая подсказка [Tooltip]

Для Text:


Далее, на Canvas вешаем скрипт Tooltip:

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

public class Tooltip : MonoBehaviour {

	public static string text;
	public static bool isUI;

	public Color BGColor = Color.white;
	public Color textColor = Color.black;
	public enum ProjectMode {Tooltip3D = 0, Tooltip2D = 1};
	public ProjectMode tooltipMode = ProjectMode.Tooltip3D;
	public int fontSize = 14; // размер шрифта
	public int maxWidth = 250; // максимальная ширина Tooltip
	public int border = 10; // ширина обводки
	public RectTransform box;
	public RectTransform arrow;
	public Text boxText;
	public Camera _camera;
	public float speed = 10; // скорость плавного затухания и проявления

	private Image[] img;
	private Color BGColorFade;
	private Color textColorFade;

	void Awake()
	{
		img = new Image[2];
		img[0] = box.GetComponent<Image>();
		img[1] = arrow.GetComponent<Image>();
		box.sizeDelta = new Vector2(maxWidth, box.sizeDelta.y);
		BGColorFade = BGColor;
		BGColorFade.a = 0;
		textColorFade = textColor;
		textColorFade.a = 0;
		isUI = false;
		foreach(Image bg in img)
		{
			bg.color = BGColorFade;
		}
		boxText.color = textColorFade;
		boxText.alignment = TextAnchor.MiddleCenter;
	}

	void LateUpdate()
	{
		bool show = false;
		boxText.fontSize = fontSize;

		if(tooltipMode == ProjectMode.Tooltip3D)
		{
			RaycastHit hit;
			Ray ray = _camera.ScreenPointToRay(Input.mousePosition);
			if(Physics.Raycast(ray, out hit))
			{
				if(hit.transform.GetComponent<TooltipText>())
				{
					text = hit.transform.GetComponent<TooltipText>().text;
					show = true;
				}
			}
		}
		else
		{
			RaycastHit2D hit = Physics2D.Raycast(_camera.ScreenToWorldPoint(Input.mousePosition), Vector2.zero);
			if(hit.transform != null)
			{
				if(hit.transform.GetComponent<TooltipText>())
				{
					text = hit.transform.GetComponent<TooltipText>().text;
					show = true;
				}
			}
		}

		boxText.text = text;
		float width = maxWidth;
		if(boxText.preferredWidth <= maxWidth - border) width = boxText.preferredWidth + border;
		box.sizeDelta = new Vector2(width, boxText.preferredHeight + border);

		float arrowShift = width / 4; // сдвиг позиции стрелки по Х

		if(show || isUI)
		{
			float arrowPositionY = -(arrow.sizeDelta.y / 2 - 1); // позиция стрелки по умолчанию (внизу)
			float arrowPositionX = arrowShift;
			
			float curY = Input.mousePosition.y + box.sizeDelta.y / 2 + arrow.sizeDelta.y;
			Vector3 arrowScale = new Vector3(1, 1, 1);
			if(curY + box.sizeDelta.y / 2 > Screen.height) // если Tooltip выходит за рамки экрана, в данном случаи по высоте
			{
				curY = Input.mousePosition.y - box.sizeDelta.y / 2 - arrow.sizeDelta.y;
				arrowPositionY = box.sizeDelta.y + (arrow.sizeDelta.y / 2 - 1);
				arrowScale = new Vector3(1, -1, 1); // отражение по вертикале
			}
			
			float curX = Input.mousePosition.x + arrowShift;
			if(curX + box.sizeDelta.x / 2 > Screen.width)
			{
				curX = Input.mousePosition.x - arrowShift;
				arrowPositionX = width - arrowShift;
			}
			
			box.anchoredPosition = new Vector2(curX, curY);
			
			arrow.anchoredPosition = new Vector2(arrowPositionX, arrowPositionY);
			arrow.localScale = arrowScale;

			foreach(Image bg in img)
			{
				bg.color = Color.Lerp(bg.color, BGColor, speed * Time.deltaTime);
			}
			boxText.color = Color.Lerp(boxText.color, textColor, speed * Time.deltaTime);
		}
		else
		{
			foreach(Image bg in img)
			{
				bg.color = Color.Lerp(bg.color, BGColorFade, speed * Time.deltaTime);
			}
			boxText.color = Color.Lerp(boxText.color, textColorFade, speed * Time.deltaTime);
		}
	}
}

Чтобы получать текстовую информацию с 3D или 2D, игровых объектов у которых есть свой коллайдер, на них нужно повесить скрипт TooltipText с соответствующем текстом подсказки:

using UnityEngine;
using System.Collections;

public class TooltipText : MonoBehaviour {

	public string text;

}

Чтобы получать текст с UI объектов, вне зависимости от выбранного режима tooltipMode, на них нужно цеплять скрипт TooltipTextUI с несколько другим кодом, чем для обычных объектов:

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

public class TooltipTextUI : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler {
	
	public string text;
	
	void IPointerEnterHandler.OnPointerEnter(PointerEventData e)
	{
		Tooltip.text = text;
		Tooltip.isUI = true;
	}
	
	void IPointerExitHandler.OnPointerExit(PointerEventData e)
	{
		Tooltip.isUI = false;
	}
}

Собственно, как-то так...

Скачать демо сцену:

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

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

Офлайн
anbee 16 апреля 2016
А как этот скрипт можно подправить, чтобы он срабатывал не просто при наведении мыши, а при наведении и нажатии левой кнопки мыши. (пока клавиша нажата, подсказка высвечивается)?
Офлайн
Light 16 апреля 2016
Цитата: anbee
А как этот скрипт можно подправить, чтобы он срабатывал не просто при наведении мыши, а при наведении и нажатии левой кнопки мыши. (пока клавиша нажата, подсказка высвечивается)?

В начале функции LateUpdate, перед if(tooltipMode == ProjectMode.Tooltip3D)...
Добавить строчку:

if(!Input.GetMouseButton(0)) return;

То есть, пока ЛКМ не нажата, ничего не будет происходить.
Офлайн
Slava XD 9 июля 2016
25-я секунда видео нижний левый угол экрана)
Офлайн
siriusspark 14 сентября 2017
Slava XD, это не имеет значения, важна суть
Офлайн
khokhlov30 1 августа 2019
Меняю разрешение экрана и тултип смещается далеко. Подскажите, как решить.
Спасибо
Офлайн
Light 1 августа 2019
khokhlov30, это в демо так или тултип сделан по другому?
Офлайн
khokhlov30 1 августа 2019
тултип сделал по описанию.
сделан на моей сцене
Офлайн
khokhlov30 1 августа 2019
скачал демку.... работает норм. буду искать у себя ошибки. прошу прощения, мои недочеты. спасибо. всего Вам.

дело было не кабине.. тултип отдельно от канваса, и сам тултип сделан тоже из отдельного канваса... о как... без демки ни за что бы не разобрался..:)
Офлайн
DJDiX35 19 ноября 2019
У меня после наведения мышки сперва всплывало большое окно подсказки на 1 кадр, потом становилось нормально. Особенно неприятно на мобилке. Переделал слегка функцию LastUpdate(). Замените её, у кого так-же. Мне помогло

Офлайн
vitt 19 октября 2020
Попытался навесить скрипт на спрайт со своим коллайдер боксом. Выдало такое ---UnassignedReferenceException: The variable box of Tooltip has not been assigned. Какой такой вариабл бокс ему нужно выдать?
Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.
  • Яндекс.Метрика