Unity3D 協(xié)程管理

Unity里面的協(xié)程好用,但總是在如何關閉指定協(xié)程,尤其是關閉帶參數的協(xié)程的問題上困惑不已。
在本文,筆者帶你用最簡單的方式(2個方案)管理這些運行中的協(xié)程,做到點對點關閉,即便是帶參數協(xié)程,也能點對點關閉。

思路:

  1. 要每一個協(xié)程都在開啟后能再次拿到,并且具備辨識度,就需要緩存引用并為其配特定名稱(key),構成 一個 KeyValuePair ,一個個 KeyValuePair 的集合不就是字典?
  2. 所以想要管理協(xié)程,做好他們的增刪改查,這兒引入 Dictionary 保存協(xié)程 Coroutine 的引用 或者 干脆保存這個迭代器 IEnumerator ,如果想要關閉指定的協(xié)程,只需從字典里面拿到這些個引用,然后使用 StopCoroutine API 即可。
  3. 如果字典里面存的是 迭代器 IEnumerator , 那樣似乎更加棒棒,嗯還可以 抽取指定協(xié)程 使用 StartCoroutine API 開啟。

方案一:

執(zhí)行 StartCoroutine,將返回這個協(xié)同程序 Coroutine 的引用,將這個引用存到字典,需要的時候直接StopCoroutine(這個引用);

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TestIEnumlator01 : MonoBehaviour {
    //方案一,執(zhí)行 StartCoroutine,將返回 Coroutine 引用,需要的時候直接 StopCoroutine(這個引用);
    Dictionary<string, Coroutine> CoroutineDic = new Dictionary<string, Coroutine>();
      //消息輸出字典,用于Debug
    Dictionary<string, string> MessageDic = new Dictionary<string, string>() {
        { "a", "Test04" },
        { "s", "Test05" },
        { "d", "Test06" }
    };
    void Start () {
        //模擬運行協(xié)程
        CoroutineDic.Add("a", StartCoroutine(Test04()));
        CoroutineDic.Add("s", StartCoroutine(Test05(1f)));
        CoroutineDic.Add("d", StartCoroutine(Test06("測試", 1f)));
    }

    private void Update()
    {
        if (Input.anyKeyDown)
        {
            string name = Input.inputString;
            if (CoroutineDic.ContainsKey(name))
            {
                StopCoroutine(CoroutineDic[name]);
                CoroutineDic.Remove(name);
                Debug.Log(string.Format("我按下了{0}鍵,字典還有{1}個對象,注意看協(xié)程{2}還有輸出不?", name, CoroutineDic.Count, MessageDic[name]));
            }
        }
    }
    //以下協(xié)程均為測試用,為了便于觀察,均為死循環(huán)且定時均為1秒
    IEnumerator Test04()//不帶參數協(xié)程
    {
        while (true)
        {
            yield return new WaitForSeconds(1f);
            Debug.Log("我是Test04:"+Time.realtimeSinceStartup);
        }
    }
    IEnumerator Test05(float _DelayTime)//帶一個參數的協(xié)程
    {
        while (true)
        {
            yield return new WaitForSeconds(_DelayTime);
            Debug.Log("我是Test05:" + Time.realtimeSinceStartup);
        }
    }
    IEnumerator Test06(string msg,float delayTime)//帶兩個參數的協(xié)程
    {
        while (true)
        {
            yield return new WaitForSeconds(delayTime);
            Debug.Log("我是Test06:"+ Time.realtimeSinceStartup + "傳入字符串為:" + msg);
        }
    }
}

方案二:

