Unity 協程 coroutine 實現一連串動作序列


建立時間: 2023年1月14日 09:23
更新時間: 2023年1月14日 14:10

說明

這次我將用簡短的內容來讓大家快速了解協程,在以往的情況下就是在 Update() 方法實作,但這次我學到協程,使用協程設計的程式,需要先了解協程是什麼概念,你才能用另外一種思維去設計程式邏輯。

協呈

以下內容從官方文件 Coroutines 個人翻譯

協程允許您將任務分散到多個幀中。在 Unity 中,協程是一種可以暫停執行並將控制權返回給 Unity,然後在下一幀從中斷處繼續執行的方法。

在大多數情況下,當您調用一個方法時,它會運行到完成,然後將控制權返回給調用方法,以及任何可選的返回值。 這意味著在一個方法中發生的任何動作都必須在單個幀更新中發生。

在您希望使用方法調用來包含過程動畫或隨時間變化的事件序列的情況下,您可以使用協程。

但是,重要的是要記住協程不是執行緒。 在協程中運行的同步操作仍然在主執行緒上執行。 如果您想減少花在主執行緒上的 CPU 時間,避免協程中的阻塞操作與在任何其他腳本代碼中一樣重要。 如果您想在 Unity 中使用多執行緒代碼,請考慮 C# Job System

如果您需要處理長時間的異步操作,例如等待 HTTP 傳輸、資產加載或文件 I/O 完成,最好使用協程。

註: Job System 以後有機會再跟大家講解

簡述

如果你還是很難理解協程的話,你可以想成自訂的 Update(),每調用一個協程,就是新增一個 MyUpdate(),差異在於 Update() 是一直在運行,而 MyUpdate() 可能只運行一段時間就結束了,如果要更細節探討協程的話可以參考 淺談 Unity Coroutine 的運行方式

yield

在開始看協程範例之前,先了解一下 yield 功能,yield 可以當作是一種轉移控制權的關鍵字,如下範例 ProduceEvenNumbers() 是可枚舉的整數函式,當 foreach 裡面調用 ProduceEvenNumbers() 時,它會執行到 yield 關鍵字,接著 return i,然後控制權轉移給 foreach,此時 ProduceEvenNumbers() 只是轉移控制權,它還沒被清除,然後 foreach 裡面的 i 就會等於剛剛 return i,接著執行 foreach 段落裡面的程式碼後,又接著進入 ProduceEvenNumbers(),控制權又回到 ProduceEvenNumbers(),它會在 yield 之後接著執行,因為它沒有被清除,所以它會跑第二次 for 迴圈,接著一直這樣直到結束。

程式原文: yield 語句 - 提供下一個專案

foreach (int i in ProduceEvenNumbers(9))
{
    Console.Write(i);
    Console.Write(" ");
}
// Output: 0 2 4 6 8

IEnumerable<int> ProduceEvenNumbers(int upto)
{
    for (int i = 0; i <= upto; i += 2)
    {
        yield return i;
    }
}

在協程中 yield 類似同樣概念,Unity 生命週期在每一幀都會跑很多方法 Update()LateUpdate() 等等,協程要在什麼時候取回控制權就要看 yield return 什麼內容,在此我們先記得兩個就好,這兩個應該也是最常用的,剩下的大家可以自行研究

  1. yield return null;Update() 後取回控制權
  2. yield return new WaitForSeconds(1); 等待一秒後取回控制權

範例

最後來看一下網友提問的 Moving a GameObject to a position, waiting, and then moving it again

這位網友想要讓遊戲物件移動到指定位置,然後等待一下,接著再走回去

為了方便我改成按 space 鍵開始協程,以及刪掉部分程式,只留重點要說明的部分。

OpenDoor.cs

public class OpenDoor : MonoBehaviour
{
    public Transform origPos;
    public Transform targetPos;
    public float speed;

    private void Start()
    {
    }

    void Update()
    {
        if (Input.GetKeyDown("space"))
        {
            StartCoroutine(MovePauseAndReturn());
        }
    }

    IEnumerator MovePauseAndReturn()
    {   
        yield return MoveTo(targetPos);
        yield return new WaitForSeconds(1);
        yield return MoveTo(origPos);
    }

    IEnumerator MoveTo(Transform destination)
    {
        while (transform.position != destination.position)
        {
            transform.position = Vector3.MoveTowards(
                transform.position,
                destination.position,
                speed * Time.deltaTime);

            yield return null;
        }
    }
}
  • StartCoroutine(MovePauseAndReturn()) 代表要開始協程
  • MoveTowards() 是 Unity 內建方法,可以從 A 點朝著 B 點移動,速度為 speed
  • 注意 MoveTo() yield 是在 while 裡面,也就是說每幀移動一些,就將控制權回到主執行緒中,在下一幀再移動一些,直到 while 迴圈結束

結論

以上就是我整理讓大家可以快速了解 Unity 協程以及用途,盡量用講單易懂的方式解釋其內容,如果比喻有不洽當的地方請多包含,內容如果有誤可到 Facebook 粉絲專頁或寫 Email 聯絡我們。

觀看次數: 1297
coroutineyield
按讚追蹤 Enjoy 軟體 Facebook 粉絲專頁
每週分享資訊技術

一杯咖啡的力量,勝過千言萬語的感謝。

支持我一杯咖啡,讓我繼續創作優質內容,與您分享更多知識與樂趣!