接上两篇:
一、用协程处理
在Unity中,协程天生可以用来执行连续的异步任务。如下:
using System.Collections;
using UnityEngine;
public class Test : MonoBehaviour
{
IEnumerator DoSthA()
{
yield return null;
}
IEnumerator DoSthB()
{
yield return new WaitForSeconds(1);
}
//Unity自带协程
//依次执行: DoSthA、DoSthB、DoSthB()、DoSthA()
private IEnumerator Start()
{
yield return DoSthA();
for (int i = 1; i < 2; i++)
{
yield return DoSthB();
}
yield return DoSthA();
}
}
二、将其简单包装一下,纳入“步进系统”
这样就可以将 “协程方式处理的异步任务” 和 “回调方式处理的异步任务”(SerialSteps) 归到一个体系下。
然后根据实际需求混合使用。
注意,执行SerialCoroutines,需要传入一个启动协程的MonoBehaviour实例。
如果你想要停止这个串行步骤,停止这个协程即可。
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace NRatel
{
/// <summary>
/// 串行协程(单元: IEnumerator)
/// </summary>
sealed public class SerialCoroutines : IMultiStep
{
private readonly List<IEnumerator> m_Stepes;
private readonly MonoBehaviour m_Behaviour;
private Action<int> m_OnProgress;
private Action m_OnComplete;
private int m_Progress;
public SerialCoroutines(List<IEnumerator> stepes, MonoBehaviour behaviour)
{
this.m_Stepes = stepes;
this.m_Behaviour = behaviour;
}
public void Enter()
{
Enter(null, null);
}
public void Enter(Action onComplete)
{
Enter(null, onComplete);
}
public void Enter(Action<int> onProgress, Action onComplete)
{
this.m_Progress = 0;
this.m_OnProgress = onProgress;
this.m_OnComplete = onComplete;
IEnumerator mergedSteps = MergeSteps();
m_Behaviour.StartCoroutine(mergedSteps);
}
private IEnumerator MergeSteps()
{
foreach (IEnumerator step in m_Stepes)
{
this.m_Progress += 1;
m_OnProgress?.Invoke(m_Progress);
yield return step;
}
m_OnComplete?.Invoke();
}
}
}
将 Unity自带协程、SerialCoroutines、SerialSteps 混合使用。测试如下:
using System;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using NRatel;
public class Test : MonoBehaviour
{
IEnumerator DoSthA()
{
yield return null;
}
IEnumerator DoSthB()
{
yield return new WaitForSeconds(1);
}
class DoSthC : IStep
{
public void Enter()
{
Enter(null);
}
public void Enter(Action onFinish)
{
onFinish();
}
}
//Unity自带协程、SerialCoroutines、SerialSteps 混合使用。
//依次执行: DoSthA、DoSthC、DoSthB()、DoSthB()、DoSthC
private IEnumerator Start()
{
yield return DoSthA();
IMultiStep sc = new SerialCoroutines(new List<IEnumerator>() { DoSthB(), DoSthB() }, this);
IMultiStep ss = new SerialSteps(new List<IStep>() { new DoSthC(), sc, new DoSthC() });
ss.Enter(() =>
{
Debug.Log("完成");
});
}
}
三、改用迭代器的方式运行
事实上,你可能只想执行一段串行过程,并不想让它依赖一个MonoBehaviour实例。
之前的文章 ,已经说过协程和迭代器的关系。只需将启动协程处改成迭代即可(它们的本质都是先将一个 List<IEnumerator> 合并成一个IEnumerator)。
需要说明的是,SerialCoroutines 和 SerialEnumerators 的单元都是 IEnumerator。
这意味着它们都只能在C#中使用,在lua中还得使用 SerialSteps。
using System;
using System.Collections;
using System.Collections.Generic;
namespace NRatel
{
/// <summary>
/// 串行迭代(单元: IEnumerator)
/// </summary>
sealed public class SerialEnumerators : IMultiStep
{
private readonly List<IEnumerator> m_Stepes;
private Action<int> m_OnProgress;
private Action m_OnComplete;
private int m_Progress;
public SerialEnumerators(List<IEnumerator> stepes)
{
this.m_Stepes = stepes;
}
public void Enter()
{
Enter(null, null);
}
public void Enter(Action onComplete)
{
Enter(null, onComplete);
}
public void Enter(Action<int> onProgress, Action onComplete)
{
this.m_Progress = 0;
this.m_OnProgress = onProgress;
this.m_OnComplete = onComplete;
IEnumerator mergedSteps = MergeSteps();
while (mergedSteps.MoveNext()) { }
}
private IEnumerator MergeSteps()
{
foreach (IEnumerator step in m_Stepes)
{
this.m_Progress += 1;
m_OnProgress?.Invoke(m_Progress);
yield return step;
}
m_OnComplete?.Invoke();
}
}
}