Unity 協程與線程


協程是不同步的

協程 不是 線程,協同程序是 不同步

      一個線程在程序中和其他線程是異步運行的,在多處理器機器中一個線程可以同時與所有其他線程的實時運行其代碼,這使得線程編程能夠解決很復雜的事情,因為可能在相同的時間里一個線程在改變它而另一個線程正在讀取它,這意味着另一個線程實際上可以改變的東西在游戲中處理的中間似乎是你的源代碼一行。這是因為你寫的代碼是由機器變成匯編語言,更是更復雜。正因為如此,你必須通過鎖,以確保這種情況不會由任何確保沒有共享內存發生。或者通過鎖定其他線程使用同一塊內存,當他們在讀取或更改時。

什么是協程?

      協同程序絕對不是一個線程。這意味着在同一時間只有一個協同程序在執行,它會被執行在游戲的主線程上,所以實際上在同一時間游戲的核心只有一個協同程序在運行[這段翻譯的不太准確]

     你永遠不需要擔心同步或鎖定一個值當你正在編寫一個協同程序。你有完全的控制權,直到你的代碼執行到 yiedld

  因此總結一下協程的定義

    協程只是部分執行,並假定在適當的條件得到滿足,在未來的某一時刻將被恢復,直到它的工作完成

Unity函數執行圖

Unity processes coroutines every frame of the game for every object that has one or more running.  The processing occurs after Update and before LateUpdate for most yield statements, but there are special cases:

Unity的流程協同程序在游戲的每一幀每個對象為具有一個或多個正在運行的。Update() 之后,LateUpdate()之前 ,發生的 yield 語句的處理,但也有特殊情況

Overview

When the coroutine is activated it will execute right up to the next yield statement and then it will pause until it is resumed.  You can see where it will resume in the diagram above, based on what you yield.

當協程被激活,它會一直到下一個 yield語句執行,然后它會暫停,直到它恢復。你可以在上圖中看到它會恢復,根據你的 yield語句。

簡單的協程示例

讓我們來看看一個非常簡單的協程

IEnumerator TestCoroutine()
{
      while(true)
      {
           Debug.Log(Time.time);
           yield return null;
      }
}

該協程將會永遠執行下去。它記錄當前的時間,然后yield,當它被恢復,它又進入了這個循環,記錄一次時間,遇到 yield 並重復之前的操作

The code inside the loop is exactly like an Update function.  It runs once every frame for this object, just after the script's Update routine runs (if it has one).

這代碼循環就像 Update() 函數。這個對象在每一幀中運行,腳本的Update 程序運行后(如果有的話)

When you call StartCoroutine(TestCoroutine()) the code executes immediately up to the first time it yields, it will then be resumed when Unity processes coroutines for this object.

當你調用 StartCoroutine(TestCoroutine()) 代碼立即第一次得到執行 然后 yield,當Unity 引擎再次處理這個GameObject時,協程會被恢復

If you start a coroutine early in the processing of a game object, like creating one in Start, Update or OnCollisionEnter then that coroutine will immediately run up to the first yield, then it will resume during the same frame if you yield return null .

如果你在早於Unity處理到GameObject就執行一個協程 比如 Start(),Update()或OnCollisionEnter()將會繼續執行,當第一次遇到yield,然后同一幀會恢復,如果你yield null。有時候會有奇怪的結果,如果你不考慮它。

 

是否會無限循環

現在還有一件事,在我們的測試協程顯然不是無限循環

下列情況協程將會不再被執行:如果你撥打電話,會停止游戲對象的協同程序,如果它被銷毀,它不會再次運行。如果腳本被直接或通過游戲對象上使用SetActive(false),它也不會再執行。

I Yield Sir

Unity processes coroutines every frame of the game for every object that has one or more running.

Unity在處理協程時是 在游戲的每一幀,每一個GameObject上進行的,可以處理1個或多個

你也許也想哦,不,它不需要,如果你使用這樣的

