<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      【Unity3D基礎教程】給初學者看的Unity教程(五):詳解Unity3D中的協程(Coroutine)

      作者:王選易,出處:http://www.rzrgm.cn/neverdie/ 歡迎轉載,也請保留這段聲明。如果你喜歡這篇文章,請點【推薦】。謝謝!

      QQ圖片20140529123319

      為什么需要協程

      在游戲中有許多過程(Process)需要花費多個邏輯幀去計算。

      • 你會遇到“密集”的流程,比如說尋路,尋路計算量非常大,所以我們通常會把它分割到不同的邏輯幀去進行計算,以免影響游戲的幀率。
      • 你會遇到“稀疏”的流程,比如說游戲中的觸發器,這種觸發器大多數時候什么也不做,但是一旦被調用會做非常重要的事情(比圖說游戲中自動開啟的門就是在門前放了一個Empty Object作為trigger,人到門前就會觸發事件)。

      不管什么時候,如果你想創建一個能夠歷經多個邏輯幀的流程,但是卻不使用多線程,那你就需要把一個任務來分割成多個任務,然后在下一幀繼續執行這個任務。

      比如,A*算法是一個擁有主循環的算法,它擁有一個open list來記錄它沒有處理到的節點,那么我們為了不影響幀率,可以讓A*算法在每個邏輯幀中只處理open list中一部分節點,來保證幀率不被影響(這種做法叫做time slicing)。

      再比如,我們在處理網絡傳輸問題時,經常需要處理異步傳輸,需要等文件下載完畢之后再執行其他任務,一般我們使用回調來解決這個問題,但是Unity使用協程可以更加自然的解決這個問題,如下邊的程序:

      private IEnumerator Test()  
      {  
          WWW www = new WWW(ASSEST_URL);  
          yield return www;  
          AssetBundle bundle = www.assetBundle;
      }

      協程是什么

      從程序結構的角度來講,協程是一個有限狀態機,這樣說可能并不是很明白,說到協程(Coroutine),我們還要提到另一樣東西,那就是子例程(Subroutine),子例程一般可以指函數,函數是沒有狀態的,等到它return之后,它的所有局部變量就消失了,但是在協程中我們可以在一個函數里多次返回,局部變量被當作狀態保存在協程函數中,知道最后一次return,協程的狀態才別清除。

      簡單來說,協程就是:你可以寫一段順序的代碼,然后標明哪里需要暫停,然后在下一幀或者一段時間后,系統會繼續執行這段代碼。

      協程怎么用?

      一個簡單的C#代碼,如下:

      IEnumerator LongComputation()
      {
          while(someCondition)
          {
              /* 做一系列的工作 */
       
              // 在這里暫停然后在下一幀繼續執行
              yield return null;
          }
      }

      協程是怎么工作的

      注意上邊的代碼示例,你會發現一個協程函數的返回值是IEnumerator,它是一個迭代器,你可以把它當成指向一個序列的某個節點的指針,它提供了兩個重要的接口,分別是Current(返回當前指向的元素)和MoveNext()(將指針向前移動一個單位,如果移動成功,則返回true)。IEnumerator是一個interface,所以你不用擔心的具體實現。

      通常,如果你想實現一個接口,你可以寫一個類,實現成員,等等。迭代器塊(iterator block)是一個方便的方式實現IEnumerator沒有任何麻煩-你只是遵循一些規則,并實現IEnumerator由編譯器自動生成。

      一個迭代器塊具備如下特征:

      1. 返回IEnumerator
      2. 使用yield關鍵字

      所以yield關鍵詞是干啥的?它聲明序列中的下一個值或者是一個無意義的值。如果使用yield x(x是指一個具體的對象或數值)的話,那么movenext返回為true并且current被賦值為x,如果使用yield break使得movenext()返回false。

      那么我舉例如下,這是一個迭代器塊:

      public void Consumer()
      {
          foreach(int i in Integers())
          {
              Console.WriteLine(i.ToString());
          }
      }
      
      public IEnumerable<int> Integers()
      {
          yield return 1;
          yield return 2;
          yield return 4;
          yield return 8;
          yield return 16;
          yield return 16777216;
      }

      注意上文在迭代的過程中,你會發現,在兩個yield之間的代碼只有執行完畢之后,才會執行下一個yield,在Unity中,我們正是利用了這一點,我們可以寫出下面這樣的代碼作為一個迭代器塊:

       

      IEnumerator TellMeASecret(){
        PlayAnimation("LeanInConspiratorially");
        while(playingAnimation)
          yield return null;
       
        Say("I stole the cookie from the cookie jar!");
        while(speaking)
          yield return null;
       
        PlayAnimation("LeanOutRelieved");
        while(playingAnimation)
          yield return null;
      }

      然后我們可以使用下文這樣的客戶代碼,來調用上文的程序,就可以實現延時的效果。

      IEnumerator e = TellMeASecret();
      while(e.MoveNext()) { 
          // do whatever you like
      }

      協程是如何實現延時的?

      如你所見,yield return返回的值并不一定是有意義的,如null,但是我們更感興趣的是,如何使用這個yield return的返回值來實現一些有趣的效果。

      Unity聲明了YieldInstruction來作為所有返回值的基類,并且提供了幾種常用的繼承類,如WaitForSeconds(暫停一段時間繼續執行),WaitForEndOfFrame(暫停到下一幀繼續執行)等等。更巧妙的是yield 也可以返回一個Coroutine真身,Coroutine A返回一個Coroutine B本身的時候,即等到B做完了再執行A。下面有詳細說明:

       

      Normal coroutine updates are run after the Update function returns. A coroutine is a function that can suspend its execution (yield) until the given YieldInstruction finishes. Different uses of Coroutines:
      
      yield; The coroutine will continue after all Update functions have been called on the next frame.
      yield WaitForSeconds(2); Continue after a specified time delay, after all Update functions have been called for the frame
      yield WaitForFixedUpdate(); Continue after all FixedUpdate has been called on all scripts
      yield WWW Continue after a WWW download has completed.
      yield StartCoroutine(MyFunc); Chains the coroutine, and will wait for the MyFunc coroutine to complete first.

       

      實現延時的關鍵代碼是在StartCoroutine里面,以為筆者也沒有見過Unity的源碼,那么我只能猜想StartCoroutine這個函數的內部構造應該是這樣的:
      List<IEnumerator> unblockedCoroutines;
      List<IEnumerator> shouldRunNextFrame;
      List<IEnumerator> shouldRunAtEndOfFrame;
      SortedList<float, IEnumerator> shouldRunAfterTimes;
       
      foreach(IEnumerator coroutine in unblockedCoroutines){
          if(!coroutine.MoveNext())
              // This coroutine has finished
              continue;
       
          if(!coroutine.Current is YieldInstruction)
          {
              // This coroutine yielded null, or some other value we don't understand; run it next frame.
              shouldRunNextFrame.Add(coroutine);
              continue;
          }
       
          if(coroutine.Current is WaitForSeconds)
          {
              WaitForSeconds wait = (WaitForSeconds)coroutine.Current;
              shouldRunAfterTimes.Add(Time.time + wait.duration, coroutine);
          }
          else if(coroutine.Current is WaitForEndOfFrame)
          {
              shouldRunAtEndOfFrame.Add(coroutine);
          }
          else /* similar stuff for other YieldInstruction subtypes */}
       
      unblockedCoroutines = shouldRunNextFrame;

      當然了,我們還可以為YieldInstruction添加各種的子類,比如一個很容易想到的就是yield return new WaitForNotification(“GameOver”)來等待某個消息的觸發,關于Unity的消息機制可以參考這篇文章:【Unity3D技巧】在Unity中使用事件/委托機制(event/delegate)進行GameObject之間的通信 (二) : 引入中間層NotificationCenter

      還有些更好玩的?

      第一個有趣的地方是,yield return可以返回任意YieldInstruction,所以我們可以在這里加上一些條件判斷:

      YieldInstruction y;
       
      if(something)
       y = null;else if(somethingElse)
       y = new WaitForEndOfFrame();else
       y = new WaitForSeconds(1.0f);
       
      yield return y;

      第二個,由于一個協程只是一個迭代器塊而已,所以你也可以自己遍歷它,這在一些場景下很有用,例如在對協程是否執行加上條件判斷的時候:

      IEnumerator DoSomething(){
        /* ... */}
       
      IEnumerator DoSomethingUnlessInterrupted(){
        IEnumerator e = DoSomething();
        bool interrupted = false;
        while(!interrupted)
        {
          e.MoveNext();
          yield return e.Current;
          interrupted = HasBeenInterrupted();
        }}

      第三個,由于協程可以yield協程,所以我們可以自己創建一個協程函數,如下:

      IEnumerator UntilTrueCoroutine(Func fn){
         while(!fn()) yield return null;}
       
      Coroutine UntilTrue(Func fn){
        return StartCoroutine(UntilTrueCoroutine(fn));}
       
      IEnumerator SomeTask(){
        /* ... */
        yield return UntilTrue(() => _lives < 3);
        /* ... */}

      總結

      這篇文章大部分是我從這篇博客里面翻譯過來的,這是我見過最棒的一篇關于Coroutine的博客,所以我把它翻譯過來與大家分享,希望你能喜歡。

      posted @ 2014-06-17 14:47  月出漸分明  閱讀(20368)  評論(21)    收藏  舉報
      主站蜘蛛池模板: 人妻精品中文字幕av| 少妇人妻偷人精品视蜜桃| 国产睡熟迷奷系列网站| 成人午夜在线观看刺激| 欧美色丁香| 精品亚洲欧美中文字幕在线看| 中文字幕一区二区久久综合| 国产精品亚洲аv无码播放| 人人做人人澡人人人爽| 亚洲精品一区二区三区中文字幕| 国产乱码精品一区二区麻豆| 国产精品视频一区不卡| 久久综合久中文字幕青草| 中文精品无码中文字幕无码专区| 欧美亚洲日本国产综合在线美利坚| 绯色蜜臀av一区二区不卡| 野花在线观看免费观看高清| 元码人妻精品一区二区三区9| 国产精品成人观看视频国产奇米| 亚洲国产亚洲国产路线久久| 国产午夜精品福利91| 中文字幕无码av不卡一区| 东京热高清无码精品| 欧美日本中文| 亚洲日韩成人无码不卡网站| 欧美不卡无线在线一二三区观| 渭南市| 日韩一区二区三区高清视频| 2020年最新国产精品正在播放| 开心一区二区三区激情| 亚洲国产亚洲国产路线久久| 免费人成再在线观看视频| 国产成人精品成人a在线观看| 色偷偷www.8888在线观看| 久久久久国产精品人妻| 日本一本无道码日韩精品| 人妻日韩人妻中文字幕| 久久SE精品一区精品二区| 亚洲欧美日韩精品久久亚洲区| 国产成人啪精品午夜网站| 国产精品日韩专区第一页|