如何使脚本以简单的方式统一等待/休眠

Posted

技术标签:

【中文标题】如何使脚本以简单的方式统一等待/休眠【英文标题】:How to make the script wait/sleep in a simple way in unity 【发布时间】:2015-07-15 09:26:50 【问题描述】:

如何在TextUI.text = .... 之间添加睡眠功能,让每个短语之间等待 3 秒?

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
.....

但我不知道如何在 Unity 中使用 C# 执行此操作

【问题讨论】:

“没用”到底是什么意思? 产生 WaitForSeconds (3);没用 “不工作”到底是什么意思? Thread.Sleep(3000) 有什么问题 我认为他们的意思是它没有放慢速度 【参考方案1】:

在 Unity 中有多种等待方式。它们真的很简单,但我认为值得介绍大多数方法:

1.带有协程和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

WaitForSecondsWaitForSecondsRealtime 之间的唯一区别是 WaitForSecondsRealtime 使用未缩放的时间等待,这意味着当使用 Time.timeScale 暂停游戏时,WaitForSecondsRealtime 函数不会受到影响,但 WaitForSeconds会的。

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 每帧递增一个变量。

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

当你想用 boolean 变量来中断等待/睡眠时,它也是很好的。这是可以使用yield 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 函数:

等到条件变为true。一个例子是等待玩家得分为100然后加载下一个级别的函数。

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 函数。

等待条件为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来调用函数。当您调用Invoke 函数时,您可以将调用该函数之前的等待时间传递给它的第二个参数。下面的示例将在调用5 秒后调用feedDog() 函数Invoke

void Start()

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


void feedDog()

    Debug.Log("Now feeding Dog");

7。使用Update() 函数和Time.deltaTime

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

【讨论】:

所有这些(除了invoke)都是协程。这实际上只是一种方法。 @TylerSigi No. #7 使用Update 函数。他们中的大多数人都使用协程,但他们等待的方式不同。添加它们不仅仅是为了好玩。它们在不同的场景中是需要的,否则你将无法获得你想要的行为,或者你最终会重新发明***,而 Unity 的内置函数可以处理一些事情。 关于Invoke(和InvokeRepeating)的特别说明:Afaik 它可以工作 - 与其他方法不同 - 也适用于 inactive GameObject 或 disabled 在某些情况下使它非常强大的组件! 不用courotine也能实现吗?【参考方案2】:

您使用 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);

【讨论】:

我不明白...我应该用什么替换 SomeCoroutine 吗? 您必须将“WaitForSeconds”作为 iEnumerator 的输出才能使其工作。尝试阅读 Unity Coroutines。【参考方案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 结合使用的功能,请参阅 this link for description about it

和this link for sample project and compare it with coroutine

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

【讨论】:

【参考方案4】:

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

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

在 Update/FixedUpdate 内部:

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

【讨论】:

您不能将 yield return 放入 Update/FixedUpdate 中,因为这些方法是 void 返回类型。【参考方案5】:

使用异步和等待

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

【讨论】:

async void 通常是一种反模式!它会阻止任何适当的异常处理!另请注意,Unity API 大多只能在主线程上使用。因此,如果您使用async,您必须确保延迟的代码实际上再次在 Unity 主线程上执行!因此使用async 来完成这类任务几乎毫无意义;)【参考方案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;
        
    

【讨论】:

这增加了什么this answer很久以前没有提到的东西?

以上是关于如何使脚本以简单的方式统一等待/休眠的主要内容,如果未能解决你的问题,请参考以下文章

Selenium自动化测试脚本中三种等待时间简介

Linux设备驱动程序 之 休眠

以编程方式提供数据源以休眠

使程序暂停或休眠C++的函数[重复]

1.Linux电源管理-休眠与唤醒

如何在收到异步函数的回调之前使线程休眠?