Search Unity

Health System

Discussion in 'Scripting' started by Robster95, Aug 21, 2020.

Thread Status:
Not open for further replies.
  1. Robster95

    Robster95

    Joined:
    Jul 3, 2019
    Posts:
    154
    So i'm trying to implement health onto a player and then use the same code as well as an enemys health. I am trying to figure out the best and most effective way to create this health system I can easily do
    Code (csharp):
    1.  
    2.     public float maxHealth;
    3.     public float currentHealth;
    4.  
    5.     public float damage;
    6.     public float healing;
    7.  
    8.  
    9.     private void Start()
    10.     {
    11.         currentHealth = maxHealth;
    12.     }
    13.  
    14.  
    15.     private void Update()
    16.     {
    17.         if (Input.GetKeyDown(KeyCode.Q))
    18.         {
    19.             ApplyHealing();
    20.         }
    21.  
    22.         if(Input.GetKeyDown(KeyCode.E))
    23.         {
    24.             DamageHealth();
    25.         }
    26.     }
    27.  
    28.     void ApplyHealing()
    29.     {
    30.         if (currentHealth < maxHealth)
    31.         {
    32.             currentHealth = currentHealth + healing;
    33.         }
    34.     }
    35.  
    36.     public void DamageHealth()
    37.     {
    38.         if (currentHealth > 0)
    39.         {
    40.             currentHealth = currentHealth - damage;
    41.         }
    42.     }
    43. }
    44.  
    I have the two methods apply and damage just to test right now but plan to implement bullets calling for this code and applying damage.

    Does anyone have any suggestions on the best way to set, damage and heal the player while also allowing lets say bullets and melee attacks to call for the damage method as well?

    Thank you!
     
  2. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,908
    A few suggestions:

    First, consolidate your damage and healing methods into one "ChangeHealth" method that accepts an amount parameter. This provides a single place where you have health-changing code, and allows things like bullets etc. to decide on their own how much damage to deal or how much health to add.

    Second, your bounds checking code is kinda being done backwards. You shouldn't check if the player is at max or minimum health BEFORE changing the health. You should check after. For example if I'm at 9 health out of max 10, then I add 5 health, that will put me at 14. Instead, clamp the value after it changes.

    Third, create an event that other scripts can listen to so they can be notified when the health changes. For example if you have a health bar UI element it can listening for health changes and redraw only when the health changes.

    Fourth, make currentHealth a private variable. You don't want other scripts changing its value willy nilly. you can create a public property to allow other scripts read access to the value though.

    Code (CSharp):
    1. // Define the health changed event and handler delegate.
    2. public delegate void HealthChangedHandler(object source, float oldHealth, float newHealth);
    3. public event HealthChangedHandler OnHealthChanged;
    4.  
    5. // Show in inspector
    6. [SerializeField]
    7. float currentHealth;
    8. // Allow other scripts a readonly property to access current health
    9. public float CurrentHealth => currentHealth;
    10.  
    11. // test values
    12. [SerializeField]
    13. float testHealAmount = 5f;
    14. [SerializeField]
    15. float testDamageAmount = -5f;
    16.  
    17. public void ChangeHealth(float amount) {
    18.    float oldHealth = currentHealth;
    19.    currentHealth += amount;
    20.    currentHealth = Mathf.Clamp(currentHealth, 0, maxHealth);
    21.  
    22.    // Fire off health change event.
    23.    OnHealthChanged?.Invoke(this, oldHealth, currentHealth);
    24. }
    25.  
    26. // Test code
    27. void Update() {
    28.   if (Input.GetKeyDown(KeyCode.Q)) {
    29.     ChangeHealth(testHealAmount);
    30.   }
    31.   if (Input.GetKeyDown(KeyCode.E)) {
    32.     ChangeHealth(testDamageAmount);
    33.   }
    34. }
    35.  
     
    Last edited: Aug 21, 2020
    JoselaiaroW and Robster95 like this.
  3. Robster95

    Robster95

    Joined:
    Jul 3, 2019
    Posts:
    154
    ok thank you i'll look into your suggestions! I have a few questions though. I see you have methods with parameters and pointers. I get confused when working with those so how would be the best way to learn them? Also how will I be able to make bullets and stuff carry over damage to the character? I'm fairly new to coding and using unity so i'm trying to learn all fields as quickly as I can!
     
  4. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,908
    I haven't used any pointers in my script at all, could you point out what you're referring to?

    Methods with parameters are simple, you're already using one few when you call Input.GetKeyDown(...) for example. You just put the parameter values between the parentheses, separated by commas if there is more than one, when you call the method.

    A common way to implement bullets is to use collision to detect when they've hit something. Here's an example of what a bullet script might look like with a rigidbody for collision. I'm going to make a few assumptions here, like that your game is 2d and that your health script is called "Health" (you didn't share the name of the script above. Maybe it's called "Player"?)

    Code (CSharp):
    1. public class Bullet : MonoBehaviour {
    2.   [SerializeField]
    3.   float damage;
    4.  
    5.   void OnCollisionEnter2D(Collision2D collision) {
    6.     Health healthComponent = collision.gameObject.GetComponent<Health>();
    7.  
    8.     // Only continue if the thing we hit has a Health component
    9.     if (healthComponent) {
    10.        // Reduce their health by our damage value!
    11.        healthComponent.ChangeHealth(-damage);
    12.     }
    13.   }
    14.  
    15. }
     
  5. Robster95

    Robster95

    Joined:
    Jul 3, 2019
    Posts:
    154
    I was refering to the => I thought that was considered a pointer. and for whats in the parenthesis that I get confused with is your code changehealth that has the (float amount) inside of it. because what I get confused about is wouldnt you need to call for the changehealth code in the update and if you do wouldnt you need to also add something into the parenthesis for the code to work?
     
  6. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,908
    It's just a thing that says "When someone reads this property, give them the value of this other variable"
    Code (CSharp):
    1. public float CurrentHealth => currentHealth;
    So when some code outside this class reads "CurrentHealth" they will get the value of "currentHealth". C# as a language generally doesn't use pointers.

    You don't need to call it in Update. You can call it from any method. Look at my example using OnCollisionEnter2D above. And yes, you need to put something in the parentheses. As you can see I'm putting the bullet's damage value in the parentheses:
    Code (CSharp):
    1. healthComponent.ChangeHealth(-damage);
     
    Robster95 likes this.
  7. Robster95

    Robster95

    Joined:
    Jul 3, 2019
    Posts:
    154
    Oh! ok so since it gets called in the oncollision thats where it applies the damage! I got confused about the update part because I know lets say you make a method like
    Code (csharp):
    1.  
    2. void ShowDebug()
    3. {
    4.    debug.log("stuff");
    5. }
    6.  
    If you were to do something like that the code wouldnt be called at any point unless it has another method calling for it right? thats what I was thinking when I said the update method.

    Now I guess my last two questions are I see you used a ? in one of the variables, what is the purpose of that.

    and the last question would be.

    Would this code be more ok to use in the game? like would I want to make a code that has lets say the characters health and then another one that calls for the methods or should I have them all in one script?
     
  8. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,493
    Robster95 likes this.
  9. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,908
    Yes, you're right Unity will not call methods with random names. There are a few special ones like you noted that Unity will call for you at certain times.

    Update and OnCollisionEnter2D are two of those methods. If you look at this page, under the section called "Messages" there is a list of all the special method names that Unity will call for you automatically: https://docs.unity3d.com/ScriptReference/MonoBehaviour.html
     
    Robster95 likes this.
  10. Robster95

    Robster95

    Joined:
    Jul 3, 2019
    Posts:
    154
    ok thank you! i'll look into that. So back to the code though. would this be the best way to do this code or should I have the creation of health and the methods that call for the change of health be in different scripts or will having the codes interact with each other just be an extra step thats not necessary and more confusion?
     
  11. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,908
    The bet way is the way I've advocated above. Keep the health data itself and the methods for modifying it on a single script. Then,. in other scripts, like the Bullet script I shared, you can interact with the health script via public methods like ChangeHealth and the public CurrentHealth property.
     
    Robster95 likes this.
  12. Robster95

    Robster95

    Joined:
    Jul 3, 2019
    Posts:
    154
    Awesome thank you! i'll try that as soon as I can! kind of wanted to take a break from coding today but will get it soon!!!
     
  13. Robster95

    Robster95

    Joined:
    Jul 3, 2019
    Posts:
    154
    Ok i imported the code into unity and i'm trying to figure out how it works and i'm actually pretty confused right now. I'm looking and i see that theres the clamp that calls for maxhealth that isnt in your script. i'm guessing you took the maxHealth I had in mine. also I see the oldhealth is set to the current health but I dont see a point where the current health gets set to the max health like if the game just starts.

    i'm trying to learn about delegates, events, the ? you put in the OnHealthChanged?.Invoke(method).

    If i'm looking at this wrong please let me know but i'm just very confused at this point because I've done this before and it worked fine in a small project I worked on (just basically teaching myself unity and C# based off of youtube videos and tutorials I found) and although I made it into a small project where I was fighting blocks that would damage me I never had to use these keywords or methods. Sorry if my confusion gets annoying.
     
Thread Status:
Not open for further replies.