0
\$\begingroup\$

I am having an issue with a health system I am making for my game. I have been at it for 3 days now and have not made much progress. For reference, I am following this tutorial by code monkey: Code Monkey Health Tutorial. The goal is to have two buttons, one for healing and one to damage the player, and a Text Mesh Pro UI Text to show the health of the player, just for testing purposes. Unlike the tutorial, the buttons and text are just on a canvas, not attached to the player, as I am setting it up as a form of a HUD system. also, forgive the formatting, during troubleshooting, I was more focused on trying to fix my issue and my code became a bit of a mess. Sorry for that.

I have three scripts

    using UnityEngine;


    public class HealthSystem
    {
        private int _health;
        private int _healthMax;

        public HealthSystem(int healthMax)
        {
            this._healthMax = healthMax;
            _health = healthMax;
            Debug.Log("healthSystem Created");
        }
        public int GetHealth()
        {
            return _health;
        }
        public void Damage(int damageAmount)
        {
            _health -= damageAmount;
            if (_health < 0) _health = 0;
        }
        public void Heal(int healAmount)
        {
            _health += healAmount;
            if (_health > _healthMax) _health = _healthMax;
        }
    }

    using UnityEngine;

    public class GameHandler : MonoBehaviour
    {
        private HealthButtons _healthButtons;

        private void Start()
        {
            HealthSystem _healthSystem = new HealthSystem(100);
            Debug.Log("Health = "+_healthSystem.GetHealth());
            _healthButtons.Setup(_healthSystem);
        }
    }
}
using UnityEngine;
using TMPro;


    public class HealthButtons : MonoBehaviour
    {
        [SerializeField]
        private TextMeshProUGUI numberText;
        private static HealthSystem _healthSystem;
        private int intToStringHealth;


        public void DamageButton()
        {
            _healthSystem.Damage(10);
            Debug.Log(_healthSystem.GetHealth());
        }
        public void HealButton()
        {
            _healthSystem.Heal(10);
            Debug.Log(_healthSystem.GetHealth());
        }
        public void Setup(HealthSystem healthSystem)
        {
            if (healthSystem == null) Debug.Log("healthSystem");
            Debug.Log(healthSystem);
            _healthSystem = healthSystem;
            Debug.Log("Setup is running");
        }
        private void Update()
        {
            intToStringHealth = _healthSystem.GetHealth();
            numberText.text = intToStringHealth.ToString();
        }
    }

my issue currently is Im trying to create an instance of HealthSystem in GameHandler, which I am successful in, then pass the information to HealthButtons using _healthButtons.Setup(). This is where I become unsuccessful as I am getting a NullReferenceException error.

from my efforts of troubleshooting currently, I have determined I am not calling the instance correctly, but I do not want to use code monkey's libraries. I have tried making all classes monoclasses and attaching them to game objects and using SerializeFields for all my variables, which does work, but I am not sure that is best practice as I do know linking everything up in the inspector can be a chore if something goes wrong.

This tells me the solution should be fairly simple, I am just not able to articulate the question into google to get the correct answer. It may be a bit overkill just to test a function, but I would like to understand what I am doing wrong and get better. Thank you very much for your help.

\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

You have a variable private HealthButtons _healthButtons; in your GameHandler script, but you are not setting it to anything in code. And you wouldn't be able to set it in the inspector, because it is neither public nor does it have the [SerializeField] property. So you probably get a NullReferenceException, because you are trying to call the method .Setup on a variable that is still uninitialized.

The simplest way to fix this problem and get you going would be to change that variable to public, go to the inspector and assign the HealthButtons to the "Health Buttons" field of your GameHandler.

Although personally I have the philosophy that mechanics should not know about the UI that visualizes them. I find it a much more flexible and robust architecture when the UI is completely self-sufficient and finds the mechanics it visualizes by itself. Or even better, to implement loose coupling through UnityEvents set up in the inspector.

Regarding your doubts about linking up things in the inspector: This is standard practice in Unity. A common beginners mistake is to distrust the Unity editor and trying to set up everything in code. But my experience is that the more you do via inspector and the less you do via code, the higher the flexibility of your components. You also gain a much faster your iteration speed, because anything exposed via inspector can be changed while the game is running. Anything you do via code require that you stop the game, make your code change, and restart it.

\$\endgroup\$
3
  • \$\begingroup\$ Hello and thank you for your help. I serialized the field as you suggested, and this did work. But for my understanding, if I did not want to use a serialized field, how would I go about it then? \$\endgroup\$
    – BobBarker
    Commented Mar 22, 2023 at 0:08
  • \$\begingroup\$ So I attempted to use a GetComponent on HealthButtons since it was on a canvas, but that does not appear to work either. I found this link and tried that as well, but no luck, still NullReferenceException. \$\endgroup\$
    – BobBarker
    Commented Mar 22, 2023 at 0:51
  • \$\begingroup\$ My last post was half the equation, and I posted my updated code if you would like to see what I did. I figured out how to use GetComponent correctly. Thank you again so much for your help! \$\endgroup\$
    – BobBarker
    Commented Mar 22, 2023 at 1:54
0
\$\begingroup\$

I found the answer. Philipp was very helpful in getting me to the fastest answer, but the answer I was looking for was to reference the UI canvas, then use a GetComponent to get to the HealthButtons script. I ended up using a SerializedField and a Debug.Log() and determined that I was never calling HealthButtons correctly. Below is the updated code.

using UnityEngine;

    public class HealthSystem
    {
        private int _health;
        private int _healthMax;

        public HealthSystem(int healthMax)
        {
            this._healthMax = healthMax;
            _health = healthMax;
            Debug.Log("healthSystem Created");
        }
        public int GetHealth()
        {
            return _health;
        }
        public void Damage(int damageAmount)
        {
            _health -= damageAmount;
            if (_health < 0) _health = 0;
        }
        public void Heal(int healAmount)
        {
            _health += healAmount;
            if (_health > _healthMax) _health = _healthMax;
        }
    }
using UnityEngine;

    public class GameHandler : MonoBehaviour
    {
        private GameObject canvas;

        private void Start()
        {
            canvas = GameObject.Find("DebugCanvas");
            HealthButtons _healthButtons = canvas.GetComponent<HealthButtons>();
            Debug.Log(_healthButtons);
            HealthSystem _healthSystem = new HealthSystem(100);
            Debug.Log("Health = "+_healthSystem.GetHealth());
            _healthButtons.Setup(_healthSystem);
        }
    }
using UnityEngine;
using TMPro;

    public class HealthButtons : MonoBehaviour
    {
        [SerializeField]
        private TextMeshProUGUI numberText;
        private HealthSystem _healthSystem;
        private int intToStringHealth;


        public void DamageButton()
        {
            _healthSystem.Damage(10);
            Debug.Log(_healthSystem.GetHealth());
        }
        public void HealButton()
        {
            _healthSystem.Heal(10);
            Debug.Log(_healthSystem.GetHealth());
        }
        public void Setup(HealthSystem healthSystem)
        {
            if (healthSystem == null) Debug.Log("healthSystem");
            Debug.Log(healthSystem);
            _healthSystem = healthSystem;
            Debug.Log("Setup is running");
        }
        private void Update()
        {
            intToStringHealth = _healthSystem.GetHealth();
            numberText.text = intToStringHealth.ToString();
        }
    }

Again, thank you so much philipp for making me realize I was focused on the wrong part of the problem.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .