[Released] ProTips - Tooltip System for UGUI

Hi @WizardGameDev ,

I’m glad you were able to find a workaround to the issue.

I tried to reproduce the issue in my project, but wasn’t able to. I setup a simple script that creates tooltip triggers from a prefab, and it also destroys all the tooltip triggers under the gameobject it created them under.

Here’s a video of me creating and destroying TooltipTriggers from the buttons, and below is the script for the Add/Remove buttons.

https://www.youtube.com/watch?v=EoEh41qQGGU

public void AddTooltipFromPrefab()
{
    // Load the button prefab from Resources.
    GameObject buttonPrefab = Resources.Load<GameObject>("DynamicObjWithTooltip");
    if (buttonPrefab != null)
    {
        // Assuming the prefab exists, instantiate the button and set its parent.
        Instantiate(buttonPrefab, tooltipTriggerButtons.transform, false);
    }
}

public void DeleteTooltipFromPrefab()
{
    TooltipTrigger[] triggers = tooltipTriggerButtons.GetComponentsInChildren<TooltipTrigger>();
    foreach (TooltipTrigger trigger in triggers)
    {
        Destroy(trigger.gameObject);
    }
}

Since I can’t seem to get the error to happen on my end, I’m thinking there must be something more to it. Is there perhaps a Canvas on your prefabs, that might cause the tooltip container object to attach itself under that canvas, and then it’s getting deleted along with the prefab, or something like that? I’d probably need to see what all is included in the prefab, or know where the error is happening (the specific error and line number). Or if you could post the code you’re using to instantiate and destroy the prefabs, that might help.

Otherwise, please feel free to zip up your project and upload it to a cloud hosting service (like DropBox, Google Drive, OneDrive, etc) and send a link to modelsharkstudio@gmail.com, and I’ll take a look at it and see if I can figure out what’s going on. Or if there’s some other way you can think of that I can reproduce the issue on my end (in our demo scene, for instance) that would help.

Many thanks for the quick reply. I’m not sure why mine was breaking as I was doing exactly the same code as the DeleteTooltipFromPrefab you provided earlier. There will be other places in my project were I will need to initialize and delete the triggers so I’ll try it again with that method. I’ll check out the demo scene as well.

Hello,

Is this asset still being supported?
Latest update was a year ago, and the pdf documentation link does not work → https://modelshark.com/Content/ProTips%20User%20Guide.pdf

Hi @zKici ,

Thanks for bringing the PDF issue to our attention. It turns out our whole website (www.modelshark.com) was down because of an SSL certificate issue. I re-installed the certificate and the PDF (and website) seem to be working fine now.

Yes, ProTips is still being supported. We get multiple requests per week for support, most of which are handled through direct emails to modelsharkstudio@gmail.com. We are also very active on this forum and our ModelShark YouTube channel, so don’t hesitate to reach out to us if you need assistance.

Regarding updates, we only update ProTips when there are breaking bugs or when we have accumulated significant changes to make a large update. ProTips is fairly mature at this point and not much has changed with the way Unity UI and the Canvas works in the last year.

1 Like

Hi there, just purchased your asset and liking it so far.
I appreciate you don’t appear to provide this functionality out of the box but was wondering if you had any tips for the most sensible way to go about extending your code to achieve the following:

I want mullti-level tooltips on top of tooltips - so e.g. player mouse hovers UI image, gets a tooltip containing some info, which locks in place (say on middlemouse button click while tooltip 1 is displaying), allowing player to freely mouseover TMPro text links in tooltip 1, which themselves display e.g. tooltip2 and tooltip 3, (potentially containing even more TMP link text and leading to tooltips 4, 5 etc. (although for most of my purposes I will need at most one level of children) While at any stage, another mouse middle button input would unlock and resume fade behaviour for the original parent tooltip, and all the children.

I want to maintain compatibility with the asset as far as possible so I’ve tried altering your demo TMPro links class implementing OnPointerClick like this, while passing in links with rich texts to the text of the scriptable object Tooltipdata so that the original data for tooltip 1 which is passed looks like this:
This is a <color=green><link=“primary”>primary link.
This is another <color=orange><link=“primarytwo”>anotherprimary one.

then in the database the text for the primarytwo link is:
which itself contains a <link=“secondary”> secondary link

        public void OnPointerClick(PointerEventData eventData)
        {

            if (eventData.button == PointerEventData.InputButton.Middle)
            {
                bool isHoveringOver = TMP_TextUtilities.IsIntersectingRectTransform(pTextMeshPro.rectTransform, Input.mousePosition, pCamera);
                bool isOverLink = TMP_TextUtilities.FindIntersectingLink(pTextMeshPro, Input.mousePosition, pCamera) != -1;
                if (isHoveringOver && !isOverLink && tooltipTrigger.Tooltip != null)
                {

                    tooltipTrigger.Tooltip.IsBlocking = false;
                    tooltipTrigger.Tooltip.StaysOpen = true;
                    GameObject go = new GameObject("childtooltipGO");
                    go.transform.parent = tooltipTrigger.gameObject.transform;
                    TooltipTrigger tooltiptrigger2 = go.AddComponent<TooltipTrigger>();
                    tooltiptrigger2.tooltipStyle = defaultTooltipStyle;

                    // Set some extra style properties on the tooltip
                    tooltiptrigger2.minTextWidth = 280;
                    tooltiptrigger2.maxTextWidth = 400;
                    tooltiptrigger2.backgroundTint = Color.white;
                    tooltiptrigger2.tipPosition = TipPosition.MouseTopRightCorner;
                    go.SetActive(true);

                    // This tooltip will be activated through code, so turn off the default OnMouseHover behavior.
                    tooltiptrigger2.isRemotelyActivated = true;

                    LinkData linkData = GetLinkData();
                    List<ToolTipData> ToolTipList = Vault.GetAll<ToolTipData>();
                    for (int i = 0; i < ToolTipList.Count; i++)
                    {
                        if (linkData.Id == ToolTipList[i].Title)
                        {
                            ToolTipData toolTip = ToolTipList[i];
                            tooltiptrigger2.SetText("BodyText", toolTip.Description);
                            tooltiptrigger2.StartHover();
                        }
                    }
                }




            }
        }
)

At the back end I have a list of Tooltip textdata in a scriptable object database and I dont have an issue connecting the data to the tooltips, but its in between I am wondering what is the best way to proceed - particularly as it seems the asset currently assumes there will only be one tooltip active at any one time - any ideas? The code above does not produce a second tooltip on top of the first. It will lock the first tooltip but not produce children. Any help would be appreciated

Thanks

Hi @johnclearybl ,

I think I can help get you over this hump, the issue of multiple tooltips not showing at the same time.

First off, there is a section of code in TooltipManager.cs that prevents multiple tooltips from being shown at the same time. You’ll want to comment out that code so you can display more than one at a time.

That alone won’t fix the problem, because ProTips is designed to only instantiate one of each Tooltip Style and re-use that one instance over and over. So to get around that, you can just make copies of your Tooltip Style. So for example, if you want 4 of the same tooltip style to be visible at once, just copy that tooltip style 4 times and give it different names:

And lastly, you just need to check “Stays Open” on your tooltip triggers, which you’re already doing in your code. So you should be able to have multiple tooltips open at the same time after that. I wrote a quick script to create 4 dynamic tooltip triggers with an Add button. Each one points to a different copy of the TooltipStyle (above). Here’s the results:

Hopefully this gets you past that hurdle. If you can’t get it working, let me know and I can send you the project I worked on for the illustration above.

Thats great, thanks so much for the quick response. At work now but will dig into this tonight, if I can’t get the hang of it I will come back to you. 5 star review coming your way on the asset store too!

Hey, just a couple of more questions. And sorry I dint have the project on my laptop and wont get in front of it until this evening so may be misremembering how your classes work.

