Unity协程的一些理解(一)

Unity协程的一些理解

使用Unity开发游戏过程中,难免会使用到协程。现在总结下使用过程中的一些理解及使用过程中遇到的一些坑。
1. 使用协成必须继承MonoBehaviour。 开发过程中我们会单独写个协程类,来使用协程的功能。常见的写法:

  public static class CoroutineManager
    	{
    	    private static CoroutineTask mTask;
    	    static CoruntineManager()
    	    {
    	        GameObject go = new GameObject("CoroutineManager");
    	        mTask = go.AddComponent<CoroutineTask>();
    	    }
    	    public static Coroutine StartCoroutine(IEnumerator co)
    	    {
    	       return mTask.StartCoroutine(co);
    	    }
    	}
    	//当我们将this.gameObject.SetActive(false)时协程也随之终止。
    	//我们不可以通过SetActive(true)来恢复协程
    	public class CoroutineTask : MonoBehaviour
    	{
    	}

2.协程是在LateUpdate后执行,且waitforSecond 受到TimeScale的影响。 我们都知道Time.time 和Time.deltaTime 是受到TimeScale的影响。当timeScale 变大,上面两个值也变大。我们用下面一段代码测试:

    /*我们还知道TimeScale会影响FixUpdate每秒的执行次数,
    却不会影响Update和LateUpdate的执行次数。当TimeScale为0时。
    FixUpdate就会停止执行。对于协程,我们测试结果如下:
    [SerializeField] private float timeScale;
    private IEnumerator TestTimeScale()
    {
    ****协程常见的几个wait方法
       // 1.yield return new waitfor second 受到TimeScale影响
       //2. 一直到下一次FixUpdate执行 受到TimeScale影响
            yield return new WaitForFixedUpdate();
       // 3. 游戏真实过去的时间 不受TimeScale 影响
         yield return new WaitForSecondsRealtime(5f);
       //4. Waits until the end of the frame after all cameras and GUI is rendered, just
           before displaying the frame on screen.不受TimeScale影响
         yield return new WaitForEndOfFrame();
       // 5.  直到返回会True时 才开始往下执行
        yield return new WaitUntil(() =>
        {
            if (Input.GetKeyDown(KeyCode.A))
            {
                return true;
            }
            return false;
        });
     // 6.直到委托方法返回的时false 往下执行
        yield return new WaitWhile(() =>
        {
            if (Input.GetKeyDown(KeyCode.B))
            {
                return false;
            }
            return false;
        });
      //  yield return new WaitForSeconds(2f);
         yield return null;
        Debug.LogError(" two Secod Later");
    }
    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            Time.timeScale = timeScale;
            CoroutineManager.StartCoroutine(TestTimeScale());
        }
    }

3.协程其实就是一个IEnumerator(迭代器),IEnumerator 接口有两个方法 Current 和 MoveNext() ,只有当MoveNext()返回 true时才可以访问 Current,否则会报错。迭代器方法运行到 yield return 语句时,会返回一个expression表达式并保留当前在代码中的位置。 当下次调用迭代器函数时执行从该位置重新启动。 Unity在每帧做的工作就是:调用 协程(迭代器)MoveNext() 方法,如果返回 true ,就从当前位置继续往下执行。

 //利用协程的特点 我们可以实现简单的小工具
 //1.时间计时器
  //指定时间后回掉
    static IEnumerator CountDown(float delayTime, Action co)
    {
        yield return new WaitForSeconds(delayTime);
        if (co != null)
        {
            co();
        }
    }
    //每一帧回掉
    static IEnumerable CallBackEveryFrame(float time, Action<bool> callBack, bool ingoreTimeScale = true)
    {
        float lastTime = Time.realtimeSinceStartup;
        for (float deltaTime = time; deltaTime > 0;)
        {

            yield return null;
            float delta = ingoreTimeScale ? (Time.realtimeSinceStartup - lastTime) : Time.timeScale;
            deltaTime = deltaTime - delta;
            lastTime = Time.realtimeSinceStartup;
            bool lastFrame = deltaTime <= 0;
            if (callBack != null)
            {
                callBack(lastFrame);
            }
        }
    }
    public static void CallBack(float totalTime,float interVal,Action callBack)
    {
        mTask.StartCoroutine(CallBackEveryInterval(totalTime,interVal, callBack));
    }
    //每隔一定时间回掉一次(实现这个功能还可以新写个类继承CustomYieldInstruction 这里就不再写了)
    static IEnumerator CallBackEveryInterval(float totalTime,float interVal,Action callBack)
    {
        for (float deltaTime = totalTime; deltaTime > 0;deltaTime-=interVal)
        {
            yield return new WaitForSeconds(interVal);
            if (callBack != null)
            {
                callBack();
            }
        }
    }


4.上面说到协程实际上是一个迭代器,现在我们用 C# 我们简单实现一个迭代器:

    class Iteration : IEnumerable
    {
        public object[] someObjects;
        public int startPoint = -1;
        public IEnumerator GetEnumerator()
        {
            //return new IterationEnumerator(this);
            **//在C#2中提供了迭代块和yield return 语句 可以完全用下面的方法来实现GetEnumerator方法 而不必写IterationEnumerator类
            for (int index = 0; index < this.someObjects.Length; index++)
            {
                yield return someObjects[(index + startPoint)%someObjects.Length];
            }**
        }

        public Iteration(Object[] someObjects, int startPoint)
        {
            this.someObjects = someObjects;
            this.startPoint = startPoint;
        }

    }

    class IterationEnumerator : IEnumerator
    {
        //迭代器对象
        private Iteration iteration;
        //当前的游标位置
        private int postion = -1;

        internal IterationEnumerator(Iteration iteration)
        {
            this.iteration = iteration;
            postion = -1;
        }
        //当调用完MoveNext之后 才会调用GetCurrent
        public bool MoveNext()
        {
            if (postion != iteration.someObjects.Length)
            {
                postion++;
            }
            //超过长度 返回false
            return postion < iteration.someObjects.Length;
        }

        public void Reset()
        {
            postion = -1;
        }

        public object Current
        {
            get
            {
                if (postion == -1 || postion == iteration.someObjects.Length)
                {
                    throw new InvalidOperationException();
                }
                int index = postion + iteration.startPoint;
                index = index % iteration.someObjects.Length;
                return iteration.someObjects[index];
            }
        }
    }
    //调用
    static void Main(string[] args)
        {
             string [] text = new string[]{"0","1","2","3"};
            Iteration a =new Iteration(text,2);
            IEnumerator n = a.GetEnumerator();
            while (true)
            {
                bool result = n.MoveNext();
                if(!result)
                    break;
                Console.WriteLine(n.Current);
            }
            Console.ReadKey();
        }

同样在Unity中我们也写个:

**//写个MyCoroutine实现IEnurator**
public class MyCoroutine : IEnumerator
{
    private Action callBack = null;
    private float endTime;

    public MyCoroutine(float time, Action callBack)
    {
        this.callBack = callBack;
        endTime = Time.realtimeSinceStartup + time;
    }

    public bool MoveNext()
    {
      //当return false时迭代器停止
        return Time.realtimeSinceStartup < endTime;
    }

    public void Reset()
    {
        throw new System.NotImplementedException();
    }
    public object Current
    {
        get
        {
        ,//每次迭代都会调用
            if (callBack != null)
            {
                callBack();
            }
            return null;
        }
    }
    **//在TestCoroutine中调用 也实现了一个计时回调**
    void Start () {
        MyCoroutine co = new MyCoroutine(10, () =>
        {
           Debug.Log("回调");
        });
        //① 这么写
	    StartCoroutine(co);
	    //② 也可以这么写  这样我就可以在 MyCoroutine中实现 new waitForSecod等 Unity API提供的功能。当然也可以自己写一些
	    StartCoroutine(Test())
	}
    private IEnumerator Test()
    {
        yield return new MyCoroutine(10, () =>
        {
            Debug.Log("TestCoroutine");
        });
        //十秒后才会输出下面的Log
           Debug.Log("-------------");
    }
	//输出结果:每一帧都会回调,知道MoveNext return为false。StartCoroutine 实现了上面Main函数中 While循环内的东西。
  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值