Bug with NPC name label

Hey, I’m experiencing a bit of a bug with the NPC label script that I’m using to make a name appear above the NPC’s head. Basically what happens is the NPC name will come back onto my screen for some odd reason if I run away from the NPC, even if I’m far away from the NPC. I’ll post a video that more clearly shows what I’m talking about:

The bug can first be seen around 1:37.

Here’s the code:

var target : Transform;  // Object that this label should follow

var offset = Vector3.up;    // Units in world space to offset; 1 unit above object by default
var clampToScreen = false;  // If true, label will be visible even if object is off screen
var clampBorderSize = .05;  // How much viewport space to leave at the borders when a label is being clamped
var useMainCamera = true;   // Use the camera tagged MainCamera
var cameraToUse : Camera;   // Only use this if useMainCamera is false
private var cam : Camera;
private var thisTransform : Transform;
private var camTransform : Transform;

function Start () {
    thisTransform = transform;
    if (useMainCamera)
        cam = Camera.main;
    else
        cam = cameraToUse;
    camTransform = cam.transform;
}

function Update () {
    if (clampToScreen) {
        var relativePosition = camTransform.InverseTransformPoint(target.position);
        relativePosition.z = Mathf.Max(relativePosition.z, 1.0);
        thisTransform.position = cam.WorldToViewportPoint(camTransform.TransformPoint(relativePosition + offset));
        thisTransform.position = Vector3(Mathf.Clamp(thisTransform.position.x, clampBorderSize, 1.0-clampBorderSize),
                                         Mathf.Clamp(thisTransform.position.y, clampBorderSize, 1.0-clampBorderSize),
                                         thisTransform.position.z);
    }
    else {
        thisTransform.position = cam.WorldToViewportPoint(target.position + offset);
    }
}

@script RequireComponent(GUIText)

bump

No one has any idea what the problem here is, or how to fix it?

I’m not quite sure what you mean here. Isn’t the NPC still in shot (although against a dark background)? Do you want the labelling to cut out at a nearer distance?

Watch the video starting at 1:37 (you don’t see the problem before that). When the player walks away from the NPC, the NPC’s name and the quest marker icon swing back around onto the screen even if the NPC is behind the player and not on screen.

If you don’t understand from the video, I have also uploaded my project to my website, and you can try playing it for yourself:

The problem is likely that the inverse camera transform happens to be able to give on-screen coordinates for objects that are behind the camera. This is common to most inverse camera transform operations I have seen, because of how they are done.

The solution is to test the target position against the camera position orientation yourself, and not render the label when the label is behind the camera. I can elaborate on this if needed, but you may already understand.

Basically it would look like this (assuming your FOV is less than 90 degrees!):

if(Vector3.Dot(camTransform.forward, target.position - camTransform.position) <= 0.0f)
    //whatever you need to do to explicitly hide your label (disable a component?)
else
    // whatever you need to do to stop hiding your label

I appreciate your help, but I’m afraid I’m very new to coding and don’t really know how to do half the things you said.

Could you perhaps break your explanation down into simpler steps?

The the dot product of two normalized vectors is the cosine of the angle between the vectors. What this means here is that if the dot product is less than 0 then the vectors are facing away from each other by more than 90 degrees.

The dot product has many other useful properties:

http://knol.google.com/k/dot-product-cross-product-in-3d#

In this case pixels isn’t bothering to normalize, as the dot of two vectors is always negative if they are facing more than 90 degrees.

The Why

It may be hard to grasp without a visual aid for your situation. Imagine your camera, floating in space and pointing in some direction. Draw an arrow starting inside the camera and pointing straight out of the lens.

Now draw an arrow from inside the camera to the point in space where the NPC exists.

If the angle between those two arrows (or vectors) is greater than 90 degrees, you can be certain that the NPC is behind the camera. It may help to imagine a few different scenarios and draw them on paper to get a feel for why this is.

Suffice it to say that for the sake of efficiency, there is some ambiguity introduced when things are behind the camera and you try to map them to screen space; so it is best to not even attempt to draw such things in those cases.

The How

Pakfront did a fine job of explaining the dot product. If you look at the code I posted, you will see that I call Vector3.Dot() which takes two vectors and returns their dot product.

The two vectors I provide it are “camTransform.forward” (this is the arrow pointing straight out of your camera lens) and “target.transform.position - camTransform.position” (this is the arrow pointing from the camera to your NPC).

Because of how the dot product works, as explained by pakfront, Vector3.Dot() will always return a negative number when the vectors are at least 90 degrees apart. Therefore, the single “if” statement provided is sufficient to know whether or not the NPC is behind your camera.

Hiding/showing the name

I assume that because you’re new to coding, this is also an unknown to you. Unfortunately I would need to know more about your project before being absolutely sure of how you would approach this.

From what I have seen though, I can guess that the full code you want to insert at the start of your Update() function should look like this:

if( Vector3.Dot( camTransform.forward, target.transform.position - camTransform.position ) <= 0 )
    gameObject.GetComponent(GUIText).enabled = false;
else
    gameObject.GetComponent(GUIText).enabled = true;

Followed by the rest of your existing code, this should eliminate the NPC name appearing on-screen when the NPC is behind your view.