Help with Coroutine, beginner

Hey!

I followed a tutorial so that you gain health entering a boxcollider on trigger, which is awesome, it works.
But as soon as I enter the box, I gain health the moment I enter the collider. I want it to take a couple of seconds or so before I start gaining health. My guess is, I need some kind of coroutine or something?

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

public class HeartScript : MonoBehaviour
{

    private void Start()
    {
    
    }
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (gameObject.tag == "Healzone")
        {
            CinemachineShake.Instance.ShakeCamera(0.5f, 50f);
            GameControlScript.health += 1;
        }
    
    }

    private void OnTriggerExit2D(Collider2D collision)
    {
        if (gameObject.tag == "Healzone")
        {
            CinemachineShake.Instance.ShakeCamera(0f, 0f);
        }
    
    }

 

}

That is my code right now, how would I write the code if I want a timer when I enter the area to start gaining health like 2 seconds after my player has triggered the BoxCollider, and not get all health at once… But to gain one health each 2-3 seconds?

And this is another script that displays the images of my hearts, and reset the level when all hearts are gone

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

public class GameControlScript : MonoBehaviour
{
    public GameObject heart1, heart2, heart3, gameOver;
    public static int health;
    // Start is called before the first frame update
    void Start()
    {
        health = 3;
        heart1.gameObject.SetActive(true);
        heart2.gameObject.SetActive(true);
        heart3.gameObject.SetActive(true);
        gameOver.gameObject.SetActive(false);
    }

    // Update is called once per frame
    void Update()
    {
        if (health > 3)
            health = 3;

        switch (health)
        {
            case 3:
                heart1.gameObject.SetActive(true);
                heart2.gameObject.SetActive(true);
                heart3.gameObject.SetActive(true);
                break;
            case 2:
                heart1.gameObject.SetActive(true);
                heart2.gameObject.SetActive(true);
                heart3.gameObject.SetActive(false);
                break;
            case 1:
                heart1.gameObject.SetActive(true);
                heart2.gameObject.SetActive(false);
                heart3.gameObject.SetActive(false);
                break;
            case 0:
                heart1.gameObject.SetActive(false);
                heart2.gameObject.SetActive(false);
                heart3.gameObject.SetActive(false);
                FindObjectOfType<LevelManager>().Restart();



                break;

        }
    }
}

I’m no Unity expert, but I reckon you can do just that with an InvokeRepeating:

InvokeRepeating(string methodName, float time, float repeatRate);

Could look something like this:

if (gameObject.tag == "Healzone")
        {
            CinemachineShake.Instance.ShakeCamera(0.5f, 50f);
            InvokeRepeating("Heal", TIME_UNTIL_START, SPEED_OF_HEALING);
        }




void Heal()
{
//Do the magical healing stuff here
}

Just set the variables in InvokeRepeating to what you need. InvokeRepeating(“Heal”, 2f, 1f); would start healing at 2 seconds and then heal every second. Whenever you want to stop the healing you can use CancelInvoke(“Heal”);

I think I understand how it works, but I am pretty new to this with unity and coding, I wrote this so far:

 private void OnTriggerEnter2D(Collider2D collision)
    {
        if (gameObject.tag == "Healzone")
        {
            CinemachineShake.Instance.ShakeCamera(0.5f, 50f);
            InvokeRepeating("Heal", 2f, 1f);
        }
     
    }

private void OnTriggerExit2D(Collider2D collision)
    {
        if (gameObject.tag == "Healzone")
        {
            CinemachineShake.Instance.ShakeCamera(0f, 0f);
            CancelInvoke("Heal");
        }
     
    }

    void Heal()
    {
        GameControlScript.health += 1;
    }

How would I write the string, methodnames etc, and where? Thats the part I don’t understand really

EDIT: The code kinda works, It takes 2 seconds and then it starts healing, however I get fully healed once I’ve waited 2 seconds, the only issue left is making it give 1 health every second or so :slight_smile:
https://gyazo.com/ff43a410b6815ce31565c23616abe2e1

The “wizard images” represents my health just for reference.

Your code looks good to me, so I suspect your issue would be from the other code you have. Let me know what you have there

Y-Hello @Lajlaj ,

Indeed you can make use of Coroutines like:

// we declare our coroutine var to keep a reference
    private IEnumerator coroutine;

You can call the coroutine in your OnTriggerEnter2D

private void OnTriggerEnter2D(Collider2D collision)
    {
        if (gameObject.tag == "Healzone")
        {
            CinemachineShake.Instance.ShakeCamera(0.5f, 50f);
          
            coroutine = HealCO(); //we assign the coroutine to the var

            StartCoroutine(coroutine); //Start the HealCO
        }

    }
IEnumerator void HealCO()
{

   int healToAdd = 4;
   int healAmount = 0;

   //you can do stuff immediately

   yield return new WaitForSeconds(2f) //Here the script is waiting 2sec

   //You can do stuff after 2 sec

   //We cycle until we reach the healAmount we set
  while (healAmount < healToAdd)
  {
      healAmount++;

      //Heal the player by 1 each cycle

      yield return new WaitForSeconds(1f); //wait 1 sec and then repeat the cycle
  }
 
}

Then of course remember to cancel the coroutine OnTriggerExit2D

private void OnTriggerExit2D(Collider2D collision)
    {
        if (gameObject.tag == "Healzone")
        {
            CinemachineShake.Instance.ShakeCamera(0f, 0f);
          
            StopCoroutine(coroutine); //we stop coroutine
        }
   
    }

hope this helps

[Edit] Another alternative is to make use of the internal method OnTriggerStay2D() which tick every frame the Collider is inside the trigger, and set up a timer with a counter there. Coroutines and TriggerStay are both valid, you can use whichever you like best

Forgot to come back here, issue solved. The InvokeRepeat works wonders, and all I had to do was to move the BoxCollider that would trigger my healing away from the ground layer, then I got healed once every 2 seconds. Somehow it created a issue when it was touching the ground layer :slight_smile: Thanks alot for your help!

Thanks! I managed to solve the issue I had. However, I will take your advice for the future since I need to experiment with different methods in unity :slight_smile:

1 Like

Awesome! I love InvokeRepeating, a simple yet powerful tool

Idk if the issue is related to this, but maybe is because you are checking the wrong gameObject tag in you triggers.

To check on the Collider obj you should do something like:

if(collision.gameObject.tag == "yourTag")

The trigger is giving you back the object “Collider2D” with which he collided.

For example if the trigger is on the healingArea gameObject you should check if the collider has the player tag, or viceversa if the trigger is on the player.