如何在 Unity 中以简单的方式使脚本等待/休眠

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

提问人:DiogoSaraiva 提问时间:5/5/2015 最后编辑:derHugoDiogoSaraiva 更新时间:10/19/2023 访问量:399611

问:

如何在 之间放置一个睡眠函数,以便在每个短语之间等待 3 秒?TextUI.text = ....

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);

我已经尝试了各种方法,但没有一个奏效,例如:

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);

在 bash 中,它将是:

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

但我不知道如何使用 C 在 Unity 中做到这一点#

c# unity-game-engine monodevelop 睡眠 等待

评论

1赞 Max Yankov 5/5/2015
“不起作用”到底是什么意思?
1赞 DiogoSaraiva 5/5/2015
yield WaitForSeconds (3);不起作用
2赞 Max Yankov 5/5/2015
“不起作用”到底是什么意思?
1赞 Mustafa Ekici 8/3/2020
Thread.Sleep(3000)有什么问题
1赞 RadicalRhino 2/2/2021
我认为他们的意思是它没有放慢速度

答:

14赞 Max Yankov 5/5/2015 #1

使用 WaitForSeconds 是正确的。但我怀疑你尝试过在没有协程的情况下使用它。这就是它应该如何工作:

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);
}

评论

1赞 DiogoSaraiva 5/6/2015
我不明白。。。我应该用什么代替 SomeCoroutine 吗?
0赞 Plastic Sturgeon 5/6/2015
您必须将“WaitForSeconds”作为 iEnumerator 的 yeild 才能正常工作。尝试阅读 Unity 协程。
177赞 Programmer 5/6/2015 #2

在 Unity 中有很多方法可以等待。它们非常简单,但我认为值得介绍大多数方法:

、使用协程和 WaitForSeconds

这是迄今为止最简单的方法。将需要等待一段时间的所有代码放在协程函数中,然后可以使用 WaitForSeconds 等待。请注意,在协程函数中,使用 调用函数。StartCoroutine(yourFunction)

下面的例子将旋转 90 度,等待 4 秒,旋转 40 度并等待 2 秒,最后旋转旋转 20 度。

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.使用协程和 WaitForSecondsRealtime

和 之间的唯一区别是使用未缩放的时间来等待,这意味着当暂停游戏时,该函数不会受到影响,但会受到影响。WaitForSecondsWaitForSecondsRealtimeWaitForSecondsRealtimeTime.timeScaleWaitForSecondsRealtimeWaitForSeconds

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);
}

等待,仍然能够看到您等待了多长时间:

3.使用协程并使用 Time.deltaTime 每帧递增一个变量。

一个很好的例子是,当您需要计时器在屏幕上显示它等待了多少时间时。基本上就像一个计时器。

当您想在 true 时使用变量中断等待/睡眠时,这也很好。这是可以使用的地方。booleanyield break;

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);
}

您仍然可以通过将循环移动到另一个协程函数中并产生它来简化这一点,并且仍然能够看到它计数甚至中断计数器。while

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;
    }
}

等待/休眠,直到变量更改或等于另一个值

4.使用协程和 WaitUntil 函数:

等到条件变为 .例如,一个函数等待玩家的分数,然后加载下一个级别。true100

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.使用协程和 WaitWhile 函数。

当条件为 时等待。例如,当您想在按下 Esc 键时退出应用程序时。true

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.使用 Invoke 函数:

您可以调用 tell Unity 来调用函数。调用该函数时,可以将调用该函数之前等待的时间传递给其第二个参数。下面的示例将在调用的几秒钟后调用该函数。InvokefeedDog()5Invoke

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

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

7.使用函数和 Time.deltaTimeUpdate()

它就像 #3 一样,只是它不使用协程。它使用该函数。Update

这样做的问题是它需要太多的变量,因此它不会每次都运行,而是在等待后计时器结束时只运行一次。

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");
}

在Unity中还有其他方法可以等待,但您绝对应该知道上面提到的方法,因为这样可以更轻松地在Unity中制作游戏。何时使用每个取决于具体情况。

对于您的特定问题,这是解决方案:

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);
}

要从 start 或 Update 函数调用/启动协程函数,请使用

StartCoroutine (showTextFuntion());

评论

4赞 Tyler S. Loeper 10/7/2017
所有这些(invoke 除外)都是协程。这实际上只是一种方法。
3赞 Programmer 10/7/2017
@TylerSigi 编号#7 使用该函数。其余的大多数都使用协程,但他们等待的方式不同。它们不仅仅是为了好玩而添加的。它们在不同的场景中是需要的,否则您将无法获得所需的行为,或者您最终会重新发明轮子,而 Unity 制作的内置函数可以处理一些东西。Update
0赞 derHugo 6/24/2020
关于(和)的特别说明:Afaik 它的工作方式 - 与其他方法不同 - 也适用于非活动游戏对象或禁用的组件,这使得它在某些情况下非常强大!InvokeInvokeRepeating
0赞 Uzair_Abdullah 12/14/2021
没有库尿汀可以实现吗?
8赞 etzl 6/7/2019 #3

在 .Net 4.x 中,可以使用基于任务的异步模式 (TAP) 来实现此目的:

// .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.");
    }
}

这是将 .Net 4.x 与 Unity 一起使用的一项功能,请参阅此链接以获取有关它的说明

这个链接的示例项目,并将其与协程进行比较

但要小心,因为文档说这并不能完全替换协程

评论

1赞 Isaak Eriksson 6/28/2022
AFAIK 这是不稳定的,因为 async/await 任务未绑定到 Unity 游戏对象。这会导致任务在退出播放模式后继续运行。
1赞 10/6/2022
@IsaakEriksson真的,但无论如何,它都很容易通过标准的 .NET 最佳实践进行纠正,方法是在创建时附加一个 (创建于 ),这样当您希望取消任何 (s) 时,只需调用 .在Unity中,当退出播放模式并返回编辑模式时,您可以专门使用和/或在何时可以执行任务。我把这一切都放在我的......TaskCancellationTokenCancellationTokenSourceTaskCancellationTokenSource.Cancel()EditorApplication.playModeStateChangedPlayModeStateChange.EnteredEditModePlayModeStateChange.ExitingPlayModeCancelclass EditorWatcher
1赞 10/6/2022
...标有 。我将始终使用简单的基于时间的函数,或者,如果我希望在工作线程上进行真正的异步操作,请使用 Unity 伪协程,因为它们“与所有 .NET 的行为不一致”。:)[InitializeOnLoad]async/await
1赞 Isaak Eriksson 11/11/2022
公平点。然而,在实践中,对于较大的代码库,当人们忘记添加取消令牌时,这将成为错误的根源。
-1赞 moloko824 6/26/2020 #4

这是没有 StartCoroutine 的更简单的方法:

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

在 Update/FixedUpdate 中:

if (t < 0){
    t += Time.deltaTIme / waittime;
    yield return t;
}

评论

3赞 Matthew 5/18/2021
不能将 yield 返回放入 Update/FixedUpdate 中,因为这些方法是 void 返回类型。
-1赞 Shubham Pandit 4/1/2021 #5

使用 async 和 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");
}

评论

2赞 derHugo 6/22/2021
async void一般来说是一种反模式!它阻止了任何适当的异常处理!另请注意,Unity API 通常只能在主线程上使用。因此,如果您使用,则必须确保延迟的代码实际上再次在Unity主线程上执行!因此,用于此类任务大多毫无意义;)asyncasync
-2赞 Ty Thomasson 4/10/2021 #6

这是我在Unity中等待的一些代码的示例,我使用一个值进行更新,并在每次更新时更新它,一旦它是if语句正在寻找的值,它将运行任务。

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;
        }
    }
}

评论

0赞 derHugo 6/22/2021
这增加了什么,这个答案很久以前没有提到什么?
2赞 PaKempf 7/25/2022 #7

请记住,协程堆栈!如果在 Update() 中启动协程,则最终可能会有大量协程内联等待,并在等待之后几乎同时执行。 为了避免这种情况,一个好的方法是使用布尔值,防止“堆叠”协程:

  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());
  }

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

评论

0赞 derHugo 7/20/2023
这似乎有点没有必要......为什么不简单地在里面放一个循环呢?while(true)MyCoroutine
0赞 derHugo 7/20/2023 #8

添加到这个关于异步任务的答案

有一个(免费的)第三方库 UniTask(也可通过 OpenUPM 获得),它提供了更轻量级的实现和另外无缝的异步 <-> Unity 主线程集成async

来自其存储库的示例

// 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");
}

在这种情况下,这可能只是看起来像,例如

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);
}
0赞 TheCoderEll 10/14/2023 #9

这是我编写的一些代码示例,希望对您有所帮助。

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);
        }
    }