[Help] Adding a timer inside a State

Hello, first of all thank you for this tutorial – I’ve been having a blast creating my own worlds. However it seems I discovered a problem I cannot overcome as easily.

Inside one of my State for a more dramatic effect I want to display new text lines after some time (variable countdown timer). At first upon looking at tutorials online I set it up with the private float variable which has deltaTime reduced from itself every second and I would set up the amount in the State and call an If statement when the timer goes less than zero. But I quickly realised why it doesn’t work – the timer is setting the amount each frame while it is in the State so it never really ticks any lower than that.

Is there a way to add a timer inside one of the States so that it changes text after some time and not after a button press?

This is an excerpt of the code I had if the explanation was confusing (key thing here is that the timer is variable and that it happens at the end of the game):

private float timer;

void Update () {

	timer -= Time.deltaTime;
	// State engine skipped for this demo

}

void EndingOpen ()
{
	text.text = "8...";
	timer = 2.5f;

	if (timer <= 0) {
		text.text = "8... 4...";
		timer = 2.2f;

		if (timer <= 0) {
			text.text = "8... 4... 2...";
			timer = 1.9f;`
			// etc.
		}
	}
}

TL;DR:

Add a text element to the scene, attach the SCRIPT below to it, and drag the Text object from the hierarchy to the “text” field.

SCRIPT:

using UnityEngine;
using UnityEngine.UI;

public class TestTimer : MonoBehaviour {

	public Text text;
	private string original;

	private int startCount = 10;
	private int endCount = 0;

	// Use this for initialization
	void Start () {
		original = text.text;
		StartCoroutine ("CountDown");
	}

	IEnumerator CountDown(){
    	while (startCount >= endCount){
    		text.text = original + "\n\nT-minus " + startCount--;
    		yield return new WaitForSeconds (1f);
    	}
    	text.text += "\n\n<color=#FF0000FF>LIFT OFF!\nLIFT OFF!\nLIFT OFF!</color>";
    	yield return null;
    }

    // Update is called once per frame
    void Update () {
		// Note the lack of code here.
    }
}

Hi Simonas.

This is probably far beyond the scope of this course, but I’ll show it to you anyway. Hopefully, exposure to this will help you learn it faster than I did (in fact, I’m still learning).

You can use something called a Coroutine - Often used if you need to make adjustments over time (e.g. fade a word’s text colour from white to red - this won’t work easily in the update function), or if you need to perform timing operations (e.g. what you’re doing).

Basically, all the methods in the course so far have run from the beginning to the end (unless skipped by if/else if/else statements inside the method).
A Coroutine is different - it allows the user to execute part of a method, then it can wait for a specified time or for the next frame to do something else.

Here’s an example for you:

using UnityEngine;
using UnityEngine.UI;

public class TestTimer : MonoBehaviour {

	public Text text;
	private string original;

	private int startCount = 10;
	private int endCount = 0;

	// Use this for initialization
	void Start () {
		original = text.text;
		StartCoroutine ("CountDown");
	}

	IEnumerator CountDown(){
    	while (startCount >= endCount){
    		text.text = original + "\n\nT-minus " + startCount--;
    		yield return new WaitForSeconds (1f);
    	}
    	text.text += "\n\n<color=#FF0000FF>LIFT OFF!\nLIFT OFF!\nLIFT OFF!</color>";
    	yield return null;
    }

    // Update is called once per frame
    void Update () {
		// Note the lack of code here.
    }
}

In order to use this script, add a text element to the scene, attach this script to it, and drag the object from the hierarchy to the “text” field.

This will make the example work (PLEASE tell me if it doesn’t work :worried:)

Now to EXPLAIN the example:

using UnityEngine;
using UnityEngine.UI;

public class TestTimer : MonoBehaviour {

    /*  public Text text: declares a "text" component with the name "text" and 
        exposes it in the inspector (A component is something like "Transform", 
        "Rect Transform", ANY script you've added... and the inspector is where you 
        can manipulate these values. In this case, you can drag anything from the 
        hierarchy into the text field as long as it has a "Text" Component.
    */
	public Text text;
    
    /*  private string original: declares a string (a value between 
        double quotes ==> <">) named "original". I'll be using this variable to store
        the original value of whatever is stored in the text object, so it doesn't
        get overwritten.
    */
	private string original;

    /*  private int startCount and private int endCount: I start counting at 10, and 
        go down until I hit 0.
    */
	private int startCount = 10;
	private int endCount = 0;

	// Use this for initialization
	void Start () {

    /*  original = text.text: Stores the value already in the text object 
            ("This is the Timer").
        StartCoroutine ("CountDown"): as the name implies, this starts the Coroutine
        named "CountDown". In Unity, you CAN call Coroutines like any other method,
        or by passing in the function like this:
            StartCoroutine (CountDown);
        HOWEVER, I prefer the method below, because then you can use the 
        StopCoroutine() method to single out and stop the Coroutine you started.
    */
		original = text.text;
		StartCoroutine ("CountDown");
	}

    /*  IEnumerator CountDown(): This is just a method declaration like any other. 
        HOWEVER, the "IEnumerator" signals to Unity that this method is a Coroutine.
    */
	IEnumerator CountDown(){
        /*  while (startCount >= endCount): This is called a WHILE loop. I don't know
            if you know about them, so I'll explain them here. 
            Basically, think of a <while> loop as an <if> statement that keeps 
            repeating until the condition is false.
                The <while> loop will literally run "<while> (condition) is 
                true." The <while> loop can also be "escaped" with a "break;" 
                statement.
            The condition is <startCount is "greater than or equal to" endCount>.
            As long as startCount is AT LEAST larger than -1 (endCount - 1), this 
            code will run.
        */
    	while (startCount >= endCount){

            /*  text.text = original + "\n\nT-minus " + startCount--: This sets the 
                text to "This is the Timer\n\nT-Minus X" (where \n = new line and 
                X is startCount)
                    I don't know if you've seen -- or ++ before, SO:
                    If you have an integer (like startCount), you can automagically
                    increment it by saying "startCount++". This will "read" as 
                    "startCount;
                     startCount = startCount + 1;"
                    Decrementing the number is the same:  "startCount--" = 
                    "startCount;
                     startCount = startCount - 1;"
                    One more gotcha: you can put the ++ and -- at the START 
                    (e.g. --startCount) to automagically increment/decrement a number
                    BEFORE it is read.
            */
    		text.text = original + "\n\nT-minus " + startCount--;

            /*  yield return new WaitForSeconds (1f): This is where your timing magic
                happens. Coroutines MUST have a return value. it can be 
    	    "yield return null", or another IEnumerator. In this case, I opted for:
                    "yield return new WaitForSeconds(1f);"
                This tells the Coroutine to stop executing for the given number of 
                seconds (specified as a float - the "f" after the 1 stops Unity 
                reading it as an integer).
            */
    		yield return new WaitForSeconds (1f);
    	}

        /*  After the loop ends ("This is the Timer\n\nT-Minus 0"), the text will take
            all existing text inside the object and add 2 newlines, then the words 
            "LIFT OFF!" in red, separated by newlines.
        */
    	text.text += "\n\n<color=#FF0000FF>LIFT OFF!\nLIFT OFF!\nLIFT OFF!</color>";
    }

    /*  And Finally, most importantly of all: 
        I DID NOT USE UPDATE.
        ... Not that update is bad. It's just meant for things that should be done 
        every frame.
        One other use for Coroutines is if you have something computationally heavy
        (e.g. maybe you need to sort a list), using the update method or a standard 
        method could cause the game to hang.

        If you wrap the operation in a Coroutine, even operating every 0.1f seconds, 
        it'll (hopefully!) improve execution time.
    */
    // Update is called once per frame
    void Update () {
		// Note the lack of code here.
    }
}

I hope this was accurate.
More importantly, I hope this has helped.

Privacy & Terms