我们知道Unity中开启一个协程是这样的:
public class ExampleScript : MonoBehaviour
{
void Start()
{
StartCoroutine(DoSomThing());
}
public IEnumerator DoSomeThing()
{
Debug.Log("Start" + Time.realtimeSinceStartup);
yield return new WaitForSeconds(5);
Debug.Log("End" + Time.realtimeSinceStartup);
}
}
yield return的作用是在 return 时,保存当前函数的状态,下次调用时继续从当前位置处理。
void Test()
{
foreach (var item in GetNumbers())
Debug.Log("Main process. item = " + item);
}
static IEnumerable<int> GetNumbers()
{
// 以[0, 1, 2] 初始化数列 list
Debug.Log("Initializating...");
List<int> list = new List<int>();
for (int i = 0; i < 3; i++)
list.Add(i);
// 每次 yield return 返回一个list的数据
Debug.Log("Processing...");
for (int i = 0; i < list.Count; i++)
{
Debug.Log("Yield called.");
yield return list[i];
}
Debug.Log("Done.");
}
如上,GetNumbers方法内部的状态并不会因为yield return跳出这次循环而改变。输出的结果是这样的
Initializating...
Processing...
Yield called.
Main process. item = 0
Yield called.
Main process. item = 1
Yield called.
Main process. item = 2
Done.
Unity提供了一个接口给我们CustomYieldInstruction,官方的示例是这样的:
using System.Collections;
using UnityEngine;
public class ExampleScript : MonoBehaviour
{
void Update()
{
if (Input.GetMouseButtonUp(0))
{
Debug.Log("Left mouse button up");
StartCoroutine(waitForMouseDown());
}
}
public IEnumerator waitForMouseDown()
{
yield return new WaitForMouseDown();
Debug.Log("Right mouse button pressed");
}
}
using UnityEngine;
public class WaitForMouseDown : CustomYieldInstruction
{
public override bool keepWaiting
{
get
{
return !Input.GetMouseButtonDown(1);
}
}
public WaitForMouseDown()
{
Debug.Log("Waiting for Mouse right button down");
}
}
因此我们可以自定义一个WaitForSeconds,取名WaitTimer
public class WaitTimer : CustomYieldInstruction
{
private float timeLeft;
private float lastTime;
public override bool keepWaiting
{
get
{
timeLeft -= Time.deltaTime;
return timeLeft > 0;
}
}
public WaitTimer(float time)
{
Reset(time);
}
public void Reset(float time = 0)
{
if (time == 0)
{
timeLeft = lastTime;
}
else
{
lastTime = timeLeft = time;
}
}
}
如果你想对yieldInstruction做更复杂的操作,你也可以直接实现 System.Collections.IEnumerator
如果你想对yieldInstruction做更复杂的操作,你也可以直接实现 System.Collections.IEnumerator
public class WaitTimerV2 : IEnumerator
{
private float timeLeft;
object IEnumerator.Current { get { return null; } }
public WaitTimerV2(float time)
{
timeLeft = time;
}
public bool MoveNext()
{
timeLeft -= Time.deltaTime;
return timeLeft > 0;
}
public void Reset()
{
timeLeft = 0;
}
}
以上就是Unity中协程的原理以及示例讲解。