[Unity]自定义协程CustomYieldInstruction/IEnumerator

  我们知道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

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中自定义协程的示例讲解。

 

上一篇:2021年了,`IEnumerator`、`IEnumerable`还傻傻分不清楚?


下一篇:C#foreach的本质是什么、如何实现自定义集合类的foreach遍历