注:需要了解C#的迭代器,不然很难理解。
之前面试有被问到unity协程的原理,以及撇开unity用纯C#去实现协程的方法。后来了解一下,确实可以的。趁这会有空,稍微总结一下。
还是结合代码说事吧:
/// <summary>
/// 等待接口
/// </summary>
public interface IWait
{
/// <summary>
/// 每帧检测是否等待结束
/// </summary>
/// <returns></returns>
bool Tick();
}
先定义一个等待接口,WaitForSeconds 和 WaitForFrames 实现接口的Tick()方法,每一帧调用Tick()方法检测是否等待结束
/// <summary>
/// 按秒等待
/// </summary>
public class WaitForSeconds:IWait
{
float _seconds = 0f; public WaitForSeconds(float seconds)
{
_seconds = seconds;
} public bool Tick()
{
_seconds -= Time.deltaTime;
return _seconds <= ;
}
}
/// <summary>
/// 按帧等待
/// </summary>
public class WaitForFrames:IWait
{
private int _frames = ;
public WaitForFrames(int frames)
{
_frames = frames;
} public bool Tick()
{
_frames -= ;
return _frames <= ;
}
}
定义 WaitForSeconds 和 WaitForFrames ,在构造函数初始化需要等待的时间/帧数
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks; public class CoroutineManager
{
private static CoroutineManager _instance = null;
public static CoroutineManager Instance
{
get
{
if (_instance == null)
{
_instance = new CoroutineManager();
}
return _instance;
}
} private LinkedList<IEnumerator> coroutineList = new LinkedList<IEnumerator>(); public void StartCoroutine(IEnumerator ie)
{
coroutineList.AddLast(ie);
} public void StopCoroutine(IEnumerator ie)
{
try
{
coroutineList.Remove(ie);
}
catch (Exception e) { Console.WriteLine(e.ToString()); }
} public void UpdateCoroutine()
{
var node = coroutineList.First;
while (node != null)
{
IEnumerator ie = node.Value;
bool ret = true;
if (ie.Current is IWait)
{
IWait wait = (IWait)ie.Current;
//检测等待条件,条件满足,跳到迭代器的下一元素 (IEnumerator方法里的下一个yield)
if (wait.Tick())
{
ret = ie.MoveNext();
}
}
else
{
ret = ie.MoveNext();
}
//迭代器没有下一个元素了,删除迭代器(IEnumerator方法执行结束)
if (!ret)
{
coroutineList.Remove(node);
}
//下一个迭代器
node = node.Next;
}
}
}
这一段代码是这里最重要的部分。这里用一个链表记录了添加的所有“协程”,并在UpdateCoroutine()方法的每一次执行都去遍历这些“协程”以及检测等待是否结束。
public class Time
{
//每帧时间(秒)
public static float deltaTime
{ get { return (float)deltaMilliseconds / ; } }
//每帧时间(毫秒)
public static int deltaMilliseconds
{ get { return ; }}
}
模拟一帧的时间。
运用测试:
public class Program
{
static void Main(string[] args)
{
var t1 = Test01();
var t2 = Test02();
CoroutineManager.Instance.StartCoroutine(t1);
CoroutineManager.Instance.StartCoroutine(t2); while (true)// 模拟update
{
Thread.Sleep(Time.deltaMilliseconds);
CoroutineManager.Instance.UpdateCoroutine();
}
} static IEnumerator Test01()
{
Console.WriteLine("start test 01");
yield return new WaitForSeconds();
Console.WriteLine("after 5 seconds");
yield return new WaitForSeconds();
Console.WriteLine("after 10 seconds");
} static IEnumerator Test02()
{
Console.WriteLine("start test 02");
yield return new WaitForFrames();
Console.WriteLine("after 500 frames");
}
}
测试结果:
用秒表掐了 一下,好像没什么毛病。