Based on the discussion above, I'd recommend moving over to the new UI system for content like this. It will be a bit of an adjustment at first, but it gives you a lot of flexibility:
Using UI Image components, you can create scalable health bars, using the "sliced" type to get rounded corners that don't distort or get cut off as the bar changes size, or the various "filled" methods to reveal a portion of the bar (including circular bars if you wanted)
You can set up animations and effects on the bars in the editor, instead of needing to describe every behaviour in code within the OnGUI
method. This also makes it more straightforward for a team to collaborate on UI, since it's not bottlenecked in one file
When you need different variations on the UI (eg. a boss character who needs a super-fancy-looking health bar) you can create them as new prefabs in the editor, instead of needing to maintain multiple copies of your UI-drawing script for each case
There's tons of tutorials out there for creating the health bar, so I won't go into too much detail about the asset setup here, focusing instead on the script to position it correctly.
So, I'll assume you've set up...
A UI Canvas that's containing your health UI, set to either Screen Space Camera or Screen Space Overlay modes (World space would be handled differently, if you want to go there)
A GameObject child of your canvas representing your health bar. It probably contains one or more child objects of its own for the various visuals (eg. bar frame, bar fill, character name text, status effect icon...)
Now, we can attach this script to the health bar GameObject:
public class UIAnchor : MonoBehaviour {
// Assign this to the object you want the health bar to track:
public Transform objectToFollow;
// This lets us tweak the anchoring position in the object's local space
// eg. if you want the bar to appear above the unit's head.
public Vector3 localOffset;
// This lets us tweak the anchoring position in our canvas space
// eg. if we want the UI to sit off to the right on our screen.
public Vector3 screenOffset;
// Cached reference to the canvas containing this object.
// We'll use this to position it correctly
RectTransform _myCanvas;
// Cache a reference to our parent canvas, so we don't repeatedly search for it.
void Start () {
_myCanvas = GetComponentInParent<Canvas>().GetComponent<RectTransform>();
}
// Use LateUpdate to apply the UI follow after all movement & animation
// for the frame has been applied, so we don't lag behind the unit.
void LateUpdate () {
// Translate our anchored position into world space.
Vector3 worldPoint = objectToFollow.TransformPoint(localOffset);
// Translate the world position into viewport space.
Vector3 viewportPoint = Camera.main.WorldToViewportPoint(worldPoint);
// Canvas local coordinates are relative to its center,
// so we offset by half. We also discard the depth.
viewportPoint -= 0.5f * Vector3.one;
viewportPoint.z = 0;
// Scale our position by the canvas size,
// so we line up regardless of resolution & canvas scaling.
Rect rect = _myCanvas.rect;
viewportPoint.x *= rect.width;
viewportPoint.y *= rect.height;
// Add the canvas space offset and apply the new position.
transform.localPosition = viewportPoint + screenOffset;
}
}
This does have the effect of separating the health bar from the character object/script, which can be awkward when you're spawning characters at runtime, as you need to do a little work to spawn their health bar too and ensure it's wired-up correctly. Two approaches you can consider:
Add the health bar as a child of your character prefab, so they get spawned together. Modify the Start
method above so instead of searching for a parent canvas, we find the main UI canvas in the scene and re-parent the health bar to it during initialization. Since both exist in the same prefab, you can have all their dependencies wired-up in advance (though without nested prefab support, this might mean you end up duplicating assets).
Have your character's health component expose a variable for a health bar prefab. During this health script's Start
method, it spawns an instance of this prefab and parents it to the scene's UI canvas. It will also need to handle wiring-up the health bar to follow it, and its own health update to scale the right part of the health bar.
healthBarPosition
as a constant - you never change it in your Update method, so it always has the same value, and thus the same absolute position in the viewport (unless there's another script modifying it?). Before we suggest alternatives, I'd like to check whether there's a particular reason you've gone this route. Would you be interested in solutions that use the new features, or would you prefer to stick to OnGUI? \$\endgroup\$ – DMGregory♦ Oct 18 '16 at 22:28