Making a timer using WaitForSecondsRealtime without keyword 'new'?

Hi guys, I did a simple timer using WaitForSecondsRealtime within a Coroutine and it worked:

using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class Timer : MonoBehaviour
{
    Text txt;
    int startTime = 10;

    void Awake()
    {
        txt = GetComponent<Text>();
    }
    void Start()
    {
        StartCoroutine(CoroutineTimer());
    }
    IEnumerator CoroutineTimer()
    {
        while (startTime >= 0)
        {
            txt.text = startTime.ToString();
            startTime -= 1;
            yield return new WaitForSecondsRealtime(1);
        }
    }
}

However, if I want to reduce garbage by removing the keywords ‘new’, and change the IEnumerator to below, it magically doesn’t work anymore:

IEnumerator CoroutineTimer()
{
    WaitForSecondsRealtime countTime = new WaitForSecondsRealtime(1);
    while (startTime >= 0)
    {
        txt.text = startTime.ToString();
        startTime -= 1;
        yield return countTime;
    }
}

It’s not very magical. A WaitForSeconds object is a simple timer. The time measured does not reset without any specific reason to - beacause that would be pretty magical. As a result, the object will never wait again.

You can, however, use a custom WaitForSeconds object that has a reset function. I’ve written one for you:

using UnityEngine;

public class WaitTimer : CustomYieldInstruction
{
    private float timeLeft;
    private float lastTime;

    public override bool keepWaiting
    {
        get
        {
            timeLeft -= Time.deltaTime;
            return timeLeft > 0;
        }
    }

    public WaitTimer(float time)
    {
        Reset(time);
    }

    public void Reset(float time = 0)
    {
        if(time == 0)
        {
            timeLeft = lastTime;
        }
        else
        {
            lastTime = timeLeft = time;
        }
    }
}

You need to reset the timer before using it again:

var timer = new WaitTimer(3);
yield return timer;
Debug.Log("3");

timer.Reset();
yield return timer;
Debug.Log("6");

timer.Reset(4);
yield return timer;
Debug.Log("10");

If you like inplace methods, you could add a function in addition to Reset that returns the timer:

public WaitTimer Again(float time = 0)
{
    Reset(time);
    return this;
}

and then use it like this:

var timer = new WaitTimer(3);
yield return timer;
Debug.Log("3");
yield return timer.Again();
Debug.Log("6");