【Unity】运用协程设计任务执行队列

在开发的时候,经常会遇到按照顺序执行一些任务的情况,但是其中一些任务必需满足一定条件后才能执行下一个任务,例如进入游戏后需要依次执行检查资源(等待与服务器对比完成)、下载资源(等待下载完成)、加载资源(等待加载完成)、初始化、进入游戏这几个方法,这个时候就容易逻辑混乱和写出一些臃肿的代码。于是我针对这个情况写了一个任务执行队列功能。

思路是将每个需要执行的方法都封装为一个IEnumerator,然后将这些IEnumerator放入列表里。按顺序执行时,只需要用协程依次执行列表里的IEnumerator就行。

一个执行队列有以下几个主要的方法:

1、创建执行队列的实例

2、添加需要执行的方法到列表里

3、依次执行列表里的方法

为了方便书写和阅读,我们采用了链式编程的方法,代码效果如下:

ActionQueue.InitOneActionQueue().
    AddAction(方法1).
    AddAction(方法2).
    AddAction(方法3).
    AddAction(方法4).
    StartQueue();

采用这种方法我们在编写的时候就可以将代码块连续地书写下去,简洁且方便。


先上代码:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 任务执行队列
/// </summary>
public class ActionQueue : MonoBehaviour
{
    event Action onComplete;
    List<OneAction> actions = new List<OneAction>();
    public static ActionQueue InitOneActionQueue()
    {
        return new GameObject().AddComponent<ActionQueue>();
    }
    /// <summary>
    /// 添加一个任务到队列
    /// </summary>
    /// <param name="startAction">开始时执行的方法</param>
    /// <param name="IsCompleted">判断该节点是否完成</param>
    /// <returns></returns>
    public ActionQueue AddAction(Action startAction, Func<bool> IsCompleted)
    {
        actions.Add(new OneAction(startAction, IsCompleted));
        return this;
    }
    /// <summary>
    /// 添加一个协程方法到队列
    /// </summary>
    /// <param name="enumerator">一个协程</param>
    /// <returns></returns>
    public ActionQueue AddAction(IEnumerator enumerator)
    {
        actions.Add(new OneAction(enumerator));
        return this;
    }
    /// <summary>
    /// 添加一个任务到队列
    /// </summary>
    /// <param name="action">一个方法</param>
    /// <returns></returns>
    public ActionQueue AddAction(Action action)
    {
        actions.Add(new OneAction(action));
        return this;
    }

    /// <summary>
    /// 绑定执行完毕回调
    /// </summary>
    /// <param name="callback"></param>
    /// <returns></returns>
    public ActionQueue BindCallback(Action callback)
    {
        onComplete += callback;
        return this;
    }
    /// <summary>
    /// 开始执行队列
    /// </summary>
    /// <returns></returns>
    public ActionQueue StartQueue()
    {
        StartCoroutine(StartQueueAsync());
        return this;
    }

    IEnumerator StartQueueAsync()
    {
        if (actions.Count > 0)
        {
            if (actions[0].startAction != null)
            {
                actions[0].startAction();
            }
        }
        while (actions.Count > 0)
        {
            yield return actions[0].enumerator;
            actions.RemoveAt(0);
            if (actions.Count > 0)
            {
                if (actions[0].startAction != null)
                {
                    actions[0].startAction();
                }
            }
            else
            {
                break;
            }
            yield return new WaitForEndOfFrame();
        }
        if (onComplete != null)
        {
            onComplete();
        }
        Destroy(gameObject);
    }

    class OneAction
    {
        public Action startAction;
        public IEnumerator enumerator;
        public OneAction(Action startAction, Func<bool> IsCompleted)
        {
            this.startAction = startAction;
            //如果没用协程,自己创建一个协程
            enumerator = new CustomEnumerator(IsCompleted);
        }

        public OneAction(IEnumerator enumerator, Action action = null)
        {
            this.startAction = action;
            this.enumerator = enumerator;
        }

