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