How to subtract coins when hurt

How to subtract coins when hurt

Coin script:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SC_2DCoin: MonoBehaviour
{
    //Keep track of total picked coins (Since the value is static, it can be accessed at "SC_2DCoin.totalCoins" from any script)
    public static int totalCoins = 0;

    void Awake()
    {
        //Make Collider2D as trigger 
        GetComponent<Collider2D>().isTrigger = true;
    }

    void OnTriggerEnter2D(Collider2D c2d)
    {
        //Destroy the coin if Object tagged Player comes in contact with it
        if (c2d.CompareTag("Player"))
        {
            //Add coin to counter
            totalCoins++;
            //Test: Print total number of coins
            Debug.Log("You currently have " + SC_2DCoin.totalCoins + " Coins.");
            //Destroy coin
            Destroy(gameObject);
        }
    }
    
}

Coin Counter

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class SC_CoinCounter : MonoBehaviour
{
    Text counterText;

    // Start is called before the first frame update
    void Start()
    {
        counterText = GetComponent<Text>();
    }

    // Update is called once per frame
    void Update()
    {
        //Set the current number of coins to display
        if (counterText.text != SC_2DCoin.totalCoins.ToString())
        {
            counterText.text = SC_2DCoin.totalCoins.ToString();
        }
    }
}

Health Script

using UnityEngine;
using System.Collections;
using UnityEngine.SceneManagement;

public class Health : MonoBehaviour
{
    [Header("Health")]
    [SerializeField] private float startingHealth;
    public float currentHealth { get; private set; }
    private Animator anim;
    private bool dead;
    public static int totalCoins = 0;
    [Header("iFrames")]
    [SerializeField] private float iFramesDuration;
    [SerializeField] private int numberOfFlashes;
    private SpriteRenderer spriteRend;

    [Header("Components")]
    [SerializeField] private Behaviour[] components;
    private bool invulnerable;
    public GameObject ivsGameobject;
 


    private void Awake()
    {
        
        currentHealth = startingHealth;
        anim = GetComponent<Animator>();
        spriteRend = GetComponent<SpriteRenderer>();
    }
    public void TakeDamage(float _damage)
    {
        if (invulnerable) return;
        currentHealth = Mathf.Clamp(currentHealth - _damage, 0, startingHealth);

        if (currentHealth > 0)
        {
            anim.SetTrigger("hurt");
            StartCoroutine(Invunerability());

        }
        else
        {
            if (!dead)
            {
                

                       
                //Deactivate all attached component classes
                foreach (Behaviour component in components)
                    component.enabled = false;

                anim.SetBool("grounded", true);
                anim.SetTrigger("die");
               
                if (tag == "Player")
                {
                    anim.SetBool("grounded", true);
                    anim.SetTrigger("die");
                    
                    GetComponent<PlayerMovement>().enabled = false;
                }
                if (tag == "boss")
                {
                    anim.SetBool("grounded", true);
                    anim.SetTrigger("die");
                    dead = true;

                    Destroy(GameObject.FindWithTag("tag"));

                }

                dead = true;

            }
        }
    }
    public void AddHealth(float _value)
    {
        currentHealth = Mathf.Clamp(currentHealth + _value, 0, startingHealth);
    }
    private IEnumerator Invunerability()
    {
        invulnerable = true;
        Physics2D.IgnoreLayerCollision(10, 11, true);
        for (int i = 0; i < numberOfFlashes; i++)
        {
            spriteRend.color = new Color(1, 0, 0, 0.5f);
            yield return new WaitForSeconds(iFramesDuration / (numberOfFlashes * 2));
            spriteRend.color = Color.white;
            yield return new WaitForSeconds(iFramesDuration / (numberOfFlashes * 2));
        }
        Physics2D.IgnoreLayerCollision(10, 11, false);
        invulnerable = false;
    }
    private void Deactivate()
    {
        gameObject.SetActive(false);
    }

    //Respawn
    public void Respawn()
    {
        dead = false;
        AddHealth(startingHealth);
        anim.ResetTrigger("die");
        anim.Play("Idle");
        StartCoroutine(Invunerability());
        GetComponent<PlayerMovement>().enabled = true;
      
        //Activate all attached component classes
        foreach (Behaviour component in components)
            component.enabled = true;
    }

Greetings @azharris005

This questions is really of the type “How do I get one GameObject/Component to communicate with another?”. The way that is usually recommended is to use Events. You can either use Unity Events or C# Events but I prefer the latter. There are plenty of tutorials out there but the basic principles are:

  • The calling routine (Health) defines an event (eg “TakeHit”)
  • When the condition occurs (eg takes a hit) the event is invoked
  • Any code interested in the event subscribes to (i.e. listens out for) the event
  • The listening code must have a method, linked to in the subscription, to react to the event
  • It’s good practice to both subscribe (OnEnable) and unsubscribe (OnDisable) to events.

Here’s some sample code that you could modify:

//Put this in your Health Class

using UnityEngine;
using System;

public class Health : MonoBehaviour
{
    public static event Action onHit;

    if (takenHit)
    {
        onHit?.Invoke();     // Only invoke if not null, ie someone has subscribed
    }
}

Then in your Coins class:

using UnityEngine;

public class Coins : MonoBehaviour
{
    private void OnEnable()
    {
        Health.onHit += Reduce;     // Subscribe to the event
    }

    private void OnDisable()
    {
        Health.onHit -= Reduce;     // Unsubscribe in case this instance is ever destroyed
    }

    void Reduce()
    {
        // Reduce the coins here;
    }
}

You’re obviously an experienced coder so, if you prefer Unity Events, they are basically the same as when you invoke events for UI constructs (eg buttons/sliders). The advantage of Events is that the invoking code doesn’t need to know who has subscribed, isolating it from other parts of the code. So, you might invoke the event and audio, animation, coins and other listeners might all subscribe and respond. Just add more Methods as you add more function and you never need to go back and change your Health code to do so.

By the way, you could also implement events so that the Coin Counter is only ever called when the number of coins changes. At the moment, you are updating the text field every frame. Not a big overhead but certainly not best practice. Events would work well here as well…