Search Unity

Get UI placed right over gameobjects "head"

Discussion in 'Scripting' started by Jejkobb, Aug 22, 2017.

  1. Jejkobb

    Jejkobb

    Joined:
    Aug 10, 2017
    Posts:
    19
    So I have a character, and when he speaks I want my dialogue UI to appear right above his head. I know how to convert his position to canvas coordinates but not his scale. Because I need his scale to add to his Y position so it will be perfectly above his head right? Is there a better way to do it??? Please help!

    If you don't understand what I'm asking then please ask for clarification.

    Thanks!
     
  2. DroidifyDevs

    DroidifyDevs

    Joined:
    Jun 24, 2015
    Posts:
    1,724
  3. Jejkobb

    Jejkobb

    Joined:
    Aug 10, 2017
    Posts:
    19
    In the script that the guy answered with he has a static height variable. I need that to be changed according to the height of my character and the size of the current resolution, then translated to UI coordinates. Look, this is my problem:

    In unity editor:


    In Build & Run:


    The gameobject being the clam in the suit. This is when I use a static variable like 3.5f for example, it's only going to look right when the screen is the right size. How do I fix it so it's always above the head no matter what? I want the height of the gameobject + the y position and then converted to canvas coordinates. I tried this:



    but for some reason the text goes higher when the localscale gets lower, it should be reversed right??? I'm so confused.
     
  4. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    Would you be open to using a WorldSpace Canvas as a child of the character, and positioning it in the world above the characters head?

    Otherwise you can get the height of the character using Bounds, and then convert to screenspace using Camera.main.WorldToScreenPoint(worldSpacePosition). If your character is made up of multiple renderers, you can get an aggregate bounds by using Bounds.Encapsulate() for each renderer making up the character.

    Also please use code tags and paste the code into your post, do not upload screenshots of code.
     
    Last edited: Aug 22, 2017
    Kiwasi and DroidifyDevs like this.
  5. DroidifyDevs

    DroidifyDevs

    Joined:
    Jun 24, 2015
    Posts:
    1,724
    You should use an offset number instead of adding its local scale. Or use @jeffreyschoch 's suggestion.
    I thought of that, but the text would get covered if an object was in front of it. In some situations, that would work though.
     
  6. eses

    eses

    Joined:
    Feb 26, 2013
    Posts:
    2,637
    @jeffreyschoch and @Jejkobb

    Was going to suggest this, but was too late:

    To make floating HUD item / text whatever work, you could make it's offset relative to canvas and proportional to canvas size. And like @jeffreyschoch said, you can use bounds of an object, for example if your character has a box collider or such...

    Code (csharp):
    1.  
    2. float offsetProp = 0.01f;
    3.  
    4.  
    5. // Proportional distance based on canvas size
    6. var propDist = canvasRect.rect.y * -offsetProp;
    7.  
    8. // Placement using transform pos, bounds and offset percentage based on canvas size
    9. var offset = target.transform.position.y  + (col.bounds.extents.y) + propDist;
    10.        
    11. Vector3 offsetPos = new Vector3(target.transform.position.x, offset, target.transform.position.z);
    12.  

    EDIT:

    This way canvas (actually screen) size shouldn't matter (I think 4k and 800x600 worked the same), and you can scale the character's size or one axis.
     
    Last edited: Aug 22, 2017
  7. LiterallyJeff

    LiterallyJeff

    Joined:
    Jan 21, 2015
    Posts:
    2,807
    You can still layer the Canvas like a sprite, and put it on a layer in front of everything else if you want.
     
    DroidifyDevs likes this.
  8. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    So what's wrong with just converting the target position from world space to screen space? This is a relatively trivial problem to solve. Here is how I approach it.
    • Create an empty GameObject as a child of the character in the position you want the text to appear
    • Convert the empty GameObject's position to screen space with Camera.WorldToScreenPoint.html
    • Set up the UI.Text with the pivot position at the bottom, anchors at bottom left corner of the screen*
    • Move the UI.Text's position to the screen space position of the empty GameObject
    *I think its bottom left. There is a sweet spot for the anchors that means Transform.position lines up with screen space. If you can't find it, just calculate an appropriate offset.
     
  9. Jejkobb

    Jejkobb

    Joined:
    Aug 10, 2017
    Posts:
    19
    Where do you get col from? and is the target the UI or the gameobject?

    This is what it looks like for me rn:

    Code (CSharp):
    1.  
    2. void positionText(GameObject target)
    3.     {
    4.         //CHANGE X POSITION
    5.  
    6.         float offsetProp = 0.01f;
    7.  
    8.         // Proportional distance based on canvas size
    9.         var propDist = target.GetComponent<RectTransform>().rect.y * -offsetProp;
    10.  
    11.         // Placement using transform pos, bounds and offset percentage based on canvas size
    12.         var offset = target.transform.position.y + (col.bounds.extents.y) + propDist;
    13.  
    14.         Vector3 offsetPos = new Vector3(target.transform.position.x, offset, target.transform.position.z);
    15.  
    16.     }
    17.  
    But I don't know how to get the "col" thing?
     
  10. eses

    eses

    Joined:
    Feb 26, 2013
    Posts:
    2,637
    @Jejkobb - it's just the collider you get from your character (which I mentioned in previous post).
     
  11. eses

    eses

    Joined:
    Feb 26, 2013
    Posts:
    2,637
    @Kiwasi

    "So what's wrong with just converting the target position from world space to screen space?"

    It was used, see the link to other thread. Only difference was that you used the empty transform, which in the end might be a better choice in this case..

    EDIT: now that I think / start to remember the reason for suggesting the idea... was that with this way it's possible to have floating HUD bar / text over target, no matter what. Think about a RTS game unit that gets turned upside down when hit, or a character that crouches and continues speaking, as target's root transform position and bounds are used to place something above it, it wouldn't matter if target is rotating or changing it's size.
     
    Last edited: Aug 23, 2017
  12. Jejkobb

    Jejkobb

    Joined:
    Aug 10, 2017
    Posts:
    19
    Nevermind, finally got it working, I put a "TextPoint" gameobject as a child so I could easily position the text like you guys said. Then made an empty script on it called TextPoint and this is what my final script looks like:

    Code (CSharp):
    1. if (target.transform.position.x < -3.5f)
    2.             {
    3.             dText.transform.position = new Vector3(
    4.                 Camera.main.WorldToScreenPoint( new Vector3(-4f, (target.transform.position.y), target.transform.position.z)).x,
    5.                 Camera.main.WorldToScreenPoint(target.GetComponentInChildren<TextPoint>().transform.position).y,
    6.                 dText.transform.position.z
    7.                 );
    8.             }
    9.             else if (target.transform.position.x >= -3.5f && target.transform.position.x < 3.5f)
    10.             {
    11.             dText.transform.position = new Vector3(
    12.                 Camera.main.WorldToScreenPoint( target.transform.position).x,
    13.                 Camera.main.WorldToScreenPoint(target.GetComponentInChildren<TextPoint>().transform.position).y,
    14.                 dText.transform.position.z
    15.                 );
    16.             }
    17.             else if (target.transform.position.x >= 3.5f)
    18.             {
    19.             dText.transform.position = new Vector3(
    20.                 Camera.main.WorldToScreenPoint(new Vector3(4f, (target.transform.position.y), target.transform.position.z)).x,
    21.                 Camera.main.WorldToScreenPoint(target.GetComponentInChildren<TextPoint>().transform.position).y,
    22.                 dText.transform.position.z
    23.                 );
    24.        }
    dText is the text UI, target is the gameobject, and I just got the components position and just converted everything. The if statements are for keeping the text within the view of the camera.

    Way simpler than I tried to make it at first. This should work no matter the resolution.