目的
当用 cocos2d 的时候有 Sequence , Spawn, Delay , MoveTo,Event 等函数可供调用,很方便的解决一些顺序执行和同步的执行的逻辑
但是在 Unity 中并没有好用的办法,例如 写个定时器还要写个协程,当然有很多人封装了自己的 Timer 做一些简单的定时任务,调用的很方便,但是在顺序执行方面却很达到我想要的功能,由此而生 UniWb Action 。
同时 Action 由 UniRx 封装而来,借助 UniRx 这个强大的插件。
定时
定时是很重要的功能,我只希望调用要简单,同时能有回调接口就更好了
using Wb;
public class NewBehaviourScript : MonoBehaviour
{
void Start()
{
this.Delay(2,a=> {
Debug.Log("延迟2秒后执行");
}).Begin(this);
// 相同功能
this.Delay(2, a =>
{
Debug.Log("延迟2秒后执行");
}).Begin().AddTo(this);
}
}
这里 Begin 的意思是开始执行的意思,传入this 的意思是随组件的生命周期而结束,也就是说,不管这个延时有没有走完,当组件销毁的时候也就结束了。
第二种写法
this.Delay(2)
.Event(a =>
{
Debug.Log("延迟2秒后执行");
}).Begin(this);
这里引入 Event 写法,目的就是与 Delay 任意组合,也就是你可以这么写
this.Event(a =>
{
Debug.Log("先执行的操作");
}).Delay(2)
.Event(a=> {
Debug.Log("延迟2秒后执行");
}).Begin(this);
嗯,有了 Delay 和 Event 其实已经能解决大部分问题
我需要这个定时器能够执行我需要的次数
this.Delay(2, 3, a =>
{
Debug.Log("延迟2秒后执行,执行三次,现在次数 :" + a);
}).Begin(this);
IDisposable disposable = this.Delay(2, -1, a =>
{
Debug.Log("延迟2秒后执行,无限循环, 当手动执行 disposable.Dispose() 时结束");
}).Begin(this);
// disposable.Dispose();
每一个链都会返回一个 IDisposable 对象,想手动结束的时候就 Dispose() 掉就可以了。
同步 Spawn
有时候你的确需要这个功能,来进行同步这操作
this.Event(a =>
{
Debug.Log("先执行");
}).Spawn(
this.Delay(2, a => { Debug.Log("aaaa"); }),
this.Delay(4, a => { Debug.Log("bbbb"); })
).Event(a => {
Debug.Log("两个延时执行完毕");
}).Begin(this);
当两个定时器都执行完毕后,才会输出 “两个延时执行完毕”
你甚至会发现 Spawn 居然能跟 Event 任意组合在一起,多么美好~~~
这里有个限制,也就是 Spawn 里的参数要是同类型的,暂时没想到什么好办法~~
Complete
有时我们需要在事件流的最后有一个回调,那么写个 Complete 吧,会方便很多,如果你真的理解 UniRx 其实就是 .Subscribe(a=> { })
调用,嗯,后面再说说。
this.Delay(2)
.Event(a =>
{
Debug.Log("1111");
})
.Delay(2)
.Event(a =>
{
Debug.Log("222");
})
.Delay(2)
.Complete(_ =>
{
Debug.Log("333");
}).AddTo(this);
DOTween
要很好的让 object 执行相关的运动,那么 DOTween 将会比较好的选择,因为光靠 Unity 实在实现比较麻烦,所以在 UniWb 里集成 DOTween 。嗯,就是那个 1.0.155 DOTween Pro 版本,AssetStore 里最新版本。
当然为了让它更好的融进链式表达式中,我新建了一个接口,就是为了容纳它,谁叫它这么好用呢。。。
using DG.Tweening;
this.DOTween(transform.DOMoveX(2, 2)).Begin(this);
嗯,就是这么用就可了,物体就会在两秒内x 轴移动两个单位长度。
你此时可能会想,这么做不是多此一举吗,跟我直接调用有什么区别呢?
this.Delay(2)
.Event(a =>
{
Debug.Log("运动前");
})
.DOTween(transform.DOMoveX(2, 2))
.Event(a=> {
Debug.Log("运动后");
})
.Begin(this);
我们的目的是为了让它融进链中~~~~
this.Delay(2)
.DOTween(transform.DOMoveX(2, 2))
.DOTween(transform.DOMoveY(2, 2))
.DOTween(transform.DOScale(2, 2))
.Begin(this);
它会按照顺序执行,
好,我们要结合 Spawn 去使用了
this.Delay(2)
.Spawn(
this.DOTween(transform.DOMoveY(2, 2)),
this.DOTween(transform.DOMoveX(2, 2))
)
.Spawn(
this.DOTween(transform.DOMoveY(0, 2)),
this.DOTween(transform.DOMoveX(0, 2))
)
.Begin(this);
同时 MoveX 和 MoveY , 获取你已经猜到了,它会斜着运动, 这段代码,就是先会斜着运动两个单位长度,然后再运动回来。
与 UniRx 结合
前面介绍的其实已经足够帮助你解决很多比较麻烦的问题了,但是它们既然来自 UniRx ,那么配合着 UniRx 将会有更强大的威力
var clickStream = Observable.EveryUpdate()
.Where(_ => Input.GetMouseButtonDown(0));
clickStream.Buffer(clickStream.Throttle(TimeSpan.FromMilliseconds(250)))
.Where(xs => xs.Count >= 2)
.FEvent(a =>
{
Debug.Log("do something");
}).
Delay(2).
FDOTween(transform.DOMoveX(1, 1)).
Begin(this);
再没有比这个示例更能说明问题的了,
这段代码实现的功能就是,当鼠标双击的时候 输出 "do something" 后,延时2秒后执行 动画移动一个单位长度。
你会发现有点不一样的地方,就是 FEvent , FDOTween , 为什么要加个 F 呢,
嗯,这里其实是 Filter 的 F ,就是为了 和 Event ,DOTween 区分开来,
因为这里的流是 持续流,我想你又记起前面水管的话了,对!这里 持续流,要么我主动 Dispose 掉,要么随着 物体销毁而销毁掉。
而主要的区别是在 OnNext 和 OnComplete , 持续流在没停止前是不会调用 OnComplete 的~~~~~