使用Unity的都知道Unity提供了协程的方式,使用协程能够使异步回调更加简洁方便。但是Unity的协程只有继承MonoBehaviour才能开启,也就是说,如果你想用,则必须存在一个全局类,其挂在GameObject上,并且为了让所有地方都能使用,这个GameObject不可以消除。
这个限制虽然不会带来太大麻烦,但只要了解Unity协程的实现原理,完全可以仿照Unity实现一套自己的协程管理类。
废话少说,直接上代码。
using System.Collections;
using System.Collections.Generic;
namespace Com.Coroutine
{
public class Coroutine
{
internal IEnumerator m_Routine;
internal IEnumerator Routine
{
get { return m_Routine; }
}
internal Coroutine()
{
}
internal Coroutine(IEnumerator routine)
{
this.m_Routine = routine;
}
internal bool MoveNext()
{
var routine = m_Routine.Current as Coroutine;
if (routine != null)
{
if (routine.MoveNext())
{
return true;
}
else if (m_Routine.MoveNext())
{
return true;
}
else
{
return false;
}
}
else if (m_Routine.MoveNext())
{
return true;
}
else
{
return false;
}
}
}
// use this as a template for functions like WaitForSeconds()
public class WaitForCount : Coroutine
{
int count = 0;
public WaitForCount(int count)
{
this.count = count;
this.m_Routine = Count();
}
IEnumerator Count()
{
while (--count >= 0)
{
System.Console.WriteLine(count);
yield return true;
}
}
}
// use this as the base class for enabled coroutines
public class CoroutineManager
{
internal List<Coroutine> m_Coroutines = new List<Coroutine>();
// just like Unity's MonoBehaviour.StartCoroutine
public Coroutine StartCoroutine(IEnumerator routine)
{
var coroutine = new Coroutine(routine);
m_Coroutines.Add(coroutine);
return coroutine;
}
// call this every frame
public void ProcessCoroutines()
{
for (int i = 0; i < m_Coroutines.Count; )
{
var coroutine = m_Coroutines[i];
if (coroutine.MoveNext())
{
++i;
}
else if (m_Coroutines.Count > 1)
{
m_Coroutines[i] = m_Coroutines[m_Coroutines.Count - 1];
m_Coroutines.RemoveAt(m_Coroutines.Count - 1);
}
else
{
m_Coroutines.Clear();
break;
}
}
}
}
}
调用可以按照下面来进行。
public IEnumerator StepBy () {
Console.WriteLine("Hello from coroutine");
yield return true;
Console.WriteLine("This is another line");
yield return true;
Console.WriteLine("And the last line");
yield return new WaitForCount(5);
Console.WriteLine("Now I'm done");
}