- Home /
How to make the Health bar on Enemys head not be in relation to Player(main) Camera?
Hi, I have a Rectangle working as a Health bar for my enemies. The bar always starts in the middle of the Enemies' x position, however the y position is in relation to the player so when i jump the health bar jumps with me. I want the health bar to be on top, and in the middle of the enemies however. (See picture) My Camera is a child of the Player. Here's how i position the Rectangle:
void OnGUI()
{
Vector2 targetPos;
targetPos = RectTransformUtility.WorldToScreenPoint(Camera.main, transform.position);
healthRect = new Rect (targetPos.x, targetPos.y, Screen.width / 10, Screen.height / 50);
float ratio = health / maxHealth;
float rectWidth = ratio*Screen.width / 10;
healthRect.width = rectWidth;
GUI.DrawTexture (healthRect, healthTexture);
}
Any particular reason you're using OnGUI ins$$anonymous$$d of Unity's newer UI system? GUI is legacy except for editor scripting.
I didn't fully understand your request as written, so others may not either. I'd recommend taking greater care in describing what you're doing and why.
To be completely honest, i'm very new to Unity, and to me the Rectangle for the Player Health bar seemed the easiest so i used that, and just tried applying that to the Enemies aswell.
If there are other solutions you would recommend to me, i'm all ears :)
SOLUTION PART 1
While yes the CANVAS was implemented with Unity 4.6... the Legacy OnGUI () still works... Since you are a new programmer let me show you How I do this, and also where I believe your problem is...
*Unfortunately moderators have been slow to approve my previous answer to you.
*Because you are pulling the transform.position directly, I am left to assume that this script is attached to each enemy gameobject
// I believe your RectTransformUtility should work, I just havent used it...
Vector3 tempPos = Camera.main.WorldToScreenPoint (transform.position);
Vector2 targetPos = new Vector2 (tempPos.x, tempPos.y);
Now remembering that when defining this Rect (). That we are actually saying that all the text/textures will fit into the rectangle. So I am curious why would define it's width and height as Screen.width/10. Because your basically saying that as the screen size changes... so will how big this rectangle is. In most games though, regardless of how big or small the screen is.... The health bars are usually the same. And are usually scaled to the game or the players. By using a variable, we can check our placement IN-GA$$anonymous$$$$anonymous$$.... like this:
Rect healthRect = new rect (targetPos.x , targetPos.y, myWidth, myHeight);
And at the top of your script you can use:
public int yAdjustment; // Ill discuss this later
public int myWidth; // this is how wide we want our imaginary rectangle
public int myHeight; // this is how high we want our imaginary rectangle
SOLUTION PART 2 - I ran out of commenting space...
So Let's talk about your original problems and get them fixed. First thing's first!!! Let's talk about your X positioning. The reason it is not centered above your character and it runs off to the right is because of this imaginary rectangle is not centered. According to your code its X,Y position is exactly the same position as your enemy character. $$anonymous$$ost sprites by default, have their GameObject transform positions in the center (Though this can easily be changed if you desired). So to fix it logicaly we can simply divide how wide this rectangle is by 2 so that we get the direct middle of it... And then make this our new X positioning. EXA$$anonymous$$PLE: targetPos.x - (myWidth/2)... And I believe the reason your Y is not working is because ONGUI uses a different coordinate system that the Unity World Coordinates/Screen Coordinates. Specifically the ONGUI x,y = 0,0 starts at the bottom-left of the screen... While the Screen pixels x,y =0,0 starts at the TOP-LEFT. But even if we fix this, your imaginary rectangle will still be in the middle of the sprite... So how do we fix this? Well up above I defined a variable Integer yAdjustment which we will add in to help lift it off of the character and put it above it's head... But first, let's flip the coordinates... The final code should look like this:
//invert the coordinate system since GUI is BOTTO$$anonymous$$,LEFT while Coordinates are TOP,LEFT
targetPos.y = Camera.main.pixelHeight - targetPos.y;
// now place it on the screen
Rect healthRect = new Rect (targetPos.x - (myWidth/2), targetPos.y + yAdjustment, myWidth, myHeight)
then put your draw call on the screen like you normally would. The benefit of putting those public variables at the top of the script is so you can run the game just as it is..... Then you can adjust those variables from the editor while the game is running. This will allow you fine-tune not only the proper width and height of this healthbar... But also you can fine-tune how high on the Y axis from center the healthbar should be so that it is actually above the enemy's head. Once you find the best numbers for myWidth, myHeight, and yAdjustment....... stop running the code and then update the Editor with those new numbers... or simply hard code them in...
I hope this helps, Cheers
Answer by karma0413 · May 31, 2015 at 11:46 AM
What is this script attached to? This script should be attached to every enemy gameObject right?
You realize that in your code your setting up the rectangle width and height to be /10 or /50 of the screen. So on different size screens you will be getting much larger or smaller size of health bar? Where the character size stays the same..was this intended?
Center the X position -- I would make this a constant instead of Screen.width /10. But regardless, You would center it by determining how wide your rectangle is...and then dividing it by two. So because the width of this healthbar is Screen.width/2... You would set your X position as: targetPos.x - (Screen.width /2)
Center the Y position -- I think I may have figured out your problem with the Y coordinates..... The ONGUI functions has a different coordinate layout than Camera.WorldToScreenPoint.....Once you got this figured out, you can simply center the Y where it needs to be by adjusting it with an adjusting variable.... like this:
IT WONT LET ME USE THE DAMN CODE BOX: so here it is in regular text format: //setup a public adjuster, so that you can double check the reference height from the editor and adjust it while the game is running...
public int adjusterY;
//Then just add this number to your Y positioning when you define the drawbox healthRect = new Rect (targetPos.x, targetPos.y + ADJUSTERY, Screen.width / 10, Screen.height / 50);
**On GUI the X,Y = 0,0 starts at the BOTTOM LEFT of the screen **However, the WorldToScreen pixels x,y = 0,0 starting at the TOP LEFT of the screen.
To put the world-screen pixels into the GUI format
//invert the coordinate system since GUI is BOTTOM,LEFT while Coordinates are TOP,LEFT
targetPos.y = Camera.main.pixelHeight - targetPos.y;
Let me know if this helped anything....
Alright, with the help of your 2comments i got it to work exactly as i wanted it to be and even better being able to adjust it for each enemy i will apply it to, thanks a lot for the detailed answer! :)
Answer by error031 · May 31, 2015 at 10:37 AM
Unity versions from 4.6 to newest one doesn't use OnGUI(). Instead they use Canvas & other UI elements, who are simpler for use, for me personaly. I recommend you to start here:
If you are really new to Unity, I recommend also Unity tutorials, they are great, and have all explained
For your specific problem, I use this code:
//I am not too good in c#, i prefer js, so there could be some mistakes
using UnityEngine;
using System.Collections;
using System.UI;
public class EnemyHealthBar : MonoBehaviour {
UI.Image healthBar;
void Update (){
//get coordinates of enemy in 2D plane
Vector2 enemyCoordinates = Camera.main.WorldToScreenPoint(transform.position);
//and get it to top of enemy head
enemyCoordinates.y+=40;
//change position of healthBar, and his fillAmount
healthBar.rectTransform.position = screenCoordinates;
healthBar.fillAmount = enemyHealth/100;
}
}
You can still use the legacy OnGUI ()... I do... I am still waiting for my previous answer to be approved. to address the OPs question
You can use it, but is recommended to switch to newer version. I think for beginner is better to learn this new system, than to learn legacy system that may or may not be supportet in newer versions of Unity.
Alright, i will probably look into the newer UI elements more, thanks either way ! :)