So, I have used a getter before to do UI health, and other UI related stuff and let me tell you, it can get out of hand when lots of systems need to keep getting information…all the time.
So since we learned about delegates, it seems like this would be a perfect time to reinforce that knowledge. I mean, sure, getters and setters is one of those things that can confuse a lot of people (especially when getting into hidden backer fields and whats REALLY going on behind the scenes when you use auto properties) but delegates are also very useful, AND we had a lesson this course on them! So we should be reiterating on things learned previously rather then use them once and forget them, despite their importance.
I changed my health code to a delegate. In fact, I decided to keep most of my player vitals in one script, and use delegates to farm them out. Here is my change to Player script.
public class PlayerStats : MonoBehaviour
{
[SerializeField] private float maxHealthPoints = 100;
private float currentHealthPoints = 100;
private float healthAsPercentage;
public delegate void OnHealthChange(float healthAsPercentage, float currentHealthPoints);
public event OnHealthChange HealthChangeListeners;
void Update()
{
if (currentHealthPoints != maxHealthPoints)
{
healthAsPercentage = currentHealthPoints / (float)maxHealthPoints;
if (HealthChangeListeners != null) HealthChangeListeners(healthAsPercentage, currentHealthPoints);
}
}
public float getCurrentHealthAsPercentance
{
get
{
return currentHealthPoints / (float)maxHealthPoints;
}
}
}
I did keep the getter in there, so I can quickly grab health during prototyping, but in the end most things that need access to health will subscribe. My code only calculates health if there is a change, and then it calls the listeners and sends them health as percentage, and current health in case something needs absolute values later on.
My Player health bar script as follows
[RequireComponent(typeof(RawImage))]
[RequireComponent(typeof(PlayerStats))]
public class PlayerHealthBar : MonoBehaviour
{
RawImage healthBarRawImage;
PlayerStats playerStats;
// Use this for initialization
void Start()
{
playerStats = FindObjectOfType<PlayerStats>();
healthBarRawImage = GetComponent<RawImage>();
playerStats.HealthChangeListeners += OnHealthChange;
}
// Update is called once per frame
public void OnHealthChange(float healthAsPercentage, float currentHealth)
{
float xValue = -(healthAsPercentage / 2f) - 0.5f;
healthBarRawImage.uvRect = new Rect(xValue, 0f, 0.5f, 1f);
}
}
Now, of course this means the health bar needs access to the script, but I don’t see the issue with this over the alternative, which is have the player health bar ask for health updates every single frame.