• entries
12
17
• views
21114

# Tooltip system on uGUI Unity 4.6

7961 views

Here we go, it took some pain as we only begun using uGUI less than a couple of weeks ago, but we managed to develop a functional tooltips system. It might still be improved and polished and the code is partially dirty, but it works well, it shows no performance issues of any kind.

No more talking and straight to how we did it, as we saw several people asking about something similar in several other places.

Video Example Tooltip System

We have a tooltip object in the canvas, consisting of a panel with the background image and a child text element. to the panel we attached our tooltip script.

first of all add on top of your scriptsusing UnityEngine.UI;using UnityEngine.Events;using UnityEngine.EventSystems;
When we are building the interfaces, we attach an EventTrigger to the object from the inspector, graphically, then from the code initializing the button we add triggers with a callback to the objects we need to get the data from in the following way :public ToolTip ttp; //initialize this by getting the script attached to the tooltipforeach(RectTransform elem in childs){ if(elem.name=="Button"){ portraits.Add(elem.GetComponent()); EventTrigger trig = elem.gameObject.GetComponent(); AddPointerEnterTrigger(trig,OnPointerEnter,EventTriggerType.PointerEnter); AddEventTrigger(trig,OnPointerExit,EventTriggerType.PointerExit); }}
and the functions areprivate void AddPointerEnterTrigger(EventTrigger evTrig, UnityAction action, EventTriggerType triggerType){ EventTrigger.TriggerEvent trigger = new EventTrigger.TriggerEvent(); AddEventTrigger(evTrig,d => OnPointerEnter(d, evTrig.gameObject),EventTriggerType.PointerEnter); EventTrigger.Entry entry = new EventTrigger.Entry() { callback = trigger, eventID = triggerType }; evTrig.delegates.Add(entry);}private void AddEventTrigger(EventTrigger evTrig, UnityAction action, EventTriggerType triggerType){ EventTrigger.TriggerEvent trigger = new EventTrigger.TriggerEvent(); trigger.AddListener((eventData) => action()); EventTrigger.Entry entry = new EventTrigger.Entry() { callback = trigger, eventID = triggerType }; evTrig.delegates.Add(entry);}[code=:0]private void AddEventTrigger(EventTrigger evTrig, UnityAction action, EventTriggerType triggerType){ EventTrigger.TriggerEvent trigger = new EventTrigger.TriggerEvent(); trigger.AddListener((eventData) => action(eventData)); EventTrigger.Entry entry = new EventTrigger.Entry() { callback = trigger, eventID = triggerType }; evTrig.delegates.Add(entry);}private void OnPointerEnter(BaseEventData dataObject, GameObject hovered){ if(hovered != null){ ttp.SetTooltip(hovered.name); } } private void OnPointerExit(){ ttp.HideTooltip(); }
and then this is the script attached to the tooltip object. (this is the version for the gui canvas on screen overlay mode), it contains already a rough function so that the tooltip never goes offscreen if the mouse is close to the edge, and resizes itself (on a single line only at the moment) according to the length of the text//text of the tooltip Text text; //if the tooltip is inside a UI element bool inside; bool xShifted = false; bool yShifted = false; int textLength; float width; float height; int screenWidth; int screenHeight; float canvasWidth; float canvasHeight; float yShift; float xShift; int canvasMode; public void SetTooltip(string ttext){ //ScreenSpaceOverlay Tooltip if(GUIMode==RenderMode.ScreenSpaceOverlay){ //set the text and fit the tooltip panel to the text size text.text=ttext; this.transform.GetComponent().sizeDelta = new Vector2(text.preferredWidth+60f,text.preferredHeight+20f); width = this.transform.GetComponent().sizeDelta[0]; height = this.transform.GetComponent().sizeDelta[1]; Vector3 newPos = Input.mousePosition-new Vector3(xShift,yShift,0f); //check and solve problems for the tooltip that goes out of the screen on the horizontal axis float val; val=(newPos.x-(width/2)); if(val<=0){ newPos.x+=(-val); } val=(newPos.x+(width/2)); if(val>screenWidth){ newPos.x-=(val-screenWidth); } //check and solve problems for the tooltip that goes out of the screen on the vertical axis val=(screenHeight-newPos.y-(height/2)); if( val<=0 && !yShifted){ yShift=(-yShift+25f); newPos.y+=yShift*2; yShifted=true; } this.transform.position=newPos; this.gameObject.SetActive(true); inside=true; //WorldSpace Tooltip }} public void HideTooltip(){ //ScreenSpaceOverlay Tooltip if(GUIMode==RenderMode.ScreenSpaceOverlay){ xShift = 40f;yShift = -30f; xShifted=yShifted=false; this.transform.position=Input.mousePosition-new Vector3(xShift,yShift,0f); this.gameObject.SetActive(false); inside=false; }} void FixedUpdate () { if(inside){ //ScreenSpaceOverlay Tooltip if(GUIMode==RenderMode.ScreenSpaceOverlay){ Vector3 newPos = Input.mousePosition-new Vector3(xShift,yShift,0f); //check and solve problems for the tooltip that goes out of the screen on the horizontal axis float val; val=(newPos.x-(width/2)); if( val<=0){ newPos.x+=(-val); } val=(newPos.x+(width/2)); if(val>screenWidth){ newPos.x-=(val-screenWidth); } //check and solve problems for the tooltip that goes out of the screen on the vertical axis val=(screenHeight-newPos.y-(height/2)); if(val<=0){ if(!yShifted){ yShift=(-yShift+25f); newPos.y+=yShift*2; yShifted=true; } } this.transform.position=newPos; }}
here is a little video showing how it works. we make the tooltip appearing a bit above the mouse pointer so that it doesn't cause onpointerover problems, and we use the fixedupdate for the refresh as it is somehow more reliable about flickering and speed.

