Search code examples
c#unity-game-engineforkwait

C# how to Make function/Method Wait without blocking program (Unity)


I want to simulate health regeneration in my game in unity, in the function RestoreHealth().

Am I overthinking it by wanting to create a child process, so when I call wait, it won't affect any current running process or thread and the child process will die when the function is done.

public void RestoreHealth() {
    if (health >= MaxHealth) return; // health and MaxHealth are Class variables

    if (health % 10 != 0) {     // if health is not dividable by 10
        int temp = health % 10; // then we round it to the closest 
                                //tenth
        temp = 10 - temp;
        health += temp;
    }

    int i = health;

    for (; i < MaxHealth; i += 10) {  // where the health grows till 100
        health += 10;
        // sleep(1000);  // make function wait for '1 second' to iterate again

        Debug.Log("Health: " + health);
    }
}

How do I create a child process in C# or unity in this case and cause it to wait?

Is there an equivalent to Fork(); like in C?

Also, this function is called when the player initially receives damage.

Solution:

note: I changed Health to Armour

public IEnumerator RestoreArmour() {
        while (_Armour < _MaxArmour) {
            _Armour++;
            Debug.Log("Health: " + _Armour);
            yield return new WaitForSeconds(ArmourRegenRate); // ArmourRegenRate is a 
                                                             // float for the seconds
        }
    }

and use this to initiate the coroutine

 void Start(){

    StartCoroutine(player.RestoreArmour());
}

Solution

  • Basic Coroutine concept

    In Unity you work with Coroutines to achieve this asychronous "threaded" behaviour

    IEnumerator RestoreHealth() {
       while (health != MaxHealth) {
           health++;
           yield return new WaitForEndOfFrame();
       }
    }
    

    and then invoke it with

    StartCoroutine(RestoreHealth());
    

    Restarting a Coroutine

    In order to stop an existing Coroutine from running and start a new one, this is how you would achieve that:

    private Coroutine _myCoroutine = null;
    
    void SomeMethod()
    {
        if (_myCoroutine != null)
            StopCoroutine(_myCoroutine);
    
        _myCoroutine = StartCoroutine(SomeOtherMethod());
    }
    
    

    Pausing armor restoration for X seconds when player has taken damage

    A common functionality is to have something restore armor when player hasn't taken damage for X seconds:

    private bool _shouldRestoreArmour = true;
    private Coroutine _pauseArmorCoroutine = null;
    
    void Update()
    {
        if (_shouldRestoreArmour)
            Armor += ArmorRegenerationPerSecond * Time.deltaTime;
    }
    
    void PlayerTakeDamage() 
    {
        if (_pauseArmorCoroutine != null) 
            StopCoroutine(_pauseArmorCoroutine);
    
        _pauseArmorCoroutine = StartCoroutine(PauseRestoreArmor());
    
        // Take damage code
    }
    
    IEnumerator PauseRestoreArmor()
    {
        _shouldRestoreArmor = false;
        yield return new WaitForSeconds(RESTORE_ARMOR_DELAY_TIME);
        _shouldRestoreArmor = true;
    }
    

    Here the player will regenerate armor at all times, except for X seconds after the player has taken damage. If the player takes damage multiple times we will simply abort the previous coroutine and start a new one, so that it will be a fresh X seconds from the last hit.