协程本质上也是一个迭代器,为了记录一下今天对二者的学习和巩固记录一下今天的知识点。 首先了解一下迭代器的知识: 迭代器模式(Iterator),提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示。C#中使用IEnumerator接口实现,Java中使用Iterator接口实现,其中原理都差不多,下面我就用C#代码来演示下迭代器的实现。
namespace System.Collections
{
public interface IEnumerator
{
object Current { get; }
bool MoveNext();
void Reset();
}
}
Current 是作为返回的正确对象,获取当前下标所指向的对象。如我们foreach时遍历返回的那个正确对象 MoveNext 即是遍历该对象时,检查并确认下一个位置的对象是否为空,如foreach时编译器来判断是否遍历到最后一个 Reset 是我们在多次使用迭代器时下标如果不更新为第一个的话,那么Current就会一直是上一次操作读取下标的对象,如果我们需要重新遍历则该下标之前的数据就找不到。所以Reset则是更新下标为第一个。
迭代器的一大特点就是可以数据封装起来,无论什么类型的数据都可以放到一个迭代器里,方便管理和读取。这样我们就可以根据自己开发需要去创建自己的数据类型,再利用迭代器去管理和遍历获取。 简单的重写一下三个方法加深理解
Current(){
Get{
return List[index];
}
}
MoveNext(){
If(index<list.Length)
{
Return true;
}
Else
Return false
}
Reset()
{
Current = null;
Index = 0 ;
}
接下来就是协程的部分知识和代码 Unity在每一帧(Frame)都回去处理对象上的协程。Unity主要是在Update后去处理协程(检查协程的条件是否满足),但也有些特例 协程与Update一样,每帧处理,如果yield条件满足,就会执行协程方法后面的代码 协程不是异步执行,他是串行逻辑。不需要考虑同步和锁的问题 咱们先用一段代码来显示一下,Monobehaviour中代码的执行顺序和协程运行时的时间记录以及逻辑顺序输出
public class xiecheng : MonoBehaviour
{
private bool isStartCall = false;
private bool isUpdateCall = false;
private bool isLateUpdateCall = false;
void Start()
{
if (!isStartCall)
{
Debug.Log("Start Call Begin: " + Time.time);
StartCoroutine(StartCoutine());
Debug.Log("Start Call End: " + Time.time);
isStartCall = true;
}
}
IEnumerator StartCoutine()
{
Debug.Log("This is Start Coroutine Call Before: " + Time.time );
yield return null;
Debug.Log("This is Start Coroutine Call End: " + Time.time);
}
void Update()
{
if (!isUpdateCall)
{
Debug.Log("Update Call Begin: " + Time.time);
StartCoroutine(UpdateCoutine());
Debug.Log("Update Call End: " + Time.time);
isUpdateCall = true;
}
}
IEnumerator UpdateCoutine()
{
Debug.Log("This is Update Corourine Call Before: " + Time.time);
yield return new WaitForSeconds(1f);
Debug.Log("This is Update Coroutine Call End: " + Time.time);
}
private void LateUpdate()
{
if(!isLateUpdateCall)
{
Debug.Log("LateUpdate Call Begin: " + Time.time);
StartCoroutine(LateCoutine());
Debug.Log("LateUpdate Call End: " + Time.time);
isLateUpdateCall = true;
}
}
IEnumerator LateCoutine()
{
Debug.Log("This is Late Coroutine Call Before: " + Time.time);
yield return new WaitForSeconds(1f);
Debug.Log("This is Late Coroutine Call End: " + Time.time);
}
}
大家可以先在脑子里或者记一下自己觉得应该执行输出的顺序和时间 我们接下来截取一下unity中的控制台输出 结合代码来理解,我们可以看到所有的协程在进入时都是跟着函数顺序按照正常逻辑执行第一句,但是yield之后就暂时不执行了,在第一个yield之前都是在第一帧中把所有的逻辑执行完,而第10行的debug则是yield return null即一帧之后再执行,我们也可以清晰的看到0.02秒之后执行,即一帧之后。而11与12行的debug则都是在1秒之后执行。 对应了yield return new waitforseconds(1f) 从这几个输出就很清晰地能够看出协程中yield的运行原理了。同时也能够巩固一下monobehaviour的函数执行顺序图。 协程还有一个特色是,他虽然写在Monobehaviour中,但是他的执行地位是和Monobehaviour同层级的,它并不会受到Monobehaviour的状态enable而影响,但它和Monobehaviour一样受挂载的游戏对象gameobject控制setactive() 再加一个简单的协程实现计时器功能的巩固吧
IEnumerator timer()
{
Debug.Log(timernum);
yield return new WaitForSeconds(1f);
timernum -= 1;
if (timernum < 0)
StopCoroutine(timer());
else
StartCoroutine(timer());
}
|