How to make the script wait/sleep in a simple way in unity
Asked Answered
M

11

102

How can I put a sleep function between the TextUI.text = ...., to wait 3 seconds between each phrase?

public Text GuessUI;
public Text TextUI;

[...truncated...]

TextUI.text = "Welcome to Number Wizard!";
TextUI.text = ("The highest number you can pick is " + max);
TextUI.text = ("The lowest number you can pick is " + min);

I already tried various things but none have worked, such as this:

TextUI.text = "Welcome to Number Wizard!";
yield WaitForSeconds (3);
TextUI.text = ("The highest number you can pick is " + max);
yield WaitForSeconds (3);
TextUI.text = ("The lowest number you can pick is " + min);

In bash, it would be:

echo "Welcome to Number Wizard!"
sleep 3
echo "The highest number you can pick is 1000"
sleep 3
.....

but I can't figure out how to do this in Unity with C#

Mirandamire answered 5/5, 2015 at 14:56 Comment(5)
What exactly "didn't worked" means?Colorific
yield WaitForSeconds (3); don't workedMirandamire
What exactly "don't worked" means?Colorific
whats wrong with Thread.Sleep(3000)Pressure
i think they mean that it did not slow downAcrimony
N
181

There are many ways to wait in Unity. They are really simple but I think it's worth covering most ways to do it:

1.With a coroutine and WaitForSeconds.

This is by far the simplest way. Put all the code that you need to wait for some time in a coroutine function then you can wait with WaitForSeconds. Note that in coroutine function, you call the function with StartCoroutine(yourFunction).

Example below will rotate 90 deg, wait for 4 seconds, rotate 40 deg and wait for 2 seconds, and then finally rotate rotate 20 deg.

void Start()
{
    StartCoroutine(waiter());
}

IEnumerator waiter()
{
    //Rotate 90 deg
    transform.Rotate(new Vector3(90, 0, 0), Space.World);

    //Wait for 4 seconds
    yield return new WaitForSeconds(4);

    //Rotate 40 deg
    transform.Rotate(new Vector3(40, 0, 0), Space.World);

    //Wait for 2 seconds
    yield return new WaitForSeconds(2);

    //Rotate 20 deg
    transform.Rotate(new Vector3(20, 0, 0), Space.World);
}

2.With a coroutine and WaitForSecondsRealtime.

The only difference between WaitForSeconds and WaitForSecondsRealtime is that WaitForSecondsRealtime is using unscaled time to wait which means that when pausing a game with Time.timeScale, the WaitForSecondsRealtime function would not be affected but WaitForSeconds would.

void Start()
{
    StartCoroutine(waiter());
}

IEnumerator waiter()
{
    //Rotate 90 deg
    transform.Rotate(new Vector3(90, 0, 0), Space.World);

    //Wait for 4 seconds
    yield return new WaitForSecondsRealtime(4);

    //Rotate 40 deg
    transform.Rotate(new Vector3(40, 0, 0), Space.World);

    //Wait for 2 seconds
    yield return new WaitForSecondsRealtime(2);

    //Rotate 20 deg
    transform.Rotate(new Vector3(20, 0, 0), Space.World);
}

Wait and still be able to see how long you have waited:

3.With a coroutine and incrementing a variable every frame with Time.deltaTime.

A good example of this is when you need the timer to display on the screen how much time it has waited. Basically like a timer.

It's also good when you want to interrupt the wait/sleep with a boolean variable when it is true. This is where yield break; can be used.

bool quit = false;

void Start()
{
    StartCoroutine(waiter());
}

IEnumerator waiter()
{
    float counter = 0;
    //Rotate 90 deg
    transform.Rotate(new Vector3(90, 0, 0), Space.World);

    //Wait for 4 seconds
    float waitTime = 4;
    while (counter < waitTime)
    {
        //Increment Timer until counter >= waitTime
        counter += Time.deltaTime;
        Debug.Log("We have waited for: " + counter + " seconds");
        //Wait for a frame so that Unity doesn't freeze
        //Check if we want to quit this function
        if (quit)
        {
            //Quit function
            yield break;
        }
        yield return null;
    }

    //Rotate 40 deg
    transform.Rotate(new Vector3(40, 0, 0), Space.World);

    //Wait for 2 seconds
    waitTime = 2;
    //Reset counter
    counter = 0;
    while (counter < waitTime)
    {
        //Increment Timer until counter >= waitTime
        counter += Time.deltaTime;
        Debug.Log("We have waited for: " + counter + " seconds");
        //Check if we want to quit this function
        if (quit)
        {
            //Quit function
            yield break;
        }
        //Wait for a frame so that Unity doesn't freeze
        yield return null;
    }

    //Rotate 20 deg
    transform.Rotate(new Vector3(20, 0, 0), Space.World);
}

You can still simplify this by moving the while loop into another coroutine function and yielding it and also still be able to see it counting and even interrupt the counter.

bool quit = false;

void Start()
{
    StartCoroutine(waiter());
}

IEnumerator waiter()
{
    //Rotate 90 deg
    transform.Rotate(new Vector3(90, 0, 0), Space.World);

    //Wait for 4 seconds
    float waitTime = 4;
    yield return wait(waitTime);

    //Rotate 40 deg
    transform.Rotate(new Vector3(40, 0, 0), Space.World);

    //Wait for 2 seconds
    waitTime = 2;
    yield return wait(waitTime);

    //Rotate 20 deg
    transform.Rotate(new Vector3(20, 0, 0), Space.World);
}

IEnumerator wait(float waitTime)
{
    float counter = 0;

    while (counter < waitTime)
    {
        //Increment Timer until counter >= waitTime
        counter += Time.deltaTime;
        Debug.Log("We have waited for: " + counter + " seconds");
        if (quit)
        {
            //Quit function
            yield break;
        }
        //Wait for a frame so that Unity doesn't freeze
        yield return null;
    }
}

Wait/Sleep until variable changes or equals to another value:

4.With a coroutine and the WaitUntil function:

Wait until a condition becomes true. An example is a function that waits for player's score to be 100 then loads the next level.

float playerScore = 0;
int nextScene = 0;

void Start()
{
    StartCoroutine(sceneLoader());
}

IEnumerator sceneLoader()
{
    Debug.Log("Waiting for Player score to be >=100 ");
    yield return new WaitUntil(() => playerScore >= 10);
    Debug.Log("Player score is >=100. Loading next Level");

    //Increment and Load next scene
    nextScene++;
    SceneManager.LoadScene(nextScene);
}

5.With a coroutine and the WaitWhile function.

Wait while a condition is true. An example is when you want to exit app when the escape key is pressed.

void Start()
{
    StartCoroutine(inputWaiter());
}

IEnumerator inputWaiter()
{
    Debug.Log("Waiting for the Exit button to be pressed");
    yield return new WaitWhile(() => !Input.GetKeyDown(KeyCode.Escape));
    Debug.Log("Exit button has been pressed. Leaving Application");

    //Exit program
    Quit();
}

void Quit()
{
    #if UNITY_EDITOR
    UnityEditor.EditorApplication.isPlaying = false;
    #else
    Application.Quit();
    #endif
}

6.With the Invoke function:

You can call tell Unity to call function in the future. When you call the Invoke function, you can pass in the time to wait before calling that function to its second parameter. The example below will call the feedDog() function after 5 seconds the Invoke is called.

void Start()
{
    Invoke("feedDog", 5);
    Debug.Log("Will feed dog after 5 seconds");
}

void feedDog()
{
    Debug.Log("Now feeding Dog");
}

7.With the Update() function and Time.deltaTime.

It's just like #3 except that it does not use coroutine. It uses the Update function.

The problem with this is that it requires so many variables so that it won't run every time but just once when the timer is over after the wait.

float timer = 0;
bool timerReached = false;

void Update()
{
    if (!timerReached)
        timer += Time.deltaTime;

    if (!timerReached && timer > 5)
    {
        Debug.Log("Done waiting");
        feedDog();

        //Set to false so that We don't run this again
        timerReached = true;
    }
}

void feedDog()
{
    Debug.Log("Now feeding Dog");
}

There are still other ways to wait in Unity but you should definitely know the ones mentioned above as that makes it easier to make games in Unity. When to use each one depends on the circumstances.

For your particular issue, this is the solution:

IEnumerator showTextFuntion()
{
    TextUI.text = "Welcome to Number Wizard!";
    yield return new WaitForSeconds(3f);
    TextUI.text = ("The highest number you can pick is " + max);
    yield return new WaitForSeconds(3f);
    TextUI.text = ("The lowest number you can pick is " + min);
}

And to call/start the coroutine function from your start or Update function, you call it with

StartCoroutine (showTextFuntion());
Nogging answered 5/5, 2015 at 23:55 Comment(4)
All of these (except invoke) are coroutine. This is really just one method.Dicast
@TylerSigi No. #7 uses the Update function. Most of the rest of them use coroutine but the way they wait is different. They were not added just for the fun it of it. They are needed in different scenario otherwise you will not get the behavior you want or you will end up re-inventing the wheel while built-in functions made by Unity are there to handle some stuff.Nogging
A special note about Invoke (and InvokeRepeating): Afaik it works - different then the other methods - also on an inactive GameObject or disabled Component which makes it extremely powerful for certain cases!Acyl
Can it be achieved without a courotine?Metternich
C
14

