Jump to content
  • Advertisement
  • entries
    12
  • comments
    20
  • views
    21361

Tooltip system on uGUI Unity 4.6

EiR_TD

8251 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.

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 scripts

using 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 tooltip
foreach(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 are

private 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);
}

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.


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

Cheers,
H

 

 




10 Comments


Recommended Comments

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

Share this comment


Link to comment

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!

Share this comment


Link to comment

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

Share this comment


Link to comment

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?)

Share this comment


Link to comment

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)

Share this comment


Link to comment

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.

Share this comment


Link to comment

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.

 

Look forward to your updates.

Share this comment


Link to comment

Hello.  Is the codes supposed to all be on one line? 

M5PrGoJ.jpg


Images are all blank too

Share this comment


Link to comment
26 minutes ago, FragmentalStew said:

Is the codes supposed to all be on one line? 

I fixed the code. The images are a bit more difficult.

Share this comment


Link to comment
6 minutes ago, khawk said:

I fixed the code. The images are a bit more difficult.

Oh, wow, that's a ton more readable. Thanks!

Share this comment


Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!