Video Example Tooltip System

if you have any problem in implementing it, feel free to ask more details!

Cheers,
H

A great overview, how I'd advise caution about applying the full EventTrigger component on a GameObject, especially if you are only using a couple of the events.

Better to create your own script which implements the interfaces (PointerEnter and PointerExit in this case) and then add you above code to it.

The "Advanced UI" demo walk-through from Unite 2014 gives a nice overview of this.

Also, how come you are using FixedUpdate in your script? There are no physics involved in the UI system or your demo.  Might be better to just use update (or use the PointerMove / PointerDrag interfaces :D)

Keep up the great work

Hi Simon, thanks for the feedback. We already though only reimplementing the interfaces only now, but why technically is using the full event trigger an overkill in this case? At first it was the easiest choice while we were learning how uGUI worked, then we thought it was maybe a overkill, but then we didn't find anything about its problems (also we will have the onlick functions additionally on the same buttons now)

I will take a look at that tutorial, i didn't find a satisfactory one about it yet.

About the fixed update we noticed that the tooltip movement got much more smoother with the FixedUpdate instead than the normal Update. It shouldn't be about other computations though, because we had the same thing in a scene that was almost empty aside from the GUI.

Thanks again man, cheers!

Interesting feedback about the fixed update, will bear that in mind.

I asked about the EventTrigger component during the Beta and that was the feedback I got.  Basically if you use the EventTrigger component, it will be checked for ALL events every update.  If you only use a couple that shouldn't be too bad but if you have lots, then.......

It's more of a "best practice" thing to build your own scripts and implement only the interfaces you use really.  nothing wrong with using EventTrigger, you just need to be aware of how it will perform.

Plus your script already implements most of the code necessary, so you might as well drop the EventTrigger component requirement :D

*sniggers* you said uGUI :D

ok, for example i didn't take into account such a behavior of the eventrigger (i begun using it no less than ten days ago 'cause i didn't want to risk messing up too much when it was still in the beta ;)),

then it would definitely make sense to implement just a small part of it that i need!

will definitely switch to that then, we spent months in writing optimized code for the main engine, i wouldn't like to then fall on the gui part :D

(ain't it uGUI the new unity gui?)

Lol, perf will always kill us :D

(uGUI was the initial/internal codename, now it's just called Unity UI, or UI)

(although if you check the new UI bug reporter, it's still called uGUI :P)

Heheh yes, will kill us but will put our product above the average quality level hopefully :D

Comments like yours are the reason why we like to post our development, cause sometimes a single sentence from an external point of view reveals something you didn't even think of, thanks man!

Ahah ok, i stuck to uGUI all the time, i had no idea it changed the name indeed.

Glad to be of service.  I'm in the final publication phase of a book on the new UI, so I always keep an eye out there for any pertinent pains or links to very useful articles.