You were correct to use WaitForSeconds. But I suspect that you tried using it without coroutines. That's how it should work:

public void SomeMethod()
{
    StartCoroutine(SomeCoroutine());
}

private IEnumerator SomeCoroutine()
{
    TextUI.text = "Welcome to Number Wizard!";
    yield return new WaitForSeconds (3);
    TextUI.text = ("The highest number you can pick is " + max);
    yield return new WaitForSeconds (3);
    TextUI.text = ("The lowest number you can pick is " + min);
}
Colorific answered 5/5, 2015 at 15:50 Comment(2)
i don't understand... should i replace SomeCoroutine with what?Mirandamire
You have to put the "WaitForSeconds" as the yeild of an iEnumerator for this to work. Try reading up on Unity Coroutines.Hyper
S
8

With .Net 4.x you can use Task-based Asynchronous Pattern (TAP) to achieve this:

// .NET 4.x async-await
using UnityEngine;
using System.Threading.Tasks;
public class AsyncAwaitExample : MonoBehaviour
{
     private async void Start()
     {
        Debug.Log("Wait.");
        await WaitOneSecondAsync();
        DoMoreStuff(); // Will not execute until WaitOneSecond has completed
     }
    private async Task WaitOneSecondAsync()
    {
        await Task.Delay(TimeSpan.FromSeconds(1));
        Debug.Log("Finished waiting.");
    }
}

this is a feature to use .Net 4.x with Unity please see this link for description about it

and this link for sample project and compare it with coroutine

But becareful as documentation says that This is not fully replacement with coroutine

Saintsimonianism answered 7/6, 2019 at 14:15 Comment(4)
AFAIK this is unstable because async/await tasks aren't bound to Unity game objects. This leads to tasks that continue running after play mode has exited.Sherrard
@IsaakEriksson true, but it is easily rectified anyway by standard .NET Task best practices by attaching upon creation a CancellationToken (created via CancellationTokenSource) so that when you wish to cancel any Task(s) you just call CancellationTokenSource.Cancel(). In Unity when exiting Play mode and returning to Edit mode you can make use of EditorApplication.playModeStateChanged specifically PlayModeStateChange.EnteredEditMode and/or PlayModeStateChange.ExitingPlayMode at which point you can Cancel your tasks. I plonk all this in my class EditorWatcher...Durst
...marked with [InitializeOnLoad]. I will always use either a simple time-based function or, if I wish true asynchronous operations on a worker thread, use async/await over Unity pseudo coroutines due to their "inconsistent behavior with all of .NET". :)Durst
Fair point. However, in practice with larger codebases this becomes a source for error when one forgets to add a cancellation token.Sherrard
R
2

Bear in mind that coroutine stack ! If starting your coroutines in Update(), you might end up with loads of coroutines waiting inline and executing almost at the same time, just after your wait. To avoid this, a good approach is to use a boolean, preventing from "stacking" coroutines :

  bool isRunning = false;
  IEnumerator MyCoroutine(){
    isRunning = true;
    print("started");
    yield return new WaitForSeconds(3);
    print("3 seconds elapsed");
    yield return new WaitForSeconds(3);
    print("more 3 seconds");
    yield return new WaitForSeconds(2);
    print("ended");
    isRunning = false;
  }
  void Update(){
    if (!isRunning) StartCoroutine(MyCoroutine());
  }

Source : https://answers.unity.com/questions/309613/calling-startcoroutine-multiple-times-seems-to-sta.html

Regret answered 25/7, 2022 at 15:33 Comment(1)
This seems a bit unnecessary .. why not rather simply put a while(true) loop inside the MyCoroutine then?Acyl
A
0

Adding to this answer about async Task

There is a (free) thrid-party library UniTask (also available via OpenUPM) which provides a more light weight async implementation and additionally seamless async <-> Unity main thread integrations

Example from their repo

// extension awaiter/methods can be used by this namespace
using Cysharp.Threading.Tasks;

// You can return type as struct UniTask<T>(or UniTask), it is unity specialized lightweight alternative of Task<T>
// zero allocation and fast excution for zero overhead async/await integrate with Unity
async UniTask<string> DemoAsync()
{
    // You can await Unity's AsyncObject
    var asset = await Resources.LoadAsync<TextAsset>("foo");
    var txt = (await UnityWebRequest.Get("https://...").SendWebRequest()).downloadHandler.text;
    await SceneManager.LoadSceneAsync("scene2");

    // .WithCancellation enables Cancel, GetCancellationTokenOnDestroy synchornizes with lifetime of GameObject
    var asset2 = await Resources.LoadAsync<TextAsset>("bar").WithCancellation(this.GetCancellationTokenOnDestroy());

    // .ToUniTask accepts progress callback(and all options), Progress.Create is a lightweight alternative of IProgress<T>
    var asset3 = await Resources.LoadAsync<TextAsset>("baz").ToUniTask(Progress.Create<float>(x => Debug.Log(x)));

    // await frame-based operation like a coroutine
    await UniTask.DelayFrame(100); 

    // replacement of yield return new WaitForSeconds/WaitForSecondsRealtime
    await UniTask.Delay(TimeSpan.FromSeconds(10), ignoreTimeScale: false);
    
    // yield any playerloop timing(PreUpdate, Update, LateUpdate, etc...)
    await UniTask.Yield(PlayerLoopTiming.PreLateUpdate);

    // replacement of yield return null
    await UniTask.Yield();
    await UniTask.NextFrame();

    // replacement of WaitForEndOfFrame(requires MonoBehaviour(CoroutineRunner))
    await UniTask.WaitForEndOfFrame(this); // this is MonoBehaviour

    // replacement of yield return new WaitForFixedUpdate(same as UniTask.Yield(PlayerLoopTiming.FixedUpdate))
    await UniTask.WaitForFixedUpdate();
    
    // replacement of yield return WaitUntil
    await UniTask.WaitUntil(() => isActive == false);

    // special helper of WaitUntil
    await UniTask.WaitUntilValueChanged(this, x => x.isActive);

    // You can await IEnumerator coroutines
    await FooCoroutineEnumerator();

    // You can await a standard task
    await Task.Run(() => 100);

    // Multithreading, run on ThreadPool under this code
    await UniTask.SwitchToThreadPool();

    /* work on ThreadPool */

    // return to MainThread(same as `ObserveOnMainThread` in UniRx)
    await UniTask.SwitchToMainThread();

    // get async webrequest
    async UniTask<string> GetTextAsync(UnityWebRequest req)
    {
        var op = await req.SendWebRequest();
        return op.downloadHandler.text;
    }

    var task1 = GetTextAsync(UnityWebRequest.Get("http://google.com"));
    var task2 = GetTextAsync(UnityWebRequest.Get("http://bing.com"));
    var task3 = GetTextAsync(UnityWebRequest.Get("http://yahoo.com"));

    // concurrent async-wait and get results easily by tuple syntax
    var (google, bing, yahoo) = await UniTask.WhenAll(task1, task2, task3);

    // shorthand of WhenAll, tuple can await directly
    var (google2, bing2, yahoo2) = await (task1, task2, task3);

    // return async-value.(or you can use `UniTask`(no result), `UniTaskVoid`(fire and forget)).
    return (asset as TextAsset)?.text ?? throw new InvalidOperationException("Asset not found");
}

In this case this might simply look like e.g.

public void RunNumberWizard()
{
    // allows to run that async task without awaiting it
    RunNumberWizardAsync().Forget();
}

private async UniTask RunNumberWizardAsync()
{
    TextUI.text = "Welcome to Number Wizard!";
    await UniTask.Delay(TimeSpan.FromSeconds(3));
    TextUI.text = ("The highest number you can pick is " + max);
    await UniTask.Delay(TimeSpan.FromSeconds(3));
    TextUI.text = ("The lowest number you can pick is " + min);
}
Acyl answered 20/7, 2023 at 7:42 Comment(0)
D
0

Here is an example of some code I wrote, hope it helps you.

using System.Collections;
using UnityEngine;


 public class HideOnStart : MonoBehaviour
    {
        enum Priority
        {
            INSTANT,
            DELAYED,
            LAST
        }

        [SerializeField] Priority priority;

        void Start()
        {
            if (priority == Priority.INSTANT) gameObject.SetActive(false);
            else { StartCoroutine(delay()); }
        }

        IEnumerator delay()
        {
            yield return new WaitForSeconds((int)priority / 50);
            gameObject.SetActive(false);
        }
    }
Deepfry answered 14/10, 2023 at 11:49 Comment(0)
K
0

You cannot use wait function without Coroutine.

void Start()
{
    StartCoroutine(Test());
}

IEnumerator Test()
{
    Debug.Log("This is in the IEnumerator");
    //Waiting will be available here.
    yield return new WaitForSecond(1);
    Debug.Log("Already wait for 1 second");
}

Remember that if you start the Coroutine, you need to stop it when it is done;

