Search Unity

  1. We are migrating the Unity Forums to Unity Discussions by the end of July. Read our announcement for more information and let us know if you have any questions.
    Dismiss Notice
  2. Dismiss Notice

Floating Health Bar

Discussion in 'Scripting' started by slumberus, Jun 22, 2007.

  1. slumberus

    slumberus

    Joined:
    Apr 17, 2007
    Posts:
    39
    The FPS tutorial covered using GUITexture to do health bars for the player. What if I want to put floating healthbars on top of the mobs?

    My initial experiment was:
    1. Creating 2 bar objects with healthbar textures (one with the alpha border texture, the other a plain red bar) and
    2. Place them within the mob hierarchy and
    3. Add a LookAt(camera) billboard script on it to ensure that the bar looks at the player at all times.

    I feel that this is a crude attempt to simulate a floating health bar, My instinct tells me that this should be considered a GUI instead of an object. Any way around this?

    Also can anyone help me with the health reduction script (to move the health bars)? I can't use the tutorial's pixelInset.xMax as this is a GUITexture exclusive script.

    Thanks. :D
     
  2. metervara

    metervara

    Joined:
    Jun 15, 2006
    Posts:
    203
    It's possible to do that with GUI textures actually. Create an empty target gameobject within the mob hierarchy.

    Then use camera.WorldToViewportPoint( target.position ) to get the viewport position for the GUI Texture.

    Alternetively you can place your GUI Texture at position (0,0,0) and use WorldToScreenPoint to update the pixelInset.
     
  3. Bampf

    Bampf

    Joined:
    Oct 21, 2005
    Posts:
    369
    The billboard approach seems better to me. If the character moves behind something or is partially obscured, by other healthbars even, their healthbar will be obscured as well.

    On the other hand, if this is more like a heads-up display, or a Sims-like floating menu, and it should always be in front of everything then the GUI approach might be more appropriate.

    Just depends what you want it to look like, I guess.

    If you go the GUI approach be aware that you may need to set the Z of the GUI object to prioritize which GUI elements paint in front of it. This might be important with multiple healthbars, or if there are GUI elements that always must be in front.
     
  4. slumberus

    slumberus

    Joined:
    Apr 17, 2007
    Posts:
    39
    Ahh thanks metervara for the GUI tip. :D Attempting it now.

    Thanks Bampf too for the heads up. I haven't thought of that yet. I have to see how it turns out.

    But judging from my experience with World of Warcraft HP bars/ Diablo II Loot Text. For instance, if a lot of chars/npc group together, the health bars will 'bump' around against each other,but not overlapping.

    How is that done? Thanks. :D
     
  5. Bampf

    Bampf

    Joined:
    Oct 21, 2005
    Posts:
    369
    You'd have to write code that arranges the list of GUITexture elements so that they don't overlap.

    A straightforward method would be to mentally divide the screen up into a grid, and each GUIText would look at the nearest grid position to see if another GUIText is already there. If so, look for a position further away that is not taken. The search pattern could proceed in a spiral, going further and further out until an empty spot is found.

    Let's say for instance you decide the game screen is about 100 healthbars wide, and about 10 healthbars tall (your bars are tall but narrow.) So the screen is divided into a grid 100x10.

    Calculate the screen coord of each healthbar as Metavara suggested. Then find the nearest point on the 100x10 grid. If there's a bar there already, search adjacent grid positions, moving further and further away, until an open spot is found.

    You may want to declare a 100x10 array for tracking which spots already have a bar in them. You would also need to clear the array and start over when starting to draw a new frame.

    (Also, keep in mind that Unity screen coordinates for GUITexture's range from 0 to 1 on each axis. I talk about 100x10 but that's just to get the idea of the algorithm across.)

    While it would often work, the above algorithm might put some bars right next to their critter, and others far away. A much fancier algorithm might try to minimize the distance all health bars are from their critter. Imagine each healthbar is attached by a rubber band to center of its critter. The healthbars would jostle and push until they minimized the overall energy in the system. I'm sure such an algorithm can be found on the internet if you hunt for it.

    That's probably overkill, unless you are writing something fancy, say, WoW.

    A lazy alternative might be to just let the GUITextures overlap. If there's aren't a lot of critters it wouldn't be too bad. You could use the distance to camera to draw them in the proper Z-order. You could also compute which critter the mouse pointer is over and highlight his healthbar, perhaps by pulling it to the front and making it opaque, or draw just the healthbar of the moused-over critter and no others.
     
  6. slumberus

    slumberus

    Joined:
    Apr 17, 2007
    Posts:
    39
    Wow grids? Dang, gotta look it up after I solve the initial problems first.

    Okay I got the code up, had a problem, though it could be silly variable practice.

    Code (csharp):
    1.  
    2. var HPtarget : Transform;
    3. var HPfabbie : GUITexture;
    4. var HPGUI : GUITexture;
    5.  
    6. function Start() {
    7. HPGUI = Instantiate(HPfabbie,Vector3(0.5,0.5,0),Quaternion.identity);
    8. }
    9.  
    10. function Update () {
    11. var mainCamera = FindCamera();
    12. var p = mainCamera.WorldToViewportPoint(HPtarget.position+Vector3.up*1.5);
    13.  
    14. HPGUI.transform.position = p;
    15.  
    16. }
    17.  
    18. function FindCamera ()
    19. {
    20.     if (camera)
    21.     return camera;
    22.     else
    23.     return Camera.main;
    24. }
    25.  
    When I declare the HPGUI as a GUITexture, it pops out an 'UnassignedReferenceException' stating 'HPGUI.transform.position=p' variable has not been assigned.

    But when I declare HPGUI as a plain variable, the HP bar works just fine. But if i leave them out, I can't use HPGUI.pixelInset as they said they are unable to find the pixelInset variable.
     
  7. Bampf

    Bampf

    Joined:
    Oct 21, 2005
    Posts:
    369
    Sorry if I wasn't clear. When I'm talking about grids I'm not talking about anything built into Unity. You are just calculating positions to snap your healthbars to so that they don't overlap with each other. By dividing up the screen (using a mathematical formula) you can take the "natural" position of a healthbar and snap it to the nearest grid coordinate, as long as another bar isn't there already.

    As for the code you posted. I *think* the problem is that a GUITexture itself doesn't have a transform, its game object does. So you could declare it as a GameObject, then to get to the GUITexture you either use FindComponent or the built-in property .guiTexture. Alternatively, declare it as a GuiTexture and then get its GameObject via the property .gameObject.

    I program Unity in C# so it's possible there's some implicit conversion in JavaScript that makes the above steps unnecessary. Or maybe there's an easier solution. But it would explain your problem so that's my guess.
     
  8. slumberus

    slumberus

    Joined:
    Apr 17, 2007
    Posts:
    39
    OMG you're right Bampf, thanks!. guiTexture solved the problem. I wasn't aware of the concept of a GUITexture WITHIN a Gameobject and needing to call a guiTexture before calling its native property until now. XD

    Here's the fixed code.
    Code (csharp):
    1.  
    2. var HPtarget : Transform;
    3. var HPfabbie : GameObject;
    4. var HPGUI: GameObject;
    5. var healthGUIWidth = 0.0;
    6.  
    7.  
    8. function Start() {
    9. HPGUI = Instantiate(HPfabbie,Vector3(0.5,0.5,0),Quaternion.identity);
    10. healthGUIWidth = HPGUI.guiTexture.pixelInset.width;
    11. }
    12.  
    13. function Update () {
    14. var mainCamera = FindCamera();
    15. var p = mainCamera.WorldToViewportPoint(HPtarget.position+Vector3.up*1.5);
    16. var healthFraction = 0.5;
    17.  
    18. HPGUI.guiTexture.pixelInset.xMax = HPGUI.guiTexture.pixelInset.xMin + healthGUIWidth * healthFraction;
    19.  
    20. HPGUI.transform.position = p;
    21.  
    22. }
    23.  
    24. function FindCamera ()
    25. {
    26.     if (camera)
    27.     return camera;
    28.     else
    29.     return Camera.main;
    30. }
    31.  
    And yeah Bampf I get it about the grid, thanks again for clarifying. :D
     
  9. slumberus

    slumberus

    Joined:
    Apr 17, 2007
    Posts:
    39
    Okay there is some wierd side effects.

    The HP bar follows the mob fine. But if lets say the mob is directly behind the camera view/character, the HP bar pops up right in front of me.

    I guess its displaying exactly as where the screen position is, but doesn't take to account the mob's Z position relative to the camera's local Z position.

    Trying to solve this, currently my idea is that 'if mob is behind camera, disable HP bar?' any other suggestions is welcomed.

    thanks. XD
     
  10. MitchStan

    MitchStan

    Joined:
    Feb 26, 2007
    Posts:
    578
    I had the same issue a while ago:

    Code (csharp):
    1.        
    2. var localPosition = transform.InverseTransformPoint(otherTransform.position);
    3.         if (localPosition.z > 0)
    4.                 { ... do your code here...}
    5.  
     
  11. slumberus

    slumberus

    Joined:
    Apr 17, 2007
    Posts:
    39
    Woot thx mitch. :D works like a charm.

    Code (csharp):
    1.  
    2. var HPtarget : Transform;
    3. var HPfabbie : GameObject;
    4. var HPGUI: GameObject;
    5. var healthGUIWidth = 0.0;
    6.  
    7.  
    8. function Start() {
    9. HPGUI = Instantiate(HPfabbie,Vector3(0.5,0.5,0),Quaternion.identity);
    10. healthGUIWidth = HPGUI.guiTexture.pixelInset.width;
    11. }
    12.  
    13. function Update () {
    14. var mainCamera = FindCamera();
    15.  
    16. cameraRelative = mainCamera.transform.InverseTransformPoint(HPtarget.position);
    17.  
    18. if (cameraRelative.z >0){
    19. var p = mainCamera.WorldToViewportPoint(HPtarget.position+Vector3.up*1.5);
    20. var healthFraction = 0.5;
    21.  
    22. HPGUI.guiTexture.pixelInset.xMax = HPGUI.guiTexture.pixelInset.xMin + healthGUIWidth * healthFraction;
    23.  
    24. HPGUI.transform.position = p;
    25. }
    26. }
    27.  
    28. function FindCamera ()
    29. {
    30.    if (camera)
    31.    return camera;
    32.    else
    33.    return Camera.main;
    34. }
    35.  
     
  12. ben0bi

    ben0bi

    Joined:
    Jun 11, 2011
    Posts:
    1
     
    Last edited: May 4, 2012