將迭代器 IEnumerator(表象是存的這個協(xié)程方法名)先添加到字典,給它指定一個特定的名字, 想拿到指定的迭代器 IEnumerator,不就是 Dictionary.TryGetValue 么,有了迭代器的在手, 就可以隨意開啟 / 關閉 指定的協(xié)程啦;

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TestIEnumlator02 : MonoBehaviour {
    //方案二,聲明字典,緩存迭代器,制作映射表。
    Dictionary<string, IEnumerator> IEnumeratorDic = new Dictionary<string, IEnumerator>();
    //消息輸出字典,用于Debug
    Dictionary<string, string> MessageDic = new Dictionary<string, string>() {
        { "q", "Test01" },
        { "w", "Test02" },
        { "e", "Test03" }
    };
    void Start () {
        //添加協(xié)程引用,并為其配置一個鍵 Key,方便后續(xù)增刪改查的操作
        IEnumeratorDic.Add("q",Test01());
        IEnumeratorDic.Add("w",Test02(1f));
        IEnumeratorDic.Add("e",Test03("測試",1f));
        //依次執(zhí)行這些協(xié)程
        foreach (var item in IEnumeratorDic)
        {
            StartCoroutine(item.Value);
        }
    }

    private void Update()
    {
        if (Input.anyKeyDown)
        {
            string name=Input.inputString;
            if (IEnumeratorDic.ContainsKey(name))
            {
                StopCoroutine(IEnumeratorDic[name]);
                IEnumeratorDic.Remove(name);
                Debug.Log(string.Format("我按下了{0}鍵,字典還有{1}個對象,注意看協(xié)程{2}還有輸出不?", name, IEnumeratorDic.Count,MessageDic[name]));
            }
        }
    }

    //以下協(xié)程均為測試用,為了便于觀察,均為死循環(huán)且定時均為1秒
    IEnumerator Test01()//不帶參數協(xié)程
    {
        while (true)
        {
            yield return new WaitForSeconds(1f);
            Debug.Log("我是Test01:" + Time.realtimeSinceStartup);
        }
    }
    IEnumerator Test02(float _DelayTime)//帶一個參數的協(xié)程
    {
        while (true)
        {
            yield return new WaitForSeconds(_DelayTime);
            Debug.Log("我是Test02:" + Time.realtimeSinceStartup);
        }
    }
    IEnumerator Test03(string msg, float delayTime)//帶兩個參數的協(xié)程
    {
        while (true)
        {
            yield return new WaitForSeconds(delayTime);
            Debug.Log("我是Test03:" + Time.realtimeSinceStartup + "傳入字符串為:" + msg);
        }
    }
}

動畫:

效果演示

總結:

原來,協(xié)程也可以這么好用的,想執(zhí)行誰,想停止誰,不管你帶沒帶參數,都可以單獨停止,單獨啟動。
想想,以前為了關閉一個帶參數的協(xié)程,不得不執(zhí)行 StopAllCoroutines()也是醉了;
引入 Dictionary 等數據結構,為這些協(xié)程指定一個有意義的key, 增刪改查手到擒來,取用起來也就更方便啦!
嗯,多研讀API,有更多驚喜~

小知識:

  1. 如果一個類里面協(xié)程有多個重載,且均只有一個參數,可以用如下的API啟動。
StartCoroutine("FuncName",1);
StartCoroutine("FuncName","參數");
StartCoroutine("FuncName",gameObject);
  1. 像tips1那樣使用字符串來開啟協(xié)程,有一個好處:就是使用StopCoroutine("FuncName")能夠停止所有的運行在這個實例上的有著這個方法名的協(xié)程。

  2. 相應的,用StartCoroutine(FuncName(1))這樣開啟的協(xié)程,不管有無參數,StopCoroutine("FuncName")都是不湊效的。

  3. 將Coroutine所在的腳本設為enable = false并不能夠將Coroutine停止,因為它跟Monobehavior是同層級的。

  4. 啟動一個協(xié)程時緩存他Coroutine類型的返回值是一個很好的習慣,這樣在啟動這個協(xié)程前判null就能保證這個協(xié)程不被多次加載,形如:

private Coroutine cor=null;
private void Start()
 {
    if(cor==null)
    {
        cor=StartCoroutine(TestFunc);
    }else
     {
      Debug.Log("協(xié)程運行中");
    }
 }
  1. 苦惱協(xié)程沒有返回值 ,ref / out 也不行? 給個 Action 或者 Func 做參數就很巴適~ 我的這篇文字:Unity3D IEnumerator 做一個通用的定時器 就是這個理念了。

擴展閱讀

基于協(xié)程的TaskManager
協(xié)程管理CoroutineManager - 筆者整理的

標簽:unity 3d,IEnumerator 協(xié)程,StartCoroutine,StopCoroutine關閉協(xié)程,關閉指定協(xié)程,關閉指定的帶參數協(xié)程

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容