yield return new WaitForSeconds(1)then it doesn't process it for another 1 second!"那么它不處理它的另外1秒Well actually Unity does process that coroutine every frame, checking to see if the right amount of time has elapsed - it doesn't process your code, but it does process the coroutine which is the wrapper its made around your script.那么實際上,Unity 會處理協程在每一幀,檢查合適的時間是否已經過去,它不會處理你的代碼,但是它會處理這個協程,是你的腳本在包裝這個協程因此我們知道,我們可以有效的暫停我們的代碼通過 yield ,下面是那些你可以Return 的:

  • null -協程執行下一次,它是合格的
  • WaitForEndOfFrame - 協程的框架上執行,在所有的渲染和圖形用戶界面完成之后
  • WaitForFixedUpdate - 導致此協程在下一次物理學的步驟執行,在所有的物理計算之后
  • WaitForSeconds - 使協程並不是一個特定的游戲時間內執行
  • WWW - waits for a web request to complete (resumes as if WaitForSeconds or null)
  • Another coroutine - in which case the new coroutine will run to completion before the yielder is resumed(在這種情況下,新的協同程序將在這個Yield恢復之前完成)

You can also issue the command yield break; which immediately stops the coroutine.你還可以發出 yield break 命令,去立即停止這個協程Because of WaitForEndOfFrame coroutines can be used to get information from render textures when all cameras have completed rendering and the GUI has been displayed因為 WaitForEndOfFrame 協程可以用於從渲染紋理中獲取信息, 當所有的Camera已完成渲染 並且 GUI 已經被顯示Using yield return new WaitForSeconds(x) will never resume if the Time.timeScale is set to 0.采用 yield return new WaitForSeconds(x) 將永遠不會被恢復,如果 Time.timeScale =0Of course the great thing about all of this is that you can write code that needs to execute over a period of time, or wait for some external event to occur, and keep it all nicely together in a single function making your code far more readable than if you had to write multiple functions or lots of code to keep checking the state of things.當然,關於這一切的偉大的事情是,你可以寫需要執行一段時間,或者等待發生一些外部事件,並保持它擁有時尚典雅的一起在一個單一的功能使你的代碼更易讀的代碼比,如果你不得不編寫多個函數的代碼或地段繼續檢查事物的狀態。這是真正的協同程序的地步。

 

總結:

  1. Coroutines are a really good way of making a sequence of operations happen over time or when some external process is completed
  2. Coroutines are not threads and are not asynchronous
  3. Nothing else is running when your coroutine is executing
  4. Your coroutine will resume when the conditions of your yield statement are met
  5. Coroutines are inactive when the script is disabled or the object is destroyed
  6. yield return new WaitForSeconds is dependent on game time which is affected by Time.timeScale

譯:

  1. 協程通過按順序的操作 或一些其實的處理 當它完成時
  2. 協程並不是線程,它沒有同步
  3. 沒有任何 或已經在運行協程
  4. 你的協程

 

協程的實際用途

希望我們已經理解了協程是什么,以及它們在運行時。我們的高級教程將研究該技術在它們身后

讓我們用協程做一些事情。幾個簡單的輔助函數,使用協程可以讓我們創建易於切割的序列

我們可以寫一個協同的移動對象到目標位置和旋轉。我們可以寫一個協程的等待動畫是一個特定的完成百分比。然后利用這兩個工具, 我們可以很容易地編寫腳本在一個單一的功能,其中它會很容易閱讀全切序列

使用協程,通過觀看它在移動,為的是要確保不會有其它的協程或Update()函數里更改它的位置在同一時間確保你只有一個協程影響GameObject在同一時間,禁用Update() 函數 移動對象

協程動畫示例

這里有一個協同的一個例子等待動畫部分完成

//Wait for an animation to be a certain amount complete
IEnumerator WaitForAnimation(string name, float ratio, bool play)
{
    //Get the animation state for the named animation
    var anim = animation[name];
    //Play the animation
    if(play) animation.Play(name);
 
    //Loop until the normalized time reports a value
    //greater than our ratio.  This method of waiting for
    //an animation accounts for the speed fluctuating as the
    //animation is played.
    while(anim.normalizedTime + float.Epsilon + Time.deltaTime < ratio)
        yield return new WaitForEndOfFrame();
 
}

You could write a coroutine to wait for an animation like this:

IEnumerator Die()
{
       //Wait for the die animation to be 50% complete
       yield return StartCoroutine(WaitForAnimation("die",0.5f, true));
       //Drop the enemies on dying pickup
       DropPickupItem();
       //Wait for the animation to complete
       yield return StartCoroutine(WaitForAnimation("die",1f, false));
       Destroy(gameObject);
}

資料

英文原文:http://unitygems.com/coroutines/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM