I'm a relative beginner trying to learn Unity, and I'm trying to do something that I thought would be relatively simple: Have a piece of text hover over an object, following it if it moves around the screen. I understand that a UI element can't be directly attached to an object because it has to exist on a canvas, so I tried writing a simple script that would move the UI text depending on where the object is on the screen.
Here's the code:
using UnityEngine;
using System.Collections;
public class NameFollow : MonoBehaviour {
public GameObject robot;
public Camera camera;
private Transform thisTransform;
private Vector3 roboPos;
// Use this for initialization
void Start () {
roboPos = robot.transform.position;
thisTransform = this.transform;
}
// Update is called once per frame
void Update () {
Vector3 roboScreenPos = camera.WorldToScreenPoint(roboPos);
Vector2 local = new Vector2();
RectTransformUtility.ScreenPointToLocalPointInRectangle(this.GetComponent<RectTransform>(), roboScreenPos, camera, out local);
this.GetComponent<RectTransform>().anchoredPosition = local;
//thisTransform.position.Set(roboScreenPos.x, roboScreenPos.y + 1f, roboScreenPos.z);
}
}
However, the text just seems to jump from its original location to what seems to be the anchor point on the canvas and back every frame, so it just flickers between the two locations. It doesn't seem to respond to the target's movement at all.
Can someone point out what mistake I'm making in my code? I'm not familiar enough with Unity UI scripting to figure it out, but conceptually that makes sense to me.
UPDATE
I managed to kind of get there
void Update () {
Vector3 roboScreenPos = camera.WorldToScreenPoint(robot.transform.TransformPoint(roboPos));
Vector2 local = new Vector2();
RectTransformUtility.ScreenPointToLocalPointInRectangle(this.GetComponent<RectTransform>(), roboScreenPos, camera, out local);
Debug.Log("Robopos " + roboScreenPos);
//Debug.Log(this.GetComponent<RectTransform>().anchoredPosition);
this.GetComponent<RectTransform>().anchoredPosition = local;
//thisTransform.position.Set(roboScreenPos.x, roboScreenPos.y + 1f, roboScreenPos.z);
}
It seems the jumping between two positions thing is being caused by RectTransformUtility.ScreenPointToLocalPointInRectangle
, since roboScreenPos
shows a consistent value as long as nothing moves, and changes reasonably when it does move.
Am I using that function the right way?
UPDATE 2
I was pointed to a not very intuitive solution, but it mostly works:
rt = GetComponent<RectTransform>();
canvasRT = GetComponentInParent<Canvas>().GetComponent<RectTransform>();
roboScreenPos = camera.WorldToViewportPoint(robot.transform.TransformPoint(roboPos));
rt.anchorMax = roboScreenPos;
rt.anchorMin = roboScreenPos;
I guess the idea is that if you only give the UI element one place to put itself, it has to put itself in that place. Maybe. Apparently I don't quite understand the way anchoredposition, anchorMax, and anchorMin work.
However it works, the UI text does now follow the character around as intended. The one issue I have now is that if the target object starts at any position that isn't the origin, then the UI text permanently offsets its position by that distance from the target, essentially placing it twice the distance away from the origin in the same direction. I does still move along with the target, but always with that offset.
Here's the current code:
public class NameFollow : MonoBehaviour
{
public Vector3 pos;
public GameObject robot;
public Camera camera;
private Vector3 roboPos;
private RectTransform rt;
private RectTransform canvasRT;
private Vector3 roboScreenPos;
// Use this for initialization
void Start()
{
roboPos = robot.transform.position;
rt = GetComponent<RectTransform>();
canvasRT = GetComponentInParent<Canvas>().GetComponent<RectTransform>();
roboScreenPos = camera.WorldToViewportPoint(robot.transform.TransformPoint(roboPos));
rt.anchoredPosition = roboScreenPos + new Vector3(0,45,0);
//rt.anchorMax = roboScreenPos;
//rt.anchorMin = roboScreenPos;
}
// Update is called once per frame
void Update()
{
roboScreenPos = camera.WorldToViewportPoint(robot.transform.TransformPoint(roboPos));
rt.anchorMax = roboScreenPos;
rt.anchorMin = roboScreenPos;
}
}
As you can see, I tried setting the position of the element in Start, but that doesn't help. It's not a major issue since it can be easily worked around, but I want to understand what's going on.