天天看點

Coroutines 協程 腳本系列1Coroutines 協程

Coroutines 協程

本文檔主要是對Unity官方手冊的個人了解與總結(其實以翻譯記錄為主:>)

僅作為個人學習使用,不得作為商業用途,歡迎轉載,并請注明出處。

文章中涉及到的操作都是基于Unity2018.3版本

參考連結:https://docs.unity3d.com/Manual/Coroutines.html

When you call a function, it runs to completion before returning. This effectively means that any action taking place in a function must happen within a single frame update; a function call can’t be used to contain a procedural animation or a sequence of events over time. As an example, consider the task of gradually reducing an object’s alpha (opacity) value until it becomes completely invisible.

當您調用一個函數時,它會在傳回之前運作到完成。這實際上意味着在函數中發生的任何操作都必須在單個幀更新中發生;函數調用不能用于包含過程動畫或随時間變化的事件序列。例如,考慮逐漸減少對象的alpha(不透明度)值,直到它完全不可見的任務。

void Fade() 
{
    for (float f = 1f; f >= 0; f -= 0.1f) 
    {
        Color c = renderer.material.color;
        c.a = f;
        renderer.material.color = c;
    }
}
           

As it stands, the Fade function will not have the effect you might expect. In order for the fading to be visible, the alpha must be reduced over a sequence of frames to show the intermediate values being rendered. However, the function will execute in its entirety within a single frame update. The intermediate values will never be seen and the object will disappear instantly.

就目前情況而言,Fade函數不會産生您所期望的效果。為了使淡出效果可見,必須在一系列幀上減少alpha值,以顯示渲染的中間值。但是,該函數将在單個幀update中完整地執行。中間值将永遠不會出現,對象将立即消失。

It is possible to handle situations like this by adding code to the Update function that executes the fade on a frame-by-frame basis. However, it is often more convenient to use a coroutine for this kind of task.

可以通過在Update函數中添加代碼來處理這種情況,該函數在逐幀的基礎上執行淡出。然而,對于這種任務,使用協程通常更友善。

A coroutine is like a function that has the ability to pause execution and return control to Unity but then to continue where it left off on the following frame. In C#, a coroutine is declared like this:

協程就像一個函數,它可以暫停執行并将控制傳回給Unity,但是在接下來的幀中,它會繼續執行它停止的地方。在c#中,協程是這樣聲明的:

IEnumerator Fade() 
{
    for (float f = 1f; f >= 0; f -= 0.1f) 
    {
        Color c = renderer.material.color;
        c.a = f;
        renderer.material.color = c;
        yield return null;
    }
}
           

It is essentially a function declared with a return type of IEnumerator and with the yield return statement included somewhere in the body. The yield return line is the point at which execution will pause and be resumed the following frame. To set a coroutine running, you need to use the StartCoroutine function:

本質上它是一個用IEnumerator作為傳回類型聲明的函數,并在主體的某個地方包含yield return語句。yield傳回行是執行将暫停并在下一幀中恢複的點。要設定協程運作,需要用start調用協程函數:

void Update()
{
    if (Input.GetKeyDown("f")) 
    {
        StartCoroutine("Fade");
    }
}
           

You will notice that the loop counter in the Fade function maintains its correct value over the lifetime of the coroutine. In fact any variable or parameter will be correctly preserved between yields.

您将注意到,Fade 函數中的循環計數器在協程的生命周期中維護其正确的值。事實上,任何變量或參數都會在yields之間被正确地保留下來。

By default, a coroutine is resumed on the frame after it yields but it is also possible to introduce a time delay using WaitForSeconds:

預設情況下,協程在yields 後會在幀上恢複,但也可以使用WaitForSeconds引入時間延遲:

IEnumerator Fade() 
{
    for (float f = 1f; f >= 0; f -= 0.1f) 
    {
        Color c = renderer.material.color;
        c.a = f;
        renderer.material.color = c;
        yield return new WaitForSeconds(.1f);
    }
}
           

This can be used as a way to spread an effect over a period of time, but it is also a useful optimization. Many tasks in a game need to be carried out periodically and the most obvious way to do this is to include them in the Update function. However, this function will typically be called many times per second. When a task doesn’t need to be repeated quite so frequently, you can put it in a coroutine to get an update regularly but not every single frame. An example of this might be an alarm that warns the player if an enemy is nearby. The code might look something like this:

這可以作為一種在一段時間内傳播效果的方法,但它也是一種有用的優化。遊戲中的許多任務需要定期執行,最明顯的方法是将它們包含在更新函數中。然而,這個函數通常每秒會被調用很多次。當一個任務不需要如此頻繁地重複時,您可以将其放入一個協程中以定期獲得更新,但不是每一幀。例如,如果附近有敵人,就會發出警告。代碼可能是這樣的:

function ProximityCheck() 
{
    for (int i = 0; i < enemies.Length; i++)
    {
        if (Vector3.Distance(transform.position, enemies[i].transform.position) < dangerDistance) {
                return true;
        }
    }
    
    return false;
}
           

If there are a lot of enemies then calling this function every frame might introduce a significant overhead. However, you could use a coroutine to call it every tenth of a second:

如果有很多敵人,那麼調用這個函數的每一幀可能會帶來很大的開銷。然而,你可以用一個協程來調用它每十分之一秒:

IEnumerator DoCheck() 
{
    for(;;) 
    {
        ProximityCheck;
        yield return new WaitForSeconds(.1f);
    }
}
           

This would greatly reduce the number of checks carried out without any noticeable effect on gameplay.

這将大大減少執行檢查的次數,而不會對遊戲玩法産生任何明顯的影響。

Note: Coroutines are not stopped when a MonoBehaviour is disabled, but only when it is definitely destroyed. You can stop a Coroutine using MonoBehaviour.StopCoroutine and MonoBehaviour.StopAllCoroutines. Coroutines are also stopped when the MonoBehaviour is destroyed.

注意:當MonoBehaviour 被禁用時,協程不會被停止,但是隻有當它确實被銷毀時,協程才會停止。您可以使用monobehavior.StopCoroutine和 MonoBehaviour.StopAllCoroutines停止協程。當MonoBehaviour 被銷毀時,協程也會停止。

繼續閱讀