- Home /
Lifebars not appearing above enemies' heads
Hi guys,
I'm using the following script to show life bars above my game's enemies, or at least that's the intent:
using UnityEngine; using System.Collections;
public class EnemyLifeBar : MonoBehaviour {
public Transform target;
public Vector3 offset = Vector3.up;
public bool clampToScreen = false;
public float clampBorderSize = 0.5f;
public bool useMainCamera = true;
public Camera cameraToUse;
public Texture2D texture;
public Rect fullRect;
public float maxHealth = 100;
public float currHealth = 100;
private Camera cam;
private Transform camTransform;
private static Vector3 thisVector;
// Use this for initialization
void Start () {
thisVector = new Vector3(transform.position.x, transform.position.y, transform.position.z);
if (useMainCamera)
cam = Camera.main;
else
cam = cameraToUse;
}
//void OnGUI()
//{
// print(string.Format("Vector [{0} ; {1} ; {2}]", thisVector.x, thisVector.y, thisVector.z));
// float healthFrac = currHealth / maxHealth;
// Rect actualRect = new Rect(thisVector.x, thisVector.y, fullRect.width * healthFrac, fullRect.height);
// GUI.BeginGroup(actualRect, texture);
// Rect innerRect = new Rect(0, 0, fullRect.width, fullRect.height);
// GUI.DrawTexture(innerRect, texture);
// GUI.EndGroup();
//}
void OnRenderObject()
{
print(string.Format("Vector [{0} ; {1} ; {2}]", thisVector.x, thisVector.y, thisVector.z));
float healthFrac = currHealth / maxHealth;
Rect actualRect = new Rect(thisVector.x, thisVector.y, fullRect.width * healthFrac, fullRect.height);
GUI.BeginGroup(actualRect, texture);
Rect innerRect = new Rect(0, 0, fullRect.width, fullRect.height);
GUI.DrawTexture(innerRect, texture);
GUI.EndGroup();
}
// Update is called once per frame
void Update () {
camTransform = cam.transform;
if (clampToScreen)
{
Vector3 relativePosition = camTransform.InverseTransformPoint(target.position);
relativePosition = new Vector3(relativePosition.x, relativePosition.y, Mathf.Max(relativePosition.z, 1.0f));
thisVector = cam.WorldToViewportPoint(camTransform.TransformPoint(relativePosition + offset));
thisVector = new Vector3(Mathf.Clamp(thisVector.x, clampBorderSize, 1.0f - clampBorderSize),
Mathf.Clamp(thisVector.y, clampBorderSize, 1.0f - clampBorderSize),
thisVector.z);
}
else
{
thisVector = cam.WorldToViewportPoint(target.position + offset);
}
}
}
The console prints out the following when I click the play button, with differing values for the vector:
Vector [-0.2314034 ; -2.436603 ; 0.2710707]
UnityEngine.MonoBehaviour:print(Object)
EnemyLifeBar:OnRenderObject() (at Assets/Entities/Enemies/Scripts/EnemyLifeBar.cs:46)
The script is attached to an enemy prefab, with the slots set as follows:
Target: Pitbull Offset: (0,0, 0) Clamp to Screen: false Clamp Border Size: 0.5 Use Main Camera: true Camera to Use: None (Camera) Texture: EnemyLifeBar Full Rect: (0, 0, 2, 1) Max Health: 100 Curr Health: 100
The problem is that when I press play, I get a strange thing where a green rectangle (the health bar), and only one of them, appears in strange locations on the screen, seemingly unconnected with the enemies in the scene, with only one bar appearing at a time. The bar doesn't appear directly above the nearest enemy's head, for example. Moreover, trying to scale it down (because it's way too big) merely causes it to go away. What's going on? Should I attempt to take a new approach to enemy life bars, using the following Player Life Script as a reference?
using UnityEngine; using System.Collections;
public class PlayerLifeBar : MonoBehaviour {
public int maxHealth = 100;
public int curHealth = 100;
public float healthBarLength;
public Font healthBarFont;
// Use this for initialization
void Start ()
{
healthBarLength = Screen.width / 2;
}
// Update is called once per frame
void Update ()
{
AdjustCurrentHealth(0);
}
void OnGUI()
{
GUI.skin.box.font = healthBarFont;
GUI.Box(new Rect(10, 10, Screen.width / 2 / (maxHealth / curHealth), 40), curHealth + "/" + maxHealth);
}
public void AdjustCurrentHealth(int adj)
{
curHealth += adj;
if (curHealth <= 0)
SendMessage("Die");
if (curHealth > maxHealth)
curHealth = maxHealth;
if(maxHealth < 1)
maxHealth = 1;
healthBarLength = (Screen.width / 2 ) * (curHealth / (float)maxHealth);
}
}
MachCUBED
Answer by MachCUBED · Feb 01, 2012 at 11:16 PM
I found an answer on YouTube:
http://www.youtube.com/watch?v=iER_LFwi1-U
This script can be adapted with an adjust health function and it should work.
Answer by KrisCadle · Jan 22, 2012 at 10:44 PM
There is a really useful script in the Wiki if you haven't seen it. It's an easy label solution which makes a GUI label follow a 3D object in model space. This way the label attributes can be adjusted (stay the same size, shrink when the character goes far away, stay on the screen if the character wanders off screen (for airplane games) etc... )
Check it out http://unifycommunity.com/wiki/index.php?title=ObjectLabel
I added the Javascript version of ObjectLabel, and then set Clamp to Screen to true. I have one GUILabel, and it's currently set to follow one of my enemies (and only one of them) around, by way of setting said enemy as the target. I tried editing the script to look like this:
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 use$$anonymous$$ainCamera = true; // Use the camera tagged $$anonymous$$ainCamera var cameraToUse : Camera; // Only use this if use$$anonymous$$ainCamera is false private var cam : Camera; private var thisTransform : Transform; private var camTransform : Transform;
function Start () { thisTransform = transform; if (use$$anonymous$$ainCamera) cam = Camera.main; else cam = cameraToUse; camTransform = cam.transform; }
// Find the closest enemy function FindClosestEnemy() : GameObject { // Find all game objects with tag Enemy var gos : GameObject[]; gos = GameObject.FindGameObjectsWithTag("Enemy"); var closest : GameObject; var distance = $$anonymous$$athf.Infinity; var position = transform.position; // Iterate through them and find the closest one for (var go : GameObject in gos) { var diff = (go.transform.position - position); var curDistance = diff.sqr$$anonymous$$agnitude; if (curDistance < distance) { closest = go; distance = curDistance; } } return closest; }
function Update () {
enemy = FindClosestEnemy();
go = enemy.transform;
if (clampToScreen) {
var relativePosition = camTransform.InverseTransformPoint(target.position);
relativePosition.z = $$anonymous$$athf.$$anonymous$$ax(relativePosition.z, 1.0);
thisTransform.position = cam.WorldToViewportPoint(camTransform.TransformPoint(relativePosition + offset));
thisTransform.position = Vector3($$anonymous$$athf.Clamp(thisTransform.position.x, clampBorderSize, 1.0-clampBorderSize),
$$anonymous$$athf.Clamp(thisTransform.position.y, clampBorderSize, 1.0-clampBorderSize),
thisTransform.position.z);
}
else {
thisTransform.position = cam.WorldToViewportPoint(target.position + offset);
}
}
@script RequireComponent(GUIText)
The idea is to make it follow whichever enemy is closest to the player. The problem is that the script only works without errors if the target is set to a given enemy in the editor (and it will only follow that enemy). However, if I don't set the target, I get a bunch of errors and a label that won't follow any of my enemies. In sort, you helped me with one problem, but I ended up with another one.
I tried the following code for life bars above my enemies' heads:
using UnityEngine; using System.Collections;
public class EnemyLifeBar : $$anonymous$$onoBehaviour {
public Transform target; // Object that this label should follow
public Vector3 offset = Vector3.up; // Units in world space to offset; 1 unit above object by default
public bool clampToScreen = false; // If true, label will be visible even if object is off screen
public float clampBorderSize = 0.05f; // How much viewport space to leave at the borders when a label is being clamped
public bool use$$anonymous$$ainCamera = true; // Use the camera tagged $$anonymous$$ainCamera
public Camera cameraToUse ; // Only use this if use$$anonymous$$ainCamera is false
public int maxHealth = 100;
public int curHealth = 100;
public float healthBarLength;
public Font healthBarFont;
Camera cam;
Transform thisTransform;
Vector3 thisVector;
Transform camTransform;
void Start ()
{
healthBarLength = 200;
thisVector = new Vector3(transform.position.x, transform.position.y, transform.position.z);
if (use$$anonymous$$ainCamera)
cam = Camera.main;
else
cam = cameraToUse;
}
void OnGUI()
{
GUI.skin.box.font = healthBarFont;
GUI.Box(new Rect(thisVector.x, thisVector.y, healthBarLength / (maxHealth / curHealth), 20), curHealth + "/" + maxHealth);
}
void Update()
{
camTransform = cam.transform;
if (clampToScreen)
{
Vector3 relativePosition = camTransform.InverseTransformPoint(target.position);
relativePosition = new Vector3(relativePosition.x, relativePosition.y, $$anonymous$$athf.$$anonymous$$ax(relativePosition.z, 1.0f));
thisVector = cam.WorldToViewportPoint(camTransform.TransformPoint(relativePosition + offset));
thisVector = new Vector3($$anonymous$$athf.Clamp(thisVector.x, clampBorderSize, 1.0f - clampBorderSize),
$$anonymous$$athf.Clamp(thisVector.y, clampBorderSize, 1.0f - clampBorderSize),
thisVector.z);
}
else
{
thisVector = cam.WorldToViewportPoint(target.position + offset);
}
}
public void AdjustCurrentHealth(int adj)
{
curHealth += adj;
if (curHealth <= 0)
Send$$anonymous$$essage("Die");
if (curHealth > maxHealth)
curHealth = maxHealth;
if(maxHealth < 1)
maxHealth = 1;
healthBarLength = healthBarLength * (curHealth / (float)maxHealth);
}
}
It causes the enemies' small life bars to appear in the upper left-hand corner of the screen, with very little movement no matter what. This is an issue whether or not screen clamping is enabled. What is wrong with my above code?
Answer by KrisCadle · Mar 09, 2012 at 07:53 PM
You could also try using the ObjectLabel as a "football", i.e. attach it to an empty object and change it's transform to the transform of the closest enemy. just an alternate method...