/// Credit drHogan /// Sourced from - http://www.hammerandravens.com/multi-use-tooltip-system-in-unity3d/ namespace UnityEngine.UI.Extensions { [AddComponentMenu("UI/Extensions/HoverTooltip")] public class HoverTooltip : MonoBehaviour { //manually selectable padding for the background image public int horizontalPadding; public int verticalPadding; //tooltip text #if UNITY_2022_1_OR_NEWER public TMPro.TMP_Text thisText; #else public Text thisText; #endif //horizontal layout of the tooltip public HorizontalLayoutGroup hlG; //tooltip background image public RectTransform bgImage; Image bgImageSource; //needed as the layout refreshes only on the first Update() call bool firstUpdate; //if the tooltip is inside a UI element bool inside; //size of the tooltip, needed to track if out of screen // public float width; // public float height; //detect canvas mode so to apply different behaviors to different canvas modes, currently only RenderMode.ScreenSpaceCamera implemented //int canvasMode; RenderMode GUIMode; //the scene GUI camera Camera GUICamera; //the default tooltip object has the following pivots, so that the offset from the mouse is always proportional to the screen resolution (the y pivot) //Pivot(0.5,-0.5) //screen viewport corners for out of screen detection Vector3 lowerLeft; Vector3 upperRight; //scale factor of proportionality to the reference resolution (1280x720) float currentYScaleFactor; float currentXScaleFactor; //standard X and Y offsets of the new tooltip float defaultYOffset; float defaultXOffset; //real on screen sizes of the tooltip object float tooltipRealHeight; float tooltipRealWidth; // Use this for initialization void Start() { //in this line you need to change the string in order to get your Camera //TODO MAYBE DO IT FROM THE INSPECTOR GUICamera = GameObject.Find("GUICamera").GetComponent(); GUIMode = this.transform.parent.parent.GetComponent().renderMode; bgImageSource = bgImage.GetComponent(); //at start the pointer is never to be considered over and UI element inside = false; //assign the tooltip to the singleton GUI class manager for fast access //TacticalGUIManager.tgm.mmttp = this; //hide the tooltip HideTooltipVisibility(); this.transform.parent.gameObject.SetActive(false); } //single string input tooltip public void SetTooltip(string text) { NewTooltip(); //init tooltip string thisText.text = text; //call the position function OnScreenSpaceCamera(); } //multi string/line input tooltip (each string of the input array is a new line) public void SetTooltip(string[] texts) { NewTooltip(); //build up the tooltip line after line with the input string tooltipText = ""; int index = 0; foreach (string newLine in texts) { if (index == 0) { tooltipText += newLine; } else { tooltipText += ("\n" + newLine); } index++; } //init tooltip string thisText.text = tooltipText; //call the position function OnScreenSpaceCamera(); } //temporary call to not mess up old code, will be removed public void SetTooltip(string text, bool test) { NewTooltip(); //init tooltip string thisText.text = text; //call the position function OnScreenSpaceCamera(); } //position function, currently not working correctly due to the use of pivots and not manual offsets, soon to be fixed public void OnScreenSpaceCamera() { //get the dynamic position of the pos in viewport coordinates Vector3 newPos = GUICamera.ScreenToViewportPoint(UIExtensionsInputManager.MousePosition); // store in val the updated position (x or y) of the tooltip edge of interest float val; //store the new offset to impose in case of out of screen float yOffSet = 0f; float xOffSet = 0f; //check for right edge of screen //obtain the x coordinate of the right edge of the tooltip val = ((GUICamera.ViewportToScreenPoint(newPos).x) + (tooltipRealWidth * bgImage.pivot.x)); //evaluate if the right edge of the tooltip goes out of screen if (val > (upperRight.x)) { float distFromRight = upperRight.x - val; if (distFromRight > (defaultXOffset * 0.75)) { //shorten the temporary offset up to a certain distance from the tooltip xOffSet = distFromRight; } else { //if the distance becomes too short flip the tooltip to below the pointer (by offset+twice the height of the tooltip) xOffSet = ((defaultXOffset) - (tooltipRealWidth) * 2f); } //assign the new modified coordinates to the tooltip and convert to screen coordinates Vector3 newTooltipPos = new Vector3(GUICamera.ViewportToScreenPoint(newPos).x + xOffSet, 0f, 0f); newPos.x = GUICamera.ScreenToViewportPoint(newTooltipPos).x; } //check for left edge of screen //obtain the x coordinate of the left edge of the tooltip val = ((GUICamera.ViewportToScreenPoint(newPos).x) - (tooltipRealWidth * bgImage.pivot.x)); //evaluate if the left edge of the tooltip goes out of screen if (val < (lowerLeft.x)) { float distFromLeft = lowerLeft.x - val; if (distFromLeft < (defaultXOffset * 0.75 - tooltipRealWidth)) { //shorten the temporary offset up to a certain distance from the tooltip xOffSet = -distFromLeft; } else { //if the distance becomes too short flip the tooltip to above the pointer (by twice the height of the tooltip) xOffSet = ((tooltipRealWidth) * 2f); } //assign the new modified coordinates to the tooltip and convert to screen coordinates Vector3 newTooltipPos = new Vector3(GUICamera.ViewportToScreenPoint(newPos).x - xOffSet, 0f, 0f); newPos.x = GUICamera.ScreenToViewportPoint(newTooltipPos).x; } //check for upper edge of the screen //obtain the y coordinate of the upper edge of the tooltip val = ((GUICamera.ViewportToScreenPoint(newPos).y) - ((bgImage.sizeDelta.y * currentYScaleFactor * (bgImage.pivot.y)) - (tooltipRealHeight))); //evaluate if the upper edge of the tooltip goes out of screen if (val > (upperRight.y)) { float distFromUpper = upperRight.y - val; yOffSet = (bgImage.sizeDelta.y * currentYScaleFactor * (bgImage.pivot.y)); if (distFromUpper > (defaultYOffset * 0.75)) { //shorten the temporary offset up to a certain distance from the tooltip yOffSet = distFromUpper; } else { //if the distance becomes too short flip the tooltip to below the pointer (by offset+twice the height of the tooltip) yOffSet = ((defaultYOffset) - (tooltipRealHeight) * 2f); } //assign the new modified coordinates to the tooltip and convert to screen coordinates Vector3 newTooltipPos = new Vector3(newPos.x, GUICamera.ViewportToScreenPoint(newPos).y + yOffSet, 0f); newPos.y = GUICamera.ScreenToViewportPoint(newTooltipPos).y; } //check for lower edge of the screen //obtain the y coordinate of the lower edge of the tooltip val = ((GUICamera.ViewportToScreenPoint(newPos).y) - ((bgImage.sizeDelta.y * currentYScaleFactor * (bgImage.pivot.y)))); //evaluate if the upper edge of the tooltip goes out of screen if (val < (lowerLeft.y)) { float distFromLower = lowerLeft.y - val; yOffSet = (bgImage.sizeDelta.y * currentYScaleFactor * (bgImage.pivot.y)); if (distFromLower < (defaultYOffset * 0.75 - tooltipRealHeight)) { //shorten the temporary offset up to a certain distance from the tooltip yOffSet = distFromLower; } else { //if the distance becomes too short flip the tooltip to above the pointer (by twice the height of the tooltip) yOffSet = ((tooltipRealHeight) * 2f); } //assign the new modified coordinates to the tooltip and convert to screen coordinates Vector3 newTooltipPos = new Vector3(newPos.x, GUICamera.ViewportToScreenPoint(newPos).y + yOffSet, 0f); newPos.y = GUICamera.ScreenToViewportPoint(newTooltipPos).y; } this.transform.parent.transform.position = new Vector3(GUICamera.ViewportToWorldPoint(newPos).x, GUICamera.ViewportToWorldPoint(newPos).y, 0f); this.transform.parent.gameObject.SetActive(true); inside = true; } //call to hide tooltip when hovering out from the object public void HideTooltip() { if (GUIMode == RenderMode.ScreenSpaceCamera) { if (this != null) { this.transform.parent.gameObject.SetActive(false); inside = false; HideTooltipVisibility(); } } } // Update is called once per frame void Update() { LayoutInit(); if (inside) { if (GUIMode == RenderMode.ScreenSpaceCamera) { OnScreenSpaceCamera(); } } } //this function is used in order to setup the size of the tooltip by cheating on the HorizontalLayoutBehavior. The resize is done in the first update. void LayoutInit() { if (firstUpdate) { firstUpdate = false; bgImage.sizeDelta = new Vector2(hlG.preferredWidth + horizontalPadding, hlG.preferredHeight + verticalPadding); defaultYOffset = (bgImage.sizeDelta.y * currentYScaleFactor * (bgImage.pivot.y)); defaultXOffset = (bgImage.sizeDelta.x * currentXScaleFactor * (bgImage.pivot.x)); tooltipRealHeight = bgImage.sizeDelta.y * currentYScaleFactor; tooltipRealWidth = bgImage.sizeDelta.x * currentXScaleFactor; ActivateTooltipVisibility(); } } //init basic variables on a new tooltip set void NewTooltip() { firstUpdate = true; lowerLeft = GUICamera.ViewportToScreenPoint(new Vector3(0.0f, 0.0f, 0.0f)); upperRight = GUICamera.ViewportToScreenPoint(new Vector3(1.0f, 1.0f, 0.0f)); currentYScaleFactor = Screen.height / this.transform.root.GetComponent().referenceResolution.y; currentXScaleFactor = Screen.width / this.transform.root.GetComponent().referenceResolution.x; } //used to visualize the tooltip one update call after it has been built (to avoid flickers) public void ActivateTooltipVisibility() { Color textColor = thisText.color; thisText.color = new Color(textColor.r, textColor.g, textColor.b, 1f); bgImageSource.color = new Color(bgImageSource.color.r, bgImageSource.color.g, bgImageSource.color.b, 0.8f); } //used to hide the tooltip so that it can be made visible one update call after it has been built (to avoid flickers) public void HideTooltipVisibility() { Color textColor = thisText.color; thisText.color = new Color(textColor.r, textColor.g, textColor.b, 0f); bgImageSource.color = new Color(bgImageSource.color.r, bgImageSource.color.g, bgImageSource.color.b, 0f); } } }