Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

HealthBar below Zero

Discussion in 'Scripting' started by Yourname, Apr 18, 2015.

  1. Yourname

    Yourname

    Joined:
    May 21, 2013
    Posts:
    15
    Does anyone know how to make the healthbar stop at zero? (it currently goes below zero) Thank you.
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3. using UnityEngine.UI;
    4.  
    5. public class HealthBar : MonoBehaviour
    6. {
    7.     #region FIELDS
    8.  
    9.     // The player's current health
    10.     public int currentHealth;
    11.  
    12.     // The player's max health
    13.     public int maxHealth;
    14.  
    15.     // The health bar's speed
    16.     public float healthSpeed;
    17.  
    18.     // The health text
    19.     public Text healthText;
    20.  
    21.     // The health's image, this is used for color changing
    22.     public Image visualHealth;
    23.  
    24.     // The current xValue of the health
    25.     private float currentValue;
    26.  
    27.     // How often can I take damage
    28.     public float cooldown;
    29.  
    30.     // Indicates if we can take damage or not
    31.     private bool onCD;
    32.  
    33.     // The healthbar's canvas
    34.     public Canvas canvas;
    35.  
    36.     #endregion
    37.  
    38.     #region PROPERTIES
    39.  
    40.     // Property for accessing the player's health
    41.     public int Health
    42.     {
    43.         get
    44.         {
    45.             return currentHealth;
    46.         }
    47.         set
    48.         {
    49.             currentHealth = value;
    50.         }
    51.     }    
    52.  
    53.     #endregion
    54.  
    55.     // Use this for initialization
    56.     void Start()
    57.     {
    58.         // Sets all start values
    59.         onCD = false;
    60.      
    61.         //Sets the current health to the maxHealth
    62.             currentHealth = maxHealth;
    63.     }
    64.  
    65.     // Update is called once per frame
    66.     void Update()
    67.     {
    68.         HandleHealthbar();
    69.         if (Input.GetKeyDown(KeyCode.Space))
    70.         {
    71.             Health -= 10;
    72.         }
    73.     }
    74.  
    75.     // Handles the healthbar my moving it and changing color
    76.     private void HandleHealthbar()
    77.     {
    78.         // Writes the current health in the text field
    79.         healthText.text = "Health: " + currentHealth;
    80.      
    81.         // Maps the min and max position to the range between 0 and max health
    82.         currentValue = Map( currentHealth, 0, maxHealth, 0, 1);
    83.      
    84.         // Sets the fillAmount of the health to simulate reduction of health
    85.         visualHealth.fillAmount = Mathf.Lerp( visualHealth.fillAmount, currentValue, Time.deltaTime * healthSpeed );
    86.      
    87.         // If we have more than 50% health we use the green colors
    88.         if ( currentHealth > maxHealth /2 )
    89.         {
    90.             visualHealth.color = new Color32( ( byte ) Map( currentHealth, maxHealth / 2, maxHealth, 255, 0 ), 255, 0, 255 );
    91.         }
    92.         // If we have less than 50% health we use the red colors
    93.         else
    94.         {
    95.             visualHealth.color = new Color32( 255, ( byte ) Map ( currentHealth, 0, maxHealth / 2, 0, 255 ), 0, 255);
    96.         }
    97.     }
    98.  
    99.     void OnTriggerStay( Collider other )
    100.     {
    101.         // Used for simulating taking damage
    102.         if ( other.tag == "Damage" )
    103.         {
    104.             if ( !onCD && currentHealth > 1 )
    105.             {
    106.                 // Makes sure that we can't take damage right away
    107.                 StartCoroutine ( CoolDownDmg() );
    108.                 // Uses the Health Property so that we recolor and rescale the health when we change it
    109.                 Health -= 1;
    110.             }
    111.         }
    112.      
    113.         // Used for simulating gaining health
    114.         if ( other.tag == "Health" )
    115.         {
    116.             if ( !onCD && currentHealth < maxHealth )
    117.             {
    118.                 // Makes sure that we can't take damage right away
    119.                 StartCoroutine( CoolDownDmg() );
    120.                 // Uses the Health Property so that we can recolor and rescale the health when we change it
    121.                 Health += 1;
    122.             }
    123.         }
    124.     }
    125.  
    126.     // Keeps track of the damage CD
    127.     IEnumerator CoolDownDmg()
    128.     {
    129.         onCD = true;
    130.         // Waits a while before we are able to take damage again
    131.         yield return new WaitForSeconds( cooldown );
    132.         onCD = false;
    133.     }
    134.  
    135.     // This method maps a range of numbers into another range
    136.     // <param name="x">The value to evaluate</param>
    137.     // <param name="in_min">The minimum value of the evaluated variable</param>
    138.     // <param name="in_max">The maximum value of the evaluated variable</param>
    139.     // <param name="out_min">The minimum number we want to map to</param>
    140.     // <param name="out_max">The maximum number we want to map to</param>
    141.     // <returns></returns>
    142.     public float Map(float x, float in_min, float in_max, float out_min, float out_max)
    143.     {
    144.         return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
    145.     }
    146.  
    147.  
    148. }
    149.  
     
    Last edited: Apr 18, 2015
  2. Stef_Morojna

    Stef_Morojna

    Joined:
    Apr 15, 2015
    Posts:
    289
    Code (CSharp):
    1. if (Input.GetKeyDown(KeyCode.Space))
    2.        {
    3.             Health -= 10;
    4.            if Health < 0 {
    5.                   Health = 0;
    6.                 }
    7.        }
    1. Evry time you decrease the healh check if its less than 0 and if it is set it back to 0
     
    Last edited: Apr 21, 2015
  3. jtsmith1287

    jtsmith1287

    Joined:
    Aug 3, 2014
    Posts:
    787
    I think this is a classic case of over engineering. I've got several "health" style scripts that work flawlessly with clean code and none of them are this complicated. My general approach is to use a property.
    Code (CSharp):
    1. public int Health {
    2.     get {
    3.         return Health;
    4.     }
    5.     set {
    6.         Health = value;
    7.         if (Health < 0)
    8.             Health = 0;
    9.     }
    10. }
    This way you can set Health to anything. You could even add a line in there to make sure it doesn't go over your maxHealth as well. So then you just say Health = 5432948392 and it'll be <maxHealth> or Health = -5839285932 and it will be 0.

    You could probably use Mathf.Clamp as well, but.. meh. I like the property. I actually cheat and have a call to my "this entity is now dead so clean it up" method inside my if < 0 block. It's considered bad form but it works great in this case.
     
  4. renaissanceCoder1

    renaissanceCoder1

    Joined:
    Apr 8, 2015
    Posts:
    127
    If you use that code, do not use capital 'H' health inside the property. This will cause the program to crash. Due to infinite looping.
     
    Joe-Censored likes this.
  5. Yourname

    Yourname

    Joined:
    May 21, 2013
    Posts:
    15
    I added:


    Code (CSharp):
    1. if (Health <= 0)
    2.          {
    3.              Health = 0;
    4.          }
    The health now stops going negative, but there is still one issue:

    once the currentHealth = 0, the fill keeps cycling (is there a way to stop the fill from continuously going down?)

    I tried this:


    Code (CSharp):
    1. // Sets the fillAmount of the health to simulate reduction of health
    2. if currentHealth !=0 ()
    3. {
    4. visualHealth.fillAmount = Mathf.Lerp( visualHealth.fillAmount, currentValue, Time.deltaTime * healthSpeed );
    5. }
    The issue is once current health = 0 the fill amount stops in its tracks (ie. does not reach 0 fill since it is going down over time) Anyone know how to fix this?

     
    Last edited: Apr 18, 2015
  6. passerbycmc

    passerbycmc

    Joined:
    Feb 12, 2015
    Posts:
    1,741
    Just clamp the value after you subtract from it. Mathf.Clamp

    Also why do you comment everything even when the intent is already obvious based on the names. Commenting the obvious just adds to visual clutter in your code and leads to worse readability. Comments should be about why you did something, not just saying the same thing the code is in different words.
     
    Last edited: Apr 18, 2015
    jtsmith1287 and Kiwasi like this.
  7. Yourname

    Yourname

    Joined:
    May 21, 2013
    Posts:
    15
    I have to for my class
     
  8. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Your teacher needs a lesson in proper coding practise. Comments are to explain intent and reasoning. Not to restate your code. Your code should be largely self documenting, using good variable names and so forth.

    This guy says it better then me.

    http://visualstudiomagazine.com/articles/2013/06/01/roc-rocks.aspx?m=1
     
    passerbycmc and jtsmith1287 like this.
  9. vothka

    vothka

    Joined:
    Mar 27, 2015
    Posts:
    59
    why even use a If-condition

    your health can only be between 0 and maxhealth as you have your code right now.
    So there is no need to update your healthbar only if it is != 0.
    without the if condition it should therefor work at the 0 point.
     
  10. jtsmith1287

    jtsmith1287

    Joined:
    Aug 3, 2014
    Posts:
    787
    Ya aside from documenting my methods and properties, I only comment lines that aren't immediately obvious, or use some obscure logic. For example, using an item number that is counted from 1 to get the next index of the associated list, which is of course counted from 0. When you do something like item = mylist[olditem.IDNumber] it looks like you're trying to get that item, but it's actually 1 beyond. Even if you're familiar with your code at first glance this is counter intuitive. So I'd comment that line to make it clear what is happening.
     
  11. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    7,462
    Code (csharp):
    1.  
    2.         public float currentHealth;
    3.         public float maxHealth;
    4.  
    5.         void Update()
    6.         {
    7.             if (currentHealth <= 0 || currentHealth >= maxHealth)
    8.             {
    9.                 currentHealth = (currentHealth <= 0) ? 0 : maxHealth;
    10.             }
    11.         }
    12.  
    Scale the healthy portion of your healthbar based on currentHealth.
     
  12. jtsmith1287

    jtsmith1287

    Joined:
    Aug 3, 2014
    Posts:
    787
    No.

    There's 2 things wrong with this and arguably 3.
    1) You're now comparing values every frame, which could be 30 or 200. Why would you do this instead of just checking when it's needed (property or method that explicitly changes health) ?
    2) You're making the comparison twice. You check if health is < 0 and then you check if health is < 0 again. I understand why, but it's not needed.
    Arguably 3) This is one of those one liners that some people hate. Sure it's cute in its brevity, but it's not clear at a glance. Not a huge deal in and of itself but if the purpose of this suggestion is to advertise a one liner it's not worth it and it's bad practice. I believe that the ? operator should be reserved for default values. It's nice in that you can attempt to set a variable and if for some reason it cannot you can determine what it should default to. Again, #3 is purely my opinion so don't let this rant distract from points #1 and #2 which are the more important. I favor longer files over brevity. I want to be able to read my code like a book. (I know some of you are going to want to say "That's perfectly readable, what are you talking about?" ... I'm talking about my opinion. :) )

    TL;DR -- Don't run logic when you don't have to. With this example you're checking your health values even if the health value hasn't changed since the last check.
     
  13. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    7,462
    Yeah, using the property as you outlined is cheaper.

    Just throwing some ideas around. I don't use properties enough so it's never in the front of my thoughts to solve stuff like that.
     
  14. ZendPro

    ZendPro

    Joined:
    Feb 3, 2021
    Posts:
    1
    Hope You Can Do It With This Method

    public class ExampleScript : MonoBehaviour{

    Code (CSharp):
    1. // This keep Health Between 2 Number So It Dont Go Below Or Grow Up More That it (Sorry For The Bad English)
    2.  
    3. public float Health
    4. float Health_Value
    5.  
    6.  
    7. Health_Value = Mathf.Clamp(Health  , Min  Number, Max Number)
    8.  
    9. }
    10.