Part of these game systems:
intermediate UI

Tooltip System

Dynamic mouse-following tooltip that auto-positions to stay on screen. Supports rich text and custom layouts.

Unity 2022.3+ · 2.5 KB · TooltipSystem.cs

How to Use

1

Create a tooltip UI panel with TextMeshPro fields (header + body)

2

Add CanvasGroup and ContentSizeFitter for auto-sizing

3

Attach TooltipSystem to the tooltip panel

4

Show: TooltipSystem.Show("Sword", "+10 Damage")

5

Hide: TooltipSystem.Hide()

6

Tooltip auto-flips to stay on screen

7

Use on hover events: OnPointerEnter/Exit to show/hide

Source Code

TooltipSystem.cs
C#
using UnityEngine;
using TMPro;

/// <summary>
/// Mouse-following tooltip that auto-positions to stay on screen.
/// Call TooltipSystem.Show/Hide from any UI element.
/// </summary>
public class TooltipSystem : MonoBehaviour
{
    public static TooltipSystem Instance { get; private set; }

    [Header("References")]
    [SerializeField] private RectTransform tooltipRect;
    [SerializeField] private TextMeshProUGUI headerText;
    [SerializeField] private TextMeshProUGUI bodyText;
    [SerializeField] private CanvasGroup canvasGroup;

    [Header("Settings")]
    [SerializeField] private Vector2 offset = new Vector2(16f, -16f);
    [SerializeField] private float showDelay = 0.3f;
    [SerializeField] private float fadeSpeed = 8f;

    [Header("Padding")]
    [SerializeField] private float screenPadding = 10f;

    private Canvas parentCanvas;
    private float showTimer;
    private bool isShowing;
    private float targetAlpha;

    private void Awake()
    {
        if (Instance == null)
            Instance = this;
        else
        {
            Destroy(gameObject);
            return;
        }

        parentCanvas = GetComponentInParent<Canvas>();
        if (canvasGroup == null)
            canvasGroup = tooltipRect.GetComponent<CanvasGroup>();
        if (canvasGroup == null)
            canvasGroup = tooltipRect.gameObject.AddComponent<CanvasGroup>();

        canvasGroup.alpha = 0f;
        canvasGroup.blocksRaycasts = false;
    }

    private void Update()
    {
        if (!isShowing) return;

        showTimer -= Time.unscaledDeltaTime;

        if (showTimer <= 0f)
            targetAlpha = 1f;

        canvasGroup.alpha = Mathf.Lerp(canvasGroup.alpha, targetAlpha, Time.unscaledDeltaTime * fadeSpeed);

        UpdatePosition();
    }

    private void UpdatePosition()
    {
        Vector2 mousePos = Input.mousePosition;
        Vector2 tooltipPos = mousePos + offset;

        // Get tooltip size
        float width = tooltipRect.rect.width;
        float height = tooltipRect.rect.height;

        // Clamp to screen edges
        if (tooltipPos.x + width > Screen.width - screenPadding)
            tooltipPos.x = mousePos.x - width - offset.x;

        if (tooltipPos.y - height < screenPadding)
            tooltipPos.y = mousePos.y + height + Mathf.Abs(offset.y);

        tooltipPos.x = Mathf.Max(screenPadding, tooltipPos.x);
        tooltipPos.y = Mathf.Min(Screen.height - screenPadding, tooltipPos.y);

        tooltipRect.position = tooltipPos;
    }

    /// <summary>
    /// Show tooltip with header and body text.
    /// </summary>
    public static void Show(string header, string body = "")
    {
        if (Instance == null) return;
        Instance.ShowInternal(header, body);
    }

    /// <summary>
    /// Show tooltip with body text only.
    /// </summary>
    public static void ShowText(string text)
    {
        if (Instance == null) return;
        Instance.ShowInternal("", text);
    }

    /// <summary>
    /// Hide the tooltip.
    /// </summary>
    public static void Hide()
    {
        if (Instance == null) return;
        Instance.HideInternal();
    }

    private void ShowInternal(string header, string body)
    {
        isShowing = true;
        showTimer = showDelay;
        targetAlpha = 0f;

        if (headerText != null)
        {
            headerText.text = header;
            headerText.gameObject.SetActive(!string.IsNullOrEmpty(header));
        }

        if (bodyText != null)
            bodyText.text = body;

        // Force layout rebuild for correct sizing
        UnityEngine.UI.LayoutRebuilder.ForceRebuildLayoutImmediate(tooltipRect);
        UpdatePosition();
    }

    private void HideInternal()
    {
        isShowing = false;
        targetAlpha = 0f;
        canvasGroup.alpha = 0f;
    }
}