The Elder Scrolls: Morrowind has a rather interesting way to display each focused objects' name. It's all a bit fuzzy in my memory right now, but the most important part I remember perfectly: the object's name is always displayed in the proximity of the object (most of the time on top of it).

My question is: given a 3D mesh, how should I proceed to calculate the name's position on the screen? I'd like to always position it on top of the given mesh for now. Like in this screenshot of Morrowind:

enter image description here

I'm working under Unity, if it helps. The first thing that comes to my mind is to somehow use the distance between the player and each object, then take the object's actual height in consideration, and determine a line in 2D space (the screen) that just "touches" (intersects) the top of the 2D representation of the mesh (what we're actually able to view on the screen). Something like this: enter image description here

But this feels a bit complicated and clunky, and might not work for characters that are not standing tall for example.

The 2nd thing that comes to my mind: would there be a way to achieve this using matrix multiplication, in the same way 2D representations of 3D objects are obtained?


  • \$\begingroup\$ If you already have bounding boxes for collision checks you could use easily make use of them. \$\endgroup\$
    – danijar
    Mar 26 '13 at 15:52
  • \$\begingroup\$ Billboard positioned at the top of (or slightly above) the character model. \$\endgroup\$
    – MichaelHouse
    Mar 26 '13 at 17:21
  • \$\begingroup\$ Never heard of billboarding before :) I've taken a look at it; this also makes Xerosigma's answer make more sense to me. I wonder if I can combine this with decals to make a different text show up for different objects (or is there a better way?) \$\endgroup\$
    – user15805
    Mar 26 '13 at 17:44

If you are using GameObjects you could easily:

float SomeYOffset = 1.1f;

// Set position of GUI element
nameBar.position = new Vector3(player.position.x, player.position.y * SomeYOffset, player.position.z);

If you're not using GameObjects, you can take the vertex with the highest Y value and offset the nameBar mesh from there.

for(int i = 0; i < nameBar.verts.Length; i++)
    nameBar.verts[i] += new Vector3(vert.x, vert.y += SomeYOffset, player.z);
  • \$\begingroup\$ Using the vertext with the highest Y-value might lead to weird placement on the horizontal axis. Maybe it would be better to use the Y-value of the highest vertext and the average of the outmost vectors for the X- and Z-coordinate. \$\endgroup\$
    – Philipp
    Mar 26 '13 at 15:50
  • \$\begingroup\$ Averaging could work with that approach. I use averaging to determine the center of my dynamic meshes all the time. \$\endgroup\$ Mar 26 '13 at 15:52
  • 2
    \$\begingroup\$ Is player.position.y *= SomeYOffset meant to be player.position.y * SomeYOffset? \$\endgroup\$
    – danijar
    Mar 26 '13 at 15:52
  • \$\begingroup\$ Lol yes, yes it is. \$\endgroup\$ Mar 26 '13 at 15:53
  • \$\begingroup\$ So basically you're telling me to treat the namebar itself as a 3D mesh? \$\endgroup\$
    – user15805
    Mar 26 '13 at 15:57

Some things to consider here are that the object might not be rotated towards the camera, and that the position in 3d world space of the object is not always linear. So, objects farther away from the camera would need the font size reduced in relation to their depth in the view.

Here is some sample code that transforms relative to the camera:

   objectname = GameObject.Instantiate(Resources.Load("HoverText")) as GameObject;
   GUIText name = objectname.GetComponent<GUIText>();

   name.text = "Test Name"; // Change Me

   objectname.transform.position = Camera.mainCamera.WorldToViewportPoint(transform.position) + new Vector3(0f, 0.05f, 0f); // Change the 0.05f value to some other value for desired height

"HoverText" should be in the Resources directory in the project. The gameobject itself should have a GUIText component added to it.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy