插件地址:传送门
1、命名空间
using System.Collections.Generic;
using MEC;
2、与传统的协程相比
传统:StartCoroutine(_CheckForWin());
被RunCoroutine取代。必须选择执行循环进程,默认为“Segment.Update”。
using System.Collections.Generic;
using UnityEngine;
using MEC;public class Test : MonoBehaviour
{void Start(){StartCoroutine(_CheckForWin());// To run in the Update segment:Timing.RunCoroutine(_CheckForWin());// To run in the FixedUpdate segment:Timing.RunCoroutine(_CheckForWin(), Segment.FixedUpdate);// To run in the LateUpdate segment:Timing.RunCoroutine(_CheckForWin(), Segment.LateUpdate);// To run in the SlowUpdate segment:Timing.RunCoroutine(_CheckForWin(), Segment.SlowUpdate);}IEnumerator<float> _CheckForWin(){yield return Timing.WaitForSeconds(1);Debug.Log("****");}
}
3、协程的退出/停止 CancelWith
相当于StopCoroutine,CancelWith确实会增加所有协程生成的不可避免的GC分配的大小大约20个字节。20字节并不大,但是如果您想避免所有可能的GC分配,那么你就可以相对容易地做CancelWith所做的事情,而不用使用这个函数。只是确保在协程中的每个yield return语句之后都做以下检查:if(gameObject != null && gameObject.activeInHierarchy)
public class Cube01 : MonoBehaviour
{private void Awake(){Timing.RunCoroutine(_moveMyButton().CancelWith(gameObject));}IEnumerator<float> _moveMyButton(){while (true){yield return Timing.WaitForSeconds(1f);Debug.Log("********");}}
}
4、停止和回复协程
MEC协程可以暂停,稍后再恢复。Unity的协程不会这样做,但是概念很简单。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using MEC;public class Cube01 : MonoBehaviour
{private CoroutineHandle handleToACoroutine;private void Awake(){handleToACoroutine= Timing.RunCoroutine(_moveMyButton().CancelWith(gameObject));}private void Update(){if (Input.GetKeyDown(KeyCode.P)){Timing.PauseCoroutines(handleToACoroutine);}if (Input.GetKeyDown(KeyCode.R)){Timing.ResumeCoroutines(handleToACoroutine);}}IEnumerator<float> _moveMyButton(){while (true){yield return Timing.WaitForSeconds(1f);Debug.Log("********");}}
}
5、等待直到完成
Unity的默认协同程序有几种情况,你可以产生返回一些变量。例如,你可以“yield return asyncOperation;”。MEC也有这样的功能函数称为WaitUntilDone。
yield return Timing.WaitUntilDone(wwwObject);yield return Timing.WaitUntilDone(asyncOperation);yield return Timing.WaitUntilDone(customYieldInstruction);// With MEC Pro you can do a little more with WaitUntilDone:yield return Timing.WaitUntilDone(newCoroutine);// 上面的代码自动启动一个新的协程,并保存当前的协程。yield returnTiming.WaitUntilTrue(functionDelegateThatReturnsBool); //免费版没有yield returnTiming.WaitUntilFalse(functionDelegateThatReturnsBool); //免费版没有
6、慢更新(slow update)
Unity的协程没有慢更新循环的概念,但MEC的协程有。
慢更新循环(默认情况下)每秒运行7次。它使用绝对时间刻度,所以当你减慢Unity的时间刻度时,它不会减慢SlowUpdate。使用SlowUpdate和总是使用“yield returnTiming.WaitForSeconds(1f/7f);”有两个主要区别。第一个是绝对时间尺度,第二个是所有的SlowUpdate节拍同时发生。
private void Awake(){Timing.RunCoroutine(_UpdateTime(), Segment.SlowUpdate);}private float clock;private IEnumerator<float> _UpdateTime(){while (true){//当前段已运行的时间(以秒为单位)。clock = Timing.LocalTime;yield return 0f;}}
SlowUpdate还可以很好地检查临时调试变量。例如,如果重建项目需要很长时间,您可以在脚本中设置一个公共bool值,以重置该脚本上的值。您需要定期检查该bool值是否已设置为true,执行该检查的最佳时间是在SlowUpdate上。当用户选中复选框时,它会感觉它立即响应,但它将在你的应用程序中使用更少的处理每1/7秒检查一次,而不是每秒30 - 100次(取决于你的帧率)。
注意:Unity的Time. deltatime变量在SlowUpdate中不会返回正确的值,因为Unity的Time类对这个片段一无所知。可以使用Timing.DeltaTime代替。
您还可以更改SlowUpdate运行的速率。例如:
Timing.Instance.TimeBetweenSlowUpdateCalls = 3f;
上面这行代码将使SlowUpdate每3秒只运行一次。
7、标签【Tag】
在启动协程时,可以选择是否提供标记。标记是标识该协程的字符串。当您标记一个协程或一组协程时,您可以稍后使用KillCoroutine(标签)或KillAllCoroutines(标签)杀死该协程或该组。【API可能会变,可以查看相关API调用】
using System.Collections.Generic;
using UnityEngine;
using MEC;public class Test : MonoBehaviour
{void Start(){Timing.RunCoroutine(_shout(1, "Hello"), "shout");Timing.RunCoroutine(_shout(2, "World!"), "shout");Timing.RunCoroutine(_shout(3, "I"), "shout2");Timing.RunCoroutine(_shout(4, "Like"), "shout2");Timing.RunCoroutine(_shout(5, "Cake!"), "shout2");Timing.RunCoroutine(_shout(6, "Bake"), "shout3");Timing.RunCoroutine(_shout(7, "Me"), "shout3");Timing.RunCoroutine(_shout(8, "Cake!"), "shout3");Debug.Log("Killed " + Timing.KillCoroutines("shout2"));}IEnumerator<float> _shout(float time, string text){yield return Timing.WaitForSeconds(time);Debug.Log(text);}
}
8、LocalTime and DeltaTime
Unity的Time. deltatime变量在SlowUpdate中不会返回正确的值,因为Unity的Time类对这个片段一无所知。可以使用Timing.DeltaTime代替。Unity中的默认Time类在大多数情况下都可以正常工作,但在SlowUpdate中无法正常工作。
9、其它的功能【Additional Functionality】
Timing对象中还包含三个辅助函数:CallDelayed、callcontinuous和callperiodic。
(1)CallDelayed: 在一定秒数后调用指定的动作。
(2)CallContinously:每帧连续调用动作,持续数秒。
(3)CallPeriodically:周期性地每隔“x”秒调用动作,持续数秒。
这三种功能都可以很容易地使用协程创建,但是这个基本功能最终被忽略了由于使用频繁,我们将其包含在基本模块中。
using System.Collections.Generic;
using UnityEngine;
using MEC;public class Test : MonoBehaviour
{private void Start(){// 2秒后启动_RunFor5Seconds。Timing.CallDelayed(2f, delegate{Timing.RunCoroutine(_RunFor5Seconds(Timing.RunCoroutine(Test01())));});// 使物体以每秒一个世界单位的速度向前推进,持续4秒。Timing.CallContinuously(4f, delegate {PushOnGameObject(Vector3.forward);}, Segment.FixedUpdate);// 尽量不要对callcontinuous进行闭包是非常重要的,因为这将导致每帧都进行GC分配。Timing.CallContinuously<Vector3>(Vector3.forward, 4f,vector => PushOnGameObject(vector), Segment.FixedUpdate);}private void PushOnGameObject(Vector3 amount){transform.position += amount * Time.deltaTime;}private IEnumerator<float> _RunFor5Seconds(CoroutineHandle waitHandle){Debug.Log("Yielding 5s..");yield return Timing.WaitUntilDone(waitHandle);Debug.Log("Starting 5 second run.");yield return Timing.WaitForSeconds(5f);Debug.Log("Finished 5 second run.");}private IEnumerator<float> Test01(){yield return Timing.WaitForOneFrame;Debug.Log("********");}
}
10、链式结构 【Fluid Architecture】
// 常规
Timing.RunCoroutine(_Foo().CancelWith(gameObject));
// MEC的链式
_Foo().CancelWith(gameObject).RunCoroutine();
请记住:与运行协程的其他方式一样,如果您忘记使用RunCoroutine,编译器不会报错,但它不会执行协程。当对Run的调用位于行尾时,这个事实可能有点难以记住。还要记住,这种流畅的语法可能会让其他习惯使用Unity默认协同程序的开发人员感到困惑,因为Unity的默认协同程序API不支持这种语法。
QA:
问:MEC有WaitForEndOfFrame函数吗?
它没有在MEC Free中实现,但MEC Pro有一个部分。
注意:关于WaitForEndOfFrame的实际作用有一些混淆。当你只是想屈服到下一帧时,WaitForEndOfFrame不是一个理想的命令,最好使用“yield return null;”。许多人在使用Unity的协程时使用WaitForEndOfFrame,因为这是他们在Unity的默认协程中最接近WaitForOneFrame的东西,他们没有意识到这可能会导致微妙的问题。MEC定义了常数Timing。WaitForOneFrame,所以在MEC中,如果你愿意,你可以使用显式变量名,而不会产生使用EndOfFrame可能导致的视觉故障和性能下降的可能性。
问:MEC有StopCoroutine的功能吗?
是的。它叫做Timing.KillCoroutines()。它可以接受前一个Timing返回的协程的句柄。RunCoroutine命令,或者它可以接受一个标记。
注意:KillCoroutine用于从不同的函数中停止协程函数。如果你想从协程的函数内部结束协程,那么最好的命令是“yieldbreak;”,这相当于在任何其他函数中调用“return;”。yield break更好的原因是因为KillCoroutine命令不能结束当前正在运行的协程函数,因此该函数将继续执行,直到下一个yield命令,但yield break没有这个问题。
问:MEC有StopAllCoroutines函数吗?
是的。Timing.KillCorutines()。如果你想暂时停止一切,你也可以使用Timing.PauseCorutines()和Timing.ResumeAllCorutines()。
问:MEC是否有一个函数使一个协程在另一个协程完成之前生成?
是的。在你想要持有的协程内部,你调用"yield return Timing.WaitUntilDone(coroutineHandle);"无论何时调用Timing.RunCoroutine,都会返回句柄。
问:MEC完全删除GC分配吗?
答:不是。MEC删除所有逐帧GC分配。(除非你在协程内部的堆上分配内存,但MEC无法控制这一点。)当协程第一次创建时,函数指针和传递给它的任何变量都被放在堆上,最终必须由垃圾收集器清理。这种不可避免的分配发生在Unity的协程和MEC协程中。MEC协程平均分配的垃圾确实比Unity协程少。
问:MEC协程总是比Unity协程内存效率更高吗,还是只有在特定情况下才如此?
答:MEC协程在所有情况下都比Unity协程产生更少的GC分配,除非你分配大字符串并将其作为协程的标记
案例:
void Start (){CoroutineHandle handle =
Timing.RunCoroutine(_RunFor10Seconds());handle = Timing.RunCoroutine(_RunFor1Second(handle));Timing.RunCoroutine(_RunFor5Seconds(handle));}private IEnumerator<float> _RunFor10Seconds(){Debug.Log("Starting 10 second run.");yield return Timing.WaitForSeconds(10f);Debug.Log("Finished 10 second run.");}private IEnumerator<float> _RunFor1Second(CoroutineHandle
waitHandle){Debug.Log("Yielding 1s..");yield return Timing.WaitUntilDone(waitHandle);Debug.Log("Starting 1 second run.");yield return Timing.WaitForSeconds(1f);Debug.Log("Finished 1 second run.");}private IEnumerator<float> _RunFor5Seconds(CoroutineHandle
waitHandle){Debug.Log("Yielding 5s..");yield return Timing.WaitUntilDone(waitHandle);Debug.Log("Starting 5 second run.");yield return Timing.WaitForSeconds(5f);Debug.Log("Finished 5 second run.");}