1. 程式人生 > >Unity--- 簡單模仿一下原生協程的實現

Unity--- 簡單模仿一下原生協程的實現

先來看下測試程式碼 ...

 void Start()
    {
        CoroutineManager.Manager.StartCorouine(testWaitTime(1));
        CoroutineManager.Manager.StartCorouine(testWaitTime(2));
        //CoroutineManager.Manager.StartCorouine(testWaitNull()); //支援null
        //CoroutineManager.Manager.StartCorouine(testWaitCoroutine());//支援等待其他協程結束
    }
    void Update()
    {
        CoroutineManager.Manager.Update();
    }
    IEnumerator testWaitTime(int index)
    {
        print(index + " start wait...");
        yield return new MyWaitForSecond(2);
        print(index + " 2s after");
        yield return new MyWaitForSecond(2);
        print(index + " 4s after");
        print(index + " end");

    }
   
    IEnumerator testWaitNull()
    {
        for (int i = 0; i < 100; i++)
        {
            print(i);
            yield return null;
        }
    }

    IEnumerator testWaitCoroutine()
    {
        yield return CoroutineManager.Manager.StartCorouine(testWaitTime2());
        print("cool");
    }
    IEnumerator testWaitTime2()
    {
        yield return new MyWaitForSecond(3);
    }

可以看到,這個排程器可以像用waitForSendond那樣 排程協程的執行。另外,還支援null和等待其他協程結束。

下面上實現

public abstract class MyYieldInstruction
{
    public abstract bool MoveNext();
}
public class MyWaitForSecond : MyYieldInstruction
{
    private float time;
    public MyWaitForSecond(float t) { time = t; }

    public override bool MoveNext()
    {
        return (time -= Time.deltaTime) > 0f;
    }
}
public class MyCoroutine : MyYieldInstruction//這個用來支援等待其他協程的協程 同時考慮可以把諸如停止標記之類的和協程管理有關的東西放在這裡面,而字典裡就以這個類的物件作為key 
{
    public MyCoroutine(IEnumerator e) { mRefrence = e; }
    private IEnumerator mRefrence;

    public override bool MoveNext()
    {
        return CoroutineManager.Manager.HasCoroutine(mRefrence);
    }
}

public class CoroutineManager
{
    public static CoroutineManager Manager = new CoroutineManager();
    Dictionary<IEnumerator, MyYieldInstruction> mIterDic = new Dictionary<IEnumerator, MyYieldInstruction>();
    //提供給外界開啟協程的介面
    public MyCoroutine StartCorouine(IEnumerator iter)
    {
        //先執行到第一個yield處
        iter.MoveNext();
        //得到yield 返回的 指令物件
        MyYieldInstruction operation = (MyYieldInstruction)iter.Current;
        //加入字典 進行管理
        mIterDic.Add(iter, operation);
        return new MyCoroutine(iter);
    }
    public bool HasCoroutine(IEnumerator e)
    {
        MyYieldInstruction temp;
        return mIterDic.TryGetValue(e, out temp);
    }
    public void Update()
    {   //每幀進行遍歷
        GoOnForeach(mIterDic.GetEnumerator());
    }
    //可以刪除修改元素的遍歷
    void GoOnForeach(IEnumerator dicIter)
    {
        while (dicIter.MoveNext())
        {
            var dicIterItem = (KeyValuePair<IEnumerator, MyYieldInstruction>)dicIter.Current;
  
                                          //指令物件結束
            if (dicIterItem.Value==null||!dicIterItem.Value.MoveNext())
            {
                //如果這個協程後面還有yield  就替換新的指令物件
                if (dicIterItem.Key.MoveNext())
                {
                    if (dicIterItem.Value != null)
                    {
                        GoOnForeach(dicIter);
                        mIterDic[dicIterItem.Key] = (MyYieldInstruction)dicIterItem.Key.Current;
                        break;
                    }                  
                }
                //否則 刪除這個協程
                else
                {                  
                    GoOnForeach(dicIter);
                    mIterDic.Remove(dicIterItem.Key);
                    break;
                }
            }
        }
    }
}

註釋裡把要講的全講完了...

總之 為了搞懂協程的含義,這波不虧