乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      簡(jiǎn)要分析unity3d中剪不斷理還亂的yield

       勤奮不止 2016-04-03

        在學(xué)習(xí)unity3d的時(shí)候很容易看到下面這個(gè)例子:

      復(fù)制代碼
      1 void Start () {
      2     StartCoroutine(Destroy());
      3 }
      4 
      5 IEnumerator Destroy(){
      6     yield return WaitForSeconds(3.0f);
      7     Destroy(gameObject);
      8 }
      復(fù)制代碼

        這個(gè)函數(shù)干的事情很簡(jiǎn)單:調(diào)用StartCoroutine函數(shù)開(kāi)啟協(xié)程,yield等待一段時(shí)間后,銷毀這個(gè)對(duì)象;由于是協(xié)程在等待,所以不影響主線程操作。一般來(lái)說(shuō),看到這里的時(shí)候都還不會(huì)暈,yield就是延時(shí)一段時(shí)間以后繼續(xù)往下執(zhí)行唄,恩,學(xué)會(huì)了,看著還蠻好用的。

      ====================================================分割線====================================================

        當(dāng)然,yield能干的事情遠(yuǎn)遠(yuǎn)不止這種簡(jiǎn)單的特定時(shí)間的延時(shí),例如可以在下一幀繼續(xù)執(zhí)行這段代碼(yield return null),可以在下一次執(zhí)行FixedUpdate的時(shí)候繼續(xù)執(zhí)行這段代碼(yield new WaitForFixedUpdate ();),可以讓異步操作(如LoadLevelAsync)在完成以后繼續(xù)執(zhí)行,可以……可以讓你看到頭暈。

        unity3d官方對(duì)于協(xié)程的解釋是:一個(gè)協(xié)同程序在執(zhí)行過(guò)程中,可以在任意位置使用yield語(yǔ)句。yield的返回值控制何時(shí)恢復(fù)協(xié)同程序向下執(zhí)行。協(xié)同程序在對(duì)象自有幀執(zhí)行過(guò)程中堪稱優(yōu)秀。協(xié)同程序在性能上沒(méi)有更多的開(kāi)銷。StartCoroutine函數(shù)是立刻返回的,但是yield可以延遲結(jié)果。直到協(xié)同程序執(zhí)行完畢。(原文:The execution of a coroutine can be paused at any point using the yield statement. The yield return value specifies when the coroutine is resumed. Coroutines are excellent when modelling behaviour over several frames. Coroutines have virtually no performance overhead. StartCoroutine function always returns immediately, however you can yield the result. This will wait until the coroutine has finished execution.)

        如果只是認(rèn)為yield用于延時(shí),那么可以用的很順暢;但是若看到y(tǒng)ield還有這么多功能,目測(cè)瞬間就凌亂了,更不要說(shuō)活學(xué)活用了。不過(guò),如果從原理上進(jìn)行理解,就很容易理清yield的各種功能了。

      C#中的yield

      復(fù)制代碼
       1 public static IEnumerable<int> GenerateFibonacci()
       2 {
       3     yield return 0;
       4     yield return 1;
       5 
       6     int last0 = 0, last1 = 1, current;
       7 
       8     while (true)
       9     {
      10         current = last0 + last1;
      11         yield return current;
      12 
      13         last0 = last1;
      14         last1 = current;
      15     }
      16 }
      復(fù)制代碼

        yield return的作用是在執(zhí)行到這行代碼之后,將控制權(quán)立即交還給外部。yield return之后的代碼會(huì)在外部代碼再次調(diào)用MoveNext時(shí)才會(huì)執(zhí)行,直到下一個(gè)yield return——或是迭代結(jié)束。雖然上面的代碼看似有個(gè)死循環(huán),但事實(shí)上在循環(huán)內(nèi)部我們始終會(huì)把控制權(quán)交還給外部,這就由外部來(lái)決定何時(shí)中止這次迭代。有了yield之后,我們便可以利用“死循環(huán)”,我們可以寫出含義明確的“無(wú)限的”斐波那契數(shù)列。轉(zhuǎn)自老趙的博客。

        IEnumerable與IEnumerator的區(qū)別比較小,在unity3d中只用到IEnumerator,功能和IEnumerable類似。至于他們的區(qū)別是什么,網(wǎng)上搜了半天,還是模糊不清,有童鞋能解釋清楚的請(qǐng)留言。不過(guò)對(duì)于這段代碼對(duì)于unity3d中yield的理解已經(jīng)足夠了。

      游戲中需要使用yield的場(chǎng)景

        既然要使用yield,就得給個(gè)理由吧,不能為了使用yield而使用yield。那么先來(lái)看看游戲中可以用得到y(tǒng)ield的場(chǎng)景:

      • 游戲結(jié)算分?jǐn)?shù)時(shí),分?jǐn)?shù)從0逐漸上漲,而不是直接顯示最終分?jǐn)?shù)

      • 人物對(duì)話時(shí),文字一個(gè)一個(gè)很快的出現(xiàn),而不是一下突然出現(xiàn)
      • 10、9、8……0的倒計(jì)時(shí)

      • 某些游戲(如拳皇)掉血時(shí)血條UI逐漸減少,而不是突然降低到當(dāng)前血量

      …………………………

      unity3d中yield應(yīng)用舉例

        首先是官網(wǎng)的一段代碼:

      復(fù)制代碼
       1 using UnityEngine;
       2 using System.Collections;
       3 
       4 public class yield1 : MonoBehaviour {
       5 
       6     IEnumerator Do() {
       7         print("Do now");
       8         yield return new WaitForSeconds(2);
       9         print("Do 2 seconds later");
      10     }
      11     void Awake() {
      12         StartCoroutine(Do());
      13         print("This is printed immediately");
      14     }
      15 
      16     // Use this for initialization
      17     void Start () {
      18     
      19     }
      20     
      21     // Update is called once per frame
      22     void Update () {
      23     
      24     }
      25 }
      復(fù)制代碼

        這個(gè)例子將執(zhí)行Do,但是Do函數(shù)之后的print指令會(huì)立刻執(zhí)行。這個(gè)例子沒(méi)有什么實(shí)際意義,只是為了驗(yàn)證一下yield確實(shí)是有延時(shí)的。

        下面來(lái)看看兩段顯示人物對(duì)話的代碼(對(duì)話隨便復(fù)制了一段內(nèi)容),功能是一樣的,但是方法不一樣:

      復(fù)制代碼
       1 using UnityEngine;
       2 using System.Collections;
       3 
       4 public class dialog_easy : MonoBehaviour {
       5     public string dialogStr = "yield return的作用是在執(zhí)行到這行代碼之后,將控制權(quán)立即交還給外部。yield return之后的代碼會(huì)在外部代碼再次調(diào)用MoveNext時(shí)才會(huì)執(zhí)行,直到下一個(gè)yield return——或是迭代結(jié)束。雖然上面的代碼看似有個(gè)死循環(huán),但事實(shí)上在循環(huán)內(nèi)部我們始終會(huì)把控制權(quán)交還給外部,這就由外部來(lái)決定何時(shí)中止這次迭代。有了yield之后,我們便可以利用“死循環(huán)”,我們可以寫出含義明確的“無(wú)限的”斐波那契數(shù)列。";
       6     public float speed = 5.0f;
       7 
       8     private float timeSum = 0.0f;
       9     private bool isShowing = false;
      10     // Use this for initialization
      11     void Start () {
      12         ShowDialog();
      13     }
      14     
      15     // Update is called once per frame
      16     void Update () {
      17         if(isShowing){
      18             timeSum += speed * Time.deltaTime;
      19             guiText.text = dialogStr.Substring(0, System.Convert.ToInt32(timeSum));
      20 
      21             if(guiText.text.Length == dialogStr.Length)
      22                 isShowing = false;
      23         }
      24     }
      25 
      26     void ShowDialog(){
      27         isShowing = true;
      28         timeSum = 0.0f;
      29     }
      30 }
      復(fù)制代碼

        這段代碼實(shí)現(xiàn)了在GUIText中逐漸顯示一個(gè)字符串的功能,速度為每秒5個(gè)字,這也是新手常用的方式。如果只是簡(jiǎn)單的在GUIText中顯示一段文字,ShowDialog()函數(shù)可以做的很好;但是如果要讓字一個(gè)一個(gè)蹦出來(lái),就需要借助游戲的循環(huán)了,最簡(jiǎn)單的方式就是在Update()中更新GUIText。

        從功能角度看,這段代碼完全沒(méi)有問(wèn)題;但是從代碼封裝性的角度來(lái)看,這是一段很惡心的代碼,因?yàn)楸緫?yīng)由ShowDialog()完成的功能放到了Update()中,并且在類中還有兩個(gè)private變量為這個(gè)功能服務(wù)。如果將來(lái)要修改或者刪除這個(gè)功能,需要在ShowDialog()和Update()中修改,并且還可能修改那兩個(gè)private變量?,F(xiàn)在代碼比較簡(jiǎn)單,感覺(jué)還不算太壞,一旦Update()中再來(lái)兩個(gè)類似的的功能,估計(jì)寫完代碼一段時(shí)間之后自己修改都費(fèi)勁。

        如果通過(guò)yield return null實(shí)現(xiàn)幀與幀之間的同步,則代碼優(yōu)雅了很多:

      復(fù)制代碼
       1 using UnityEngine;
       2 using System.Collections;
       3 
       4 public class dialog_yield : MonoBehaviour {
       5     public string dialogStr = "yield return的作用是在執(zhí)行到這行代碼之后,將控制權(quán)立即交還給外部。yield return之后的代碼會(huì)在外部代碼再次調(diào)用MoveNext時(shí)才會(huì)執(zhí)行,直到下一個(gè)yield return——或是迭代結(jié)束。雖然上面的代碼看似有個(gè)死循環(huán),但事實(shí)上在循環(huán)內(nèi)部我們始終會(huì)把控制權(quán)交還給外部,這就由外部來(lái)決定何時(shí)中止這次迭代。有了yield之后,我們便可以利用“死循環(huán)”,我們可以寫出含義明確的“無(wú)限的”斐波那契數(shù)列。";
       6     public float speed = 5.0f;
       7 
       8     // Use this for initialization
       9     void Start () {
      10         StartCoroutine(ShowDialog());
      11     }
      12     
      13     // Update is called once per frame
      14     void Update () {
      15     }
      16     
      17     IEnumerator ShowDialog(){
      18         float timeSum = 0.0f;
      19         while(guiText.text.Length < dialogStr.Length){
      20             timeSum += speed * Time.deltaTime;
      21             guiText.text = dialogStr.Substring(0, System.Convert.ToInt32(timeSum));
      22             yield return null;
      23         }
      24     }
      25 }
      復(fù)制代碼

        相關(guān)代碼都被封裝到了ShowDialog()中,這么一來(lái),不論是要增加、修改或刪除功能,都變得容易了很多。

        根據(jù)官網(wǎng)手冊(cè)的描述,yield return null可以讓這段代碼在下一幀繼續(xù)執(zhí)行。在ShowDialog()中,每次更新文字以后yield return null,直到這段文字被完整顯示??吹竭@里,可能有童鞋不解:

      • 為什么在協(xié)程中也可以用Time.deltaTime?
      • 協(xié)程中的Time.deltaTime和Update()中的一樣嗎?
      • 這樣使用協(xié)程,會(huì)不會(huì)出現(xiàn)與主線程訪問(wèn)共享資源沖突的問(wèn)題?(線程的同步與互斥問(wèn)題)
      • yield return null太神奇了,為什么會(huì)在下一幀繼續(xù)執(zhí)行這個(gè)函數(shù)?
      • 這段代碼是不是相當(dāng)于為ShowDialog()構(gòu)造了一個(gè)自己的Update()?

        要解釋這些問(wèn)題,先看看unity3d中的協(xié)程是怎么運(yùn)行的吧。

      協(xié)程原理分析

        本段內(nèi)容轉(zhuǎn)自這篇博客,想看的童鞋自己點(diǎn)擊。

       

        首先,請(qǐng)你牢記:協(xié)程不是線程,也不是異步執(zhí)行的。協(xié)程和 MonoBehaviour 的 Update函數(shù)一樣也是在MainThread中執(zhí)行的。使用協(xié)程你不用考慮同步和鎖的問(wèn)題。

        UnityGems.com給出了協(xié)程的定義:

        A coroutine is a function that is executed partially and, presuming suitable conditions are met, will be resumed at some point in the future until its work is done.

        協(xié)程是一個(gè)分部執(zhí)行,遇到條件(yield return 語(yǔ)句)會(huì)掛起,直到條件滿足才會(huì)被喚醒繼續(xù)執(zhí)行后面的代碼。Unity在每一幀(Frame)都會(huì)去處理對(duì)象上的協(xié)程。Unity主要是在Update后去處理協(xié)程(檢查協(xié)程的條件是否滿足):

        從上圖的剖析就明白,協(xié)程跟Update()其實(shí)一樣的,都是Unity每幀對(duì)會(huì)去處理的函數(shù)(如果有的話)。如果MonoBehaviour 是處于激活(active)狀態(tài)的而且yield的條件滿足,就會(huì)協(xié)程方法的后面代碼。還可以發(fā)現(xiàn):如果在一個(gè)對(duì)象的前期調(diào)用協(xié)程,協(xié)程會(huì)立即運(yùn)行到第一個(gè) yield return 語(yǔ)句處,如果是 yield return null ,就會(huì)在同一幀再次被喚醒。如果沒(méi)有考慮這個(gè)細(xì)節(jié)就會(huì)出現(xiàn)一些奇怪的問(wèn)題。
        注:圖和結(jié)論都是從UnityGems.com 上得來(lái)的,經(jīng)過(guò)驗(yàn)證發(fā)現(xiàn)與實(shí)際不符,D.S.Qiu用的是Unity 4.3.4f1 進(jìn)行測(cè)試的。經(jīng)過(guò)測(cè)試驗(yàn)證,協(xié)程至少是每幀的LateUpdate()后去運(yùn)行。

        協(xié)程其實(shí)就是一個(gè)IEnumerator(迭代器),IEnumerator 接口有兩個(gè)方法 Current 和 MoveNext() ,迭代器方法運(yùn)行到 yield return 語(yǔ)句時(shí),會(huì)返回一個(gè)expression表達(dá)式并保留當(dāng)前在代碼中的位置。 當(dāng)下次調(diào)用迭代器函數(shù)時(shí)執(zhí)行從該位置重新啟動(dòng)。unity3d在每幀做的工作就是:調(diào)用協(xié)程(迭代器)MoveNext() 方法,如果返回 true ,就從當(dāng)前位置繼續(xù)往下執(zhí)行。詳情見(jiàn)這篇博客。

       

        如果理解了這張圖,之前顯示人物對(duì)話的功能最后提到的那些疑惑也就很容易理解了:

      • 協(xié)程和Update()一樣更新,自然可以使用Time.deltaTime了,而且這個(gè)Time.deltaTime和在Update()當(dāng)中使用是一樣的效果(使用yield return null的情況下)
      • 協(xié)程并不是多線程,它和Update()一樣是在主線程中執(zhí)行的,所以不需要處理線程的同步與互斥問(wèn)題
      • yield return null其實(shí)沒(méi)什么神奇的,只是unity3d封裝以后,這個(gè)協(xié)程在下一幀就被自動(dòng)調(diào)用了
      • 可以理解為ShowDialog()構(gòu)造了一個(gè)自己的Update(),因?yàn)閥ield return null讓這個(gè)函數(shù)每幀都被調(diào)用了

        本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
        轉(zhuǎn)藏 分享 獻(xiàn)花(0

        0條評(píng)論

        發(fā)表

        請(qǐng)遵守用戶 評(píng)論公約

        類似文章 更多