提问人:DiogoSaraiva 提问时间:5/5/2015 最后编辑:derHugoDiogoSaraiva 更新时间:10/19/2023 访问量:399611
如何在 Unity 中以简单的方式使脚本等待/休眠
How to make the script wait/sleep in a simple way in unity
问:
如何在 之间放置一个睡眠函数,以便在每个短语之间等待 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 中做到这一点#
答:
使用 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);
}
评论
在 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
。
和 之间的唯一区别是使用未缩放的时间来等待,这意味着当暂停游戏时,该函数不会受到影响,但会受到影响。WaitForSeconds
WaitForSecondsRealtime
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
每帧递增一个变量。
一个很好的例子是,当您需要计时器在屏幕上显示它等待了多少时间时。基本上就像一个计时器。
当您想在 true 时使用变量中断等待/睡眠时,这也很好。这是可以使用的地方。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
函数。
当条件为 时等待。例如,当您想在按下 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 来调用函数。调用该函数时,可以将调用该函数之前等待的时间传递给其第二个参数。下面的示例将在调用的几秒钟后调用该函数。Invoke
feedDog()
5
Invoke
void Start()
{
Invoke("feedDog", 5);
Debug.Log("Will feed dog after 5 seconds");
}
void feedDog()
{
Debug.Log("Now feeding Dog");
}
7.使用函数和 Time.deltaTime
。Update()
它就像 #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());
评论
Update
Invoke
InvokeRepeating
在 .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 一起使用的一项功能,请参阅此链接以获取有关它的说明
但要小心,因为文档说这并不能完全替换协程
评论
Task
CancellationToken
CancellationTokenSource
Task
CancellationTokenSource.Cancel()
EditorApplication.playModeStateChanged
PlayModeStateChange.EnteredEditMode
PlayModeStateChange.ExitingPlayMode
Cancel
class EditorWatcher
[InitializeOnLoad]
async/await
这是没有 StartCoroutine 的更简单的方法:
float t = 0f;
float waittime = 1f;
在 Update/FixedUpdate 中:
if (t < 0){
t += Time.deltaTIme / waittime;
yield return t;
}
评论
使用 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");
}
评论
async void
一般来说是一种反模式!它阻止了任何适当的异常处理!另请注意,Unity API 通常只能在主线程上使用。因此,如果您使用,则必须确保延迟的代码实际上再次在Unity主线程上执行!因此,用于此类任务大多毫无意义;)async
async
这是我在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;
}
}
}
评论
请记住,协程堆栈!如果在 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
评论
while(true)
MyCoroutine
添加到这个关于异步
任务的答案
有一个(免费的)第三方库 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);
}
这是我编写的一些代码示例,希望对您有所帮助。
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);
}
}
评论