        public OneAction(Action action)
        {
            this.startAction = action;
            this.enumerator = null;
        }

        /// <summary>
        /// 自定义的协程
        /// </summary>
        class CustomEnumerator : IEnumerator
        {
            public object Current => null;
            Func<bool> IsCompleted;
            public CustomEnumerator(Func<bool> IsCompleted)
            {
                this.IsCompleted = IsCompleted;
            }
            public bool MoveNext()
            {
                return !IsCompleted();
            }

            public void Reset()
            {
            }
        }
    }
}

现在我们分析一下代码

1、首先我们需要将每个方法封装成一个OneAction对象:

class OneAction
    {
        public Action startAction;
        public IEnumerator enumerator;
        public OneAction(Action startAction, Func<bool> IsCompleted)
        {
            this.startAction = startAction;
            //如果没用协程,自己创建一个协程
            enumerator = new CustomEnumerator(IsCompleted);
        }

        public OneAction(IEnumerator enumerator, Action action = null)
        {
            this.startAction = action;
            this.enumerator = enumerator;
        }

        public OneAction(Action action)
        {
            this.startAction = action;
            this.enumerator = null;
        }

        /// <summary>
        /// 自定义的协程
        /// </summary>
        class CustomEnumerator : IEnumerator
        {
            public object Current => null;
            Func<bool> IsCompleted;
            public CustomEnumerator(Func<bool> IsCompleted)
            {
                this.IsCompleted = IsCompleted;
            }
            public bool MoveNext()
            {
                return !IsCompleted();
            }

            public void Reset()
            {
            }
        }
    }

OneAction主要工作就是将方法存储起来,并且提供一个IEnumerator供协程使用,这样就可以方便我们的存储、读取与执行。

然后我们再来看ActionQueue这个类,首先是ActionQueue的创建方法:

    public static ActionQueue InitOneActionQueue()
    {
        return new GameObject().AddComponent<ActionQueue>();
    }

我们采用静态方法创建一个gameobject,然后绑定ActionQueue脚本,返回实例化的对象。队列执行完后,这个物体会被自动销毁。

2、然后添加任务到队列:


    public ActionQueue AddAction(Action startAction, Func<bool> IsCompleted)

    public ActionQueue AddAction(IEnumerator enumerator)

    public ActionQueue AddAction(Action action)

这三种方法其实就是对应了OneAction的三种构造函数,然后将实例化的对象加入列表里。

3、最后执行协程StartQueueAsync方法,将列表里的OneAction依次执行。


举个例子:

我们用任务执行队列依次执行检查资源、下载资源、加载资源、初始化、进入游戏这几个方法

using System.Collections;
using UnityEngine;

public class ActionQueueTest : MonoBehaviour
{
    void Start()
    {
        ActionQueue.InitOneActionQueue().
            AddAction(CheckResources()).
            AddAction(DownloadResources()).
            AddAction(LoadGameObjects, () => loadCompleted).
            AddAction(Initialize).
            BindCallback(StartGame).
            StartQueue();
    }

    public IEnumerator CheckResources()
    {
        Debug.Log("开始检查资源...");
        yield return new WaitForSeconds(1);
        Debug.Log("检查资源完毕!");
    }

    IEnumerator DownloadResources()
    {
        Debug.Log("开始下载资源...");
        yield return new WaitForSeconds(1);
        Debug.Log("资源下载完毕!");
    }
    bool loadCompleted = false;
    void LoadGameObjects()
    {
        Debug.Log("加载游戏物体...");
        Invoke("LoadCompleted", 1);
    }

    void LoadCompleted()
    {
        loadCompleted = true;
    }

    void Initialize()
    {
        Debug.Log("初始化");
    }

    void StartGame()
    {
        Debug.Log("开始游戏!");
    }
}

执行后效果如下:

转载请注明原文:https://blog.csdn.net/b8566679/article/details/103579132

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值