Did you need to do anything special, e.g. with the sort order or layer of the dynamic tooltips in your example to ensure the latest tooltip displays on top of the previous one? Like, if you were to click the dynamic tooltip buttons right to left instead of left to right would the last one instantiated display on top or underneath the others?

At runtime, plan is -as follows to get the child links/new tooltips to come themselves from TMPro links - if any of this doesnt make sense to you, or if you have any suggestions would really appreciate it.

Pseudo:

,OnPointerClick:
if trigger1.tooltip isactive and showing
OnMiddlebuttonclick call MakeTrigger2

MakeTrigger2
set trigger1 or parent to staysopen.
instantiate a new gameobject and add a tooltiptrigger to it
make the new gameobject for trigger2 a child of the gameobject holding trigger1,
.(maybe adding a horizontal layout group with child force expand to ensure that the newGO holding trigger2 is at least as big as trigger1)OR double the offset, override the display location?

OnHoverTrigger2
if
trigger1.tooltip is active, trigger2.tooltip isnotactive, and TMP TextUtils intersecting link is true
{
Call TMP TextUtils Link intersectig and match with key in SO database
Set text trigger2.tooltip to text from database
begin Hover() trigger2
}
otherwise return

OnPointerExit
{
set trigger1 to deactivate
Maybe find all active trigger tooltips in the scene and call deactivate on them?
End Hover trigger2
}

Hey @johnclearybl ,

You can control the sort order of the multiple open tooltips by adding the following line of code to the Tooltip Manager, in the Show() method. This will force the last tooltip triggered to be the one on top.

TooltipManager.cs => Show() method

Hope that helps!

Hi Zangad,

Does this asset only work with Buttons? I have some UI elements that are just text with the option next to them in a settings menu (toggle, dropdown, slider etc) but I would like to have the pop-up whenever the user hovers over the text UI element. Is this possible?

Hi @EpicMcDude ,

ProTips works with more than buttons. As long as your canvas has a GraphicsRaycaster on it (it does by default), then anything under the canvas that blocks raycasts can be used as a tooltip trigger.

For example, text and TextMeshPro elements block raycats, as well as images, including things like canvas panels and control elements such as buttons/toggles/etc. So, almost anything on the canvas can be used as a tooltip trigger.

1 Like

Hi, I want to ask regarding the tooltip.

How can I design my tooltip like in this video? so when I point to the different object it will popup different information in the tooltip

Hi @nurhidayahhamri

The data that populates the tooltip in that video comes from the EquipmentItem.cs script that is on the tooltip trigger object.

If you look at the 2. ProTips Demo scene and click on the armor in Scene View, you will see the Equipment Item script (see image below).

The data for your tooltips may come from someplace different, like a database or a JSON file, but regardless of where the data comes from, there are really just two things you need to do to set the text on a tooltip dynamically:
(1) Get a reference to the tooltip trigger you are interested in setting text on, and
(2) Set the text for that tooltip trigger based on your data.

If you look at the EquipmentItem.cs script, most of the work is done in these few lines of code:

(1) Get a reference to the tooltip trigger on the current game object
TooltipTrigger = gameObject.GetComponent<TooltipTrigger>();

(2) Set the text on the tooltip trigger based on the data in the EquipmentItem script
TooltipTrigger.SetText(“TitleText”, String.Format(“{0} ({1})”, itemName, itemType));
TooltipTrigger.SetText(“Stats”, GetStatsText(this));
TooltipTrigger.SetImage(“ItemImage”, Image.sprite);
… and so on

So really how you accomplish this depends on where the data for your tooltip comes from. If you can give me some more details about your project and where your tooltip data is coming from, I can try to provide more specific assistance. I hope this helps!

1 Like

Hi @zangad , Thank you for the explanation.

I want to display the image using a tooltip. So when I point to the 3d object, it will display the image that I already created. Right now I already set the sprite in the tooltip but I have an error in the function HideToolTip. In this function, I want to hide the image when the arrow/cursor is away from the object.

------TooltipManager Script-----
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;

public class TooltipManager : MonoBehaviour
{
public static TooltipManager _instance;

public Image imageComponent;

//public SpriteRenderer imageComponent;
// SpriteRenderer spriteRenderer = gameObject.GetComponent();

private void Awake()
{
if (_instance != null && _instance != this)
{
Destroy(this.gameObject);
}
else
{
_instance = this;
}
}

// Start is called before the first frame update
void Start()
{
Cursor.visible = true;
gameObject.SetActive(false);
}

// Update is called once per frame
void Update()
{
transform.position = Input.mousePosition;
}

public void SetAndShowToolTip(Sprite Image)
{
gameObject.SetActive(true);
imageComponent.sprite = Image;
}

public void HideToolTip()
{
gameObject.SetActive(false);
imageComponent.sprite = Sprite.Destroy();
}

}
Right now, I have an error in HideToolTip function.
------Tooltip Script-----
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Tooltip : MonoBehaviour
{
public Sprite Image;

private void OnMouseEnter()
{
TooltipManager._instance.SetAndShowToolTip(Image);
}

private void OnMouseExit()
{
TooltipManager._instance.HideToolTip();
}
}

You mentioned you have an error in the HideToolTip function. What is the error you’re getting?

How to deal a Canvas on the prefabs? A case is the canvas on the Dropdown component. It’s very common case.

Hi there,

I’m not sure I understand the question, but hopefully I can help. There is an example of how to use ProTips with a dropdown component in the “2. ProTips Demo” scene:

9168527--1276163--upload_2023-7-24_12-39-45.png

Please let me know if this isn’t what you’re asking about.

Hi @zangad
we already talked about idea I have in DestroyIt thread but here seems the better place to continue it.
So my suggestion is that it would be great to have ProTips functionality for 3d objects (as in demo 3. 3D world object demo) not only based on cursor of mouse as is now but also i.e. if cursor is disabled and/or if additional “switch variable” is enabled in TooltipManager.
Detection would be active for center of screen (maybe with additional Vector2 shift correction if in our game “center of screen” is not the same place as “crosshair” or “crosshair” is “floating” when i.e. steering the ship, seems like “Vector2 shift” would have to be also added in TooltipManager as public).
I do not know how your asset works inside, but to me it looks like in place where you detect mouse position, you needed just add in some cases (when mouse cursor is disabled and/or when check in TooltipManager is enabled) switching to “center of screen + Vector2 shift” and then work as now, so check if any 3d object with ProTip is targeted (probably raycasted).

What do you think about this idea?
I think that this alternative way of detection when Tip should be triggered would add a lot of flexibillity to your asset. ProTips could be used as kind of functionality for any statuses in fps games as i.e. in no mans sky

Hi @dulinieck ,

Thanks for posting your question here. Like I mentioned over on the DestroyIt thread, there is a demo scene in ProTips “10. Tooltip Position Override”. In that demo, it demonstrates how to have the tooltip follow a transform or a vector. You would just assign your tooltip to follow the crosshair transform - you don’t need to assign it to a mouse pointer.

You just check “Override Position” on the TooltipTrigger component, and select what you want the tooltip to attach itself to.

9233511--1289988--upload_2023-8-18_16-38-17.png

So in the example above, the tooltip will appear anchored at vector X:1011, Y:508, Z:0. And this value can be assigned at runtime, so you can do some simple screen vector math (divide X and Y by 2) to find out what the center of the screen is, then plug that value into the TooltipTrigger component, and it will anchor the tooltip to that vector. Hope that helps! :slight_smile:

I understand how to use override to make i.e. “common/constat position” for tooltips.
But I think you did not understand my question or I do not understand how your asset works.
The question is how your asset works to detect that tooltip should be at all displayed?
I understand it is handled by “TooltipManager”, and it checks cursor position, but what if there is no cursor?
The game runs in FPP mode and there is no cursor - there is only crosshair in the middle of screen, and 3d objects with assigned TooltipTrigger.