void Start()
{
    StartCoroutine(Test());

    //Stopping is here;
    StopCoroutine(Test());
}
Katrinka answered 21/2, 2024 at 8:1 Comment(0)
D
0
  1. I'll provide you an instance using UniRx.
using UniRx; //or R3
public class ExampleWithUniRx : MonoBehaviour
{
    IDisposable stream = null;
    public void Play(Text TextUI, string max, string min)
    {
        stream?.Dispose();
        TimeSpan delay = TimeSpan.FromSeconds(3);
        var stringsArray = new string[3]{"Welcome to Number Wizard!"
            , "The highest number you can pick is " + max
            , "The lowest number you can pick is " + min};

        stream = Observable.Timer(TimeSpan.Zero, delay, Scheduler.MainThreadIgnoreTimeScale)
        //this stream emits values (0,1,...) each *delay* starting from TimeSpan.Zero
        //If you wish to in app time scale has 
        //influence on timer then use Scheduler.MainThread
            .TakeWhile(x => x < stringsArray.Length)
            .Select(x => stringsArray[x])
            .Subscribe(text => TextUI.text = text);
     //Never forget to dispose stream 
     //You can dispose in a OnDestroy method, for instance.
     //If you use this code in MonoBehaviour class, you can call .AddTo(this) after subscribe. 
     //Then your stream will be connected to the lifetime of current gameObject

    }
    protected virtual void OnDestroy()
    {
        stream?.Dispose();
    }
}

You can use R3 as well instead of UniRx. it's a rework of good old UniRx from the same author. But there are several extra steps to install it.

  1. Another approach is to use DoTween library. It has included DOText method. And DOTween also allows to create pipelines as well. enter image description here

This 2 approaches are more elegant and can be extensed much easier than approaches with coroutines and tasks IMHO.

Dredger answered 15/3, 2024 at 15:0 Comment(0)
C
-1

here is more simple way without StartCoroutine:

float t = 0f;
float waittime = 1f;

and inside Update/FixedUpdate:

if (t < 0){
    t += Time.deltaTIme / waittime;
    yield return t;
}
Cicelycicenia answered 26/6, 2020 at 14:21 Comment(1)
You cannot put a yield return into an Update/FixedUpdate because those methods are void return types.Longford
C
-1

Use async and await

public void Start() {
     doTask();
}

 async void doTask() {
        Debug.Log("Long running task started");
        // wait for 5 seconds, update your UI
        await Task.Delay(TimeSpan.FromSeconds(5f));

        // update your UI
        Debug.Log("Long running task has completed");
}
Chesney answered 1/4, 2021 at 4:55 Comment(1)
async void in general is an anti-pattern! It prevents any proper exception handling! Also note that the Unity API can mostly only be used on the main thread. So if you use async you have to make sure that the delayed code is actually being executed on the Unity main thread again! therefore using async for this kind of task mostly makes no sense at all ;)Acyl
A
-2

//Here is a example of some of my code to wait in Unity I have made using a value and update dating it every update, once it its the value the if statement is looking for it will run the task.

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

public class EnterCarCollider : MonoBehaviour
{
    public GameObject player;

    //Calls & Delcares vehicle objects
    public GameObject Camera;
    public VehicleControl ascript;
    public Collider enterDriverCollider;
    public Collider parkBreakCollider;
    public GameObject enterVehicleDriverToolTip;

    public int forStayInTime = 32;
    public int timeInActiveTriggeredCollider;

    private void Start()
    {
        ascript = GetComponent<VehicleControl>();
        timeInActiveTriggeredCollider = 0;
    }

    private void OnTriggerStay(Collider other)
    {
        if (forStayInTime <= timeInActiveTriggeredCollider)
        {
            if (Input.GetKey(KeyCode.E))
            {
                ascript.enabled = !ascript.enabled;
                Camera.active = true;
                player.active = false;
                enterDriverCollider.enabled = false;
                parkBreakCollider.enabled = false;
           }
           // TODO: Enter car message
           enterVehicleDriverToolTip.active = true;
        }
        timeInActiveTriggeredCollider++;
    }

    private void OnTriggerExit(Collider other)
    {
        enterVehicleDriverToolTip.active = false;
        timeInActiveTriggeredCollider = 0;
    }

    private void Update()
    {
        if (enterDriverCollider.enabled is false)
        {
            timeInActiveTriggeredCollider = 0;
        }
    }
}
Afghanistan answered 10/4, 2021 at 12:49 Comment(1)
What does this add what this answer didn't mention already long ago?Acyl

© 2022 - 2025 — McMap. All rights reserved.