Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

How to get UI text to follow a character?

Discussion in 'UGUI & TextMesh Pro' started by ViggyNash, Jan 4, 2016.

  1. ViggyNash

    ViggyNash

    Joined:
    Apr 21, 2015
    Posts:
    9
    Let me say first that I'm very much a beginning with Unity.

    I'm trying to do something I though was rather simple: have UI text follow a character, like a character name hovering over a character as they move around. However, I've been finding this very difficult. I tried writing a simple script that just sets the position of the UI element to be an offset of the target object's position, but the UI element doesn't move. I then tried modifying the RectTransform of the UI element by setting that to be the offset of the target object's position relative to the viewport, using camera.WorldToViewportPoint, but nothing happens then either.

    It's possible that I'm just approaching this problem the wrong way because I don't have a solid grasp on how UI works, but otherwise I'm not sure what I'm doing wrong. I have written a working script that gets a camera to smoothly follow a car-ish thing that has simple controls, so I'm fairly sure the problem isn't the way I'm manipulating things, only what I'm manipulating.

    Here's the code that tries to modify RectTransform:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class NameFollow : MonoBehaviour {
    5.  
    6.     public GameObject robot;
    7.     public Camera camera;
    8.     private Transform thisTransform;
    9.     private Vector3 roboPos;
    10.  
    11.     // Use this for initialization
    12.     void Start () {
    13.         roboPos = robot.transform.position;
    14.         thisTransform = this.transform;
    15.     }
    16.  
    17.     // Update is called once per frame
    18.     void Update () {
    19.         Vector3 roboScreenPos = camera.WorldToViewportPoint(roboPos);
    20.         this.GetComponent<RectTransform>().anchoredPosition.Set(roboScreenPos.x, roboScreenPos.y + 1f);
    21.         //thisTransform.position.Set(roboScreenPos.x, roboScreenPos.y + 1f, roboScreenPos.z);
    22.     }
    23. }
     
  2. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    7,795
  3. ViggyNash

    ViggyNash

    Joined:
    Apr 21, 2015
    Posts:
    9
    I changed the update function to this:

    Code (CSharp):
    1. void Update () {
    2.         Vector3 roboScreenPos = camera.WorldToScreenPoint(roboPos);
    3.         Vector2 local;
    4.         RectTransformUtility.ScreenPointToLocalPointInRectangle(this.GetComponent<RectTransform>(), roboScreenPos, camera, out local);
    5.         this.GetComponent<RectTransform>().anchoredPosition = local;
    6.         //thisTransform.position.Set(roboScreenPos.x, roboScreenPos.y + 1f, roboScreenPos.z);
    7.     }
    But now the UI element jumps back and forth between its initial position and what I assume is the anchor point each frame, and still doesn't follow the character. At least it moves...

    What am I getting wrong in the above code?
     
  4. ViggyNash

    ViggyNash

    Joined:
    Apr 21, 2015
    Posts:
    9
    After looking around, I found out about transform.TransformPoint, which helped to fix finding the screen point for the target object. However, RectTransformUtility.ScreenPointToLocalPointInRectangle returns one of two values alternately every frame and jumps to a different position when the object's orientation is changed. However, the text sort of loosely follows the object instead of staying still like before.

    Here's the current code:
    Code (csharp):
    1.  
    2. void Update () {
    3.         Vector3 roboScreenPos = camera.WorldToScreenPoint(robot.transform.TransformPoint(roboPos));
    4.         Vector2 local = new Vector2();
    5.         RectTransformUtility.ScreenPointToLocalPointInRectangle(this.GetComponent<RectTransform>(), roboScreenPos, camera, out local);
    6. Debug.Log("Robopos " + roboScreenPos);
    7. //Debug.Log(this.GetComponent<RectTransform>().anchoredPosition);
    8.         this.GetComponent<RectTransform>().localPosition = local;
    9.         //thisTransform.position.Set(roboScreenPos.x, roboScreenPos.y + 1f, roboScreenPos.z);
    10.     }
     
  5. karl_jones

    karl_jones

    Unity Technologies

    Joined:
    May 5, 2015
    Posts:
    7,795
    Last edited: Jan 6, 2016
    zhuchun and rajsoftrajesh like this.
  6. ViggyNash

    ViggyNash

    Joined:
    Apr 21, 2015
    Posts:
    9
    Thanks for your help and the link, they were really helpful. Here's the final code:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class NameFollow : MonoBehaviour {
    5.  
    6.     public Vector3 pos;
    7.  
    8.     public GameObject robot;
    9.     public Camera camera;
    10.     private Vector3 roboPos;
    11.     private RectTransform rt;
    12.     private RectTransform canvasRT;
    13.     private Vector3 roboScreenPos;
    14.  
    15.     // Use this for initialization
    16.     void Start () {
    17.         roboPos = robot.transform.position;
    18.  
    19.         rt = GetComponent<RectTransform>();
    20.         canvasRT = GetComponentInParent<Canvas>().GetComponent<RectTransform>();
    21.         roboScreenPos = camera.WorldToViewportPoint(robot.transform.TransformPoint(roboPos));
    22.         rt.anchorMax = roboScreenPos;
    23.         rt.anchorMin = roboScreenPos;
    24.     }
    25.    
    26.     // Update is called once per frame
    27.     void Update () {
    28.         roboScreenPos = camera.WorldToViewportPoint(robot.transform.TransformPoint(roboPos));
    29.         rt.anchorMax = roboScreenPos;
    30.         rt.anchorMin = roboScreenPos;
    31.     }
    32. }
    Although it now works mostly as I intended, there's a small idiosyncrasy that I'd like to understand. If I set the target's initial position to anything other than the origin of world space, then the UI element offsets from the target by the same distance that the target was offset from the origin. Any idea why that would be?
     
  7. fyrnstudios

    fyrnstudios

    Joined:
    Jun 8, 2020
    Posts:
    1
    from a quick glance at your code - it may be because roboPos = robot.transform.position; is in the start function only. Maybe put it at the top of the update function?