保举阅读
- CSDN主页
- GitHub开源地址
- Unity3D插件分享
- 简书地址
- 我的个人博客
- QQ群:1040082875
各人好,我是佛系工程师☆舒适的小魔龙☆,不定时更新Unity开辟本领,以为有效记得一键三连哦。
一、先容UniRx插件
UniRx是一种基于Unity3D的相应式编程框架。
UniRx就是Unity版本的Rx相应式扩展,相应式就是观察者和定时器,扩展指的是LINQ的使用符。Rx相应式扩展的特点就是善于处理时间上的异步的逻辑。用Rx相应式扩展的方式编程可以很好地构造大量异步与并行处理。
UniRx重写了.Net的相应式扩展,主要作用是办理时间上异步的逻辑,让异步逻辑变得更加简洁和优雅。
Unity3D通常是单线程,但是UniRx可以让多线程更容易。
UniRx可以简化 UGUI 的编程,全部的UI事件可以转化为UniRx 的事件流。
UniRx支持的平台有PC/Mac/Android/iOS/WebGL/WindowsStore等平台和库。
二、为什么使用UniRx插件
在项目中的一些逻辑使用需要做异步时间处理,好比说动画播放、网络哀求、资源加载、场景过渡等等,这种情况通常要使用协程,也就是WWW和Coroutine,但是使用协程来做异步通常不是一个很好的选择,因为:
- 协程不能返回值,它的返回类型必须是IEnumerator
- 协程不能处理异常,因为yield return 语句没有办法try-catch
- 会导致使用大量的回调来处理逻辑
- 使用协程会导致程序的耦合性高,造成协程中的逻辑过于复杂
UniRx就是为了办理这些题目来的,那么它有哪些长处呢:
- UniRx的使用方式介于回调和事件之间,有事件的概念,也使用了回调,回调是在事件颠末构造之后,只需要调用一次举行事件的处理。
- UniRx促进了多线程的使用,提供了UGUI的UI编程,UI事件可以转化为UniRx的事件流。
- Unity3D在2017版本后支持了C#中的astnc/await,UniRx也为Unity提供了更轻量、强大的astnc/await集成。
三、UniRx插件下载
源码地址:
https://github.com/neuecc/UniRx
Unity Asset Store 地址(免费):
http://u3d.as/content/neuecc/uni-rx-reactive-extensions-for-unity/7tT
插件下载地址:
https://github.com/neuecc/UniRx/releases
UniRx中的astnc/await集成
https://github.com/Cysharp/UniTask
四、怎么使用UniRx插件
4-1、快速入门
将插件导入到项目中:

新建脚本UniRxTest.cs编辑代码,实现一个双击检测Demo:
- using System;
- using UniRx;
- using UnityEngine;
- public class UniRxTest : MonoBehaviour
- {
- void Start()
- {
- // Observable.EveryUpdate调用协程的yield return null。
- // 它位于Update之后,LateUpdate之前。
- // Where等待操作的事件(当前事件是左键单击)
- var doubleClick = Observable.EveryUpdate()
- .Where(value => Input.GetMouseButtonDown(0));
- // Buffer 添加一个事件
- // Throttle 响应的最大间隔
- // TimeSpan.FromMilliseconds(250) 设置为250毫秒
- // Where 等待操作的事件(当前事件是左键单击)
- // Subscribe 绑定委托
- doubleClick.Buffer(doubleClick.Throttle(TimeSpan.FromMilliseconds(250)))
- .Where(value => value.Count >= 2)
- .Subscribe(value => Debug.Log("双击! 点击次数:" + value.Count));
- }
- }
复制代码 运行效果:

这个Demo使用了5行代码就演示了以下功能:
- Update作为事件流
- 组合事件流
- 合并自身流
- 方便处理基于时间的使用
4-2、定时功能(与协程对比)
在平时项目开辟中,可能会遇到需要颠末一段时间出发某些逻辑的使用,可以用协程这么写:
- using System;
- using System.Collections;
- using UniRx;
- using UnityEngine;
- public class UniRxTest : MonoBehaviour
- {
- void Start()
- {
- // 每5秒调用一次函数
- StartCoroutine(Timer(5, DoSomething));
- }
- // 定时器
- IEnumerator Timer(float seconds, Action callback)
- {
- yield return new WaitForSeconds(seconds);
- callback();
- }
- // 调用函数
- void DoSomething()
- {
- Debug.Log("TODO");
- }
- }
复制代码 那么用UniRx怎么写呢:
- using System;
- using System.Collections;
- using UniRx;
- using UnityEngine;
- public class UniRxTest : MonoBehaviour
- {
- void Start()
- {
- // 每5秒调用一次函数
- Observable.Timer(TimeSpan.FromSeconds(5))
- .Subscribe(value => {DoSomething();});
- }
- // 调用函数
- void DoSomething()
- {
- Debug.Log("TODO");
- }
- }
复制代码 乃至可以简化成一行代码:
- using System;
- using System.Collections;
- using UniRx;
- using UnityEngine;
- public class UniRxTest : MonoBehaviour
- {
- void Start()
- {
- // 每5秒调用一次函数
- Observable.Timer(TimeSpan.FromSeconds(5)).Subscribe(value => { Debug.Log("TODO"); });
- }
- }
复制代码 为了制止this烧毁的时间,流程还没有烧毁的情况,可以加一行代码:
- using System;
- using System.Collections;
- using UniRx;
- using UnityEngine;
- public class UniRxTest : MonoBehaviour
- {
- void Start()
- {
- // 每5秒调用一次函数
- Observable.Timer(TimeSpan.FromSeconds(5))
- .Subscribe(value => { DoSomething(); })
- .AddTo(this);
- }
- // 调用函数
- void DoSomething()
- {
- Debug.Log("TODO");
- }
- }
复制代码 AddTo(this)之后,就会将延长和this(MonoBehaviour)绑定在一起了,当this被烧毁的时间,界说的流程也会被烧毁。
4-3、GET和POST使用
一般写法:
- using System;
- using System.Collections;
- using UniRx;
- using UnityEngine;
- using UnityEngine.Networking;
- public class UniRxTest : MonoBehaviour
- {
- void Start()
- {
- StartCoroutine(RequestData("www.baidu.com", new WWWForm(), ReturnValue));
- }
- //回调函数
- private void ReturnValue(string value)
- {
- Debug.Log(value);
- }
- /// <summary>
- /// 数据请求与发送
- /// </summary>
- /// <param name="url">请求的url</param>
- /// <param name="form">表单</param>
- /// <param name="dele">返回数据</param>
- /// <returns></returns>
- private IEnumerator RequestData(string url, WWWForm form, Action<string> dele = null)
- {
- UnityWebRequest req = UnityWebRequest.Post(url, form);
- yield return req.SendWebRequest();
- if (req.result == UnityWebRequest.Result.ProtocolError)
- {
- dele?.Invoke(req.error);
- }
- if (req.isDone)
- {
- dele?.Invoke(req.downloadHandler.text);
- }
- }
- }
复制代码 用UniRx写法:
- using System;
- using System.Collections;
- using UniRx;
- using UnityEngine;
- using UnityEngine.Networking;
- public class UniRxTest : MonoBehaviour
- {
- void Start()
- {
- var request = ObservableWWW.Post("www.baidu.com", new WWWForm())
- .Subscribe(value => Debug.Log(value))
- .AddTo(this);
- }
- }
复制代码 留意:不是讨论那个写法好,那么写法欠好,只是使用UniRx更加简洁,更保举是用UnityWebRequest,因为UnityWebRequest功能更完善,更加有效。
4-4、加载场景-AsyncOperation
在异步加载资源大概异步加载场景的时间通常会用到 AsyncOperation。
UniRx 对 AsyncOperation 做了支持。使得加载进度可以很容易地监听。
示例代码如下:
- using UniRx;
- using UnityEngine;
- using UnityEngine.SceneManagement;
- namespace UniRxLesson
- {
- public class AsyncOperationExample : MonoBehaviour
- {
- void Start()
- {
- var progressObservable = new ScheduledNotifier();
- SceneManager.LoadSceneAsync(0).AsAsyncOperationObservable(progressObservable)
- .Subscribe(asyncOperation =>
- {
- Debug.Log("load done");
- Resources.LoadAsync("TestCanvas").AsAsyncOperationObservable()
- .Subscribe(resourceRequest =>
- {
- Instantiate(resourceRequest.asset);
- });
- });
- progressObservable.Subscribe(progress =>
- {
- Debug.LogFormat("加载了:{0}", progress);
- });
- }
- }
- }
复制代码 4-5、UGUI支持
示例代码:
- using System;
- using System.Collections;
- using UniRx;
- using UniRx.Diagnostics;
- using UnityEngine;
- using UnityEngine.Networking;
- using UnityEngine.UI;
- public class UniRxTest : MonoBehaviour
- {
- public Button mButton;
- public Toggle mToggle;
- public InputField mInput;
- public Text mText;
- public Slider mSlider;
- void Start()
- {
- // Button 按钮绑定事件
- mButton.onClick.AsObservable().Subscribe(_ => Debug.Log("clicked"));
- // Toggle 控制其他UI对象的激活
- mToggle.OnValueChangedAsObservable().SubscribeToInteractable(mButton);
- // mInput Where筛选值不等于空的情况 绑定Text组件
- mInput.OnValueChangedAsObservable()
- .Where(x => x != null)
- .SubscribeToText(mText);
- // mSlider 绑定Text组件
- mSlider.OnValueChangedAsObservable()
- .SubscribeToText(mText, x => Math.Round(x, 2).ToString());
- }
- }
复制代码 可以看出来,UniRx去绑定UI照旧很好用的,但不但于此。
使用 UniRx可以很容易地实现 MVP(MVRP)计划模式。
为什么应该用 MVP模式而不是 MVVM模式?Unity 没有提供 UI 绑定机制,创建一个绑定层过于复杂而且会对性能造成影响(使用反射)。只管云云,视图照旧需要更新。 Presenters层知道 View 组件而且能更新它们。
固然没有真的绑定,但 Observables 可以关照订阅者,功能上也差不多。这种模式叫做 Reactive Presenter 计划模式,示例代码如下:
- using System;
- using System.Collections;
- using UniRx;
- using UniRx.Diagnostics;
- using UnityEngine;
- using UnityEngine.Networking;
- using UnityEngine.UI;
- public class UniRxTest : MonoBehaviour
- {
- public Button mButton;
- public Toggle mToggle;
- public Text MyText;
- // 状态更改Model
- Enemy enemy = new Enemy(1000);
- void Start()
- {
- // 以响应式的方式从视图和模型中提供用户事件
- mButton.OnClickAsObservable().Subscribe(_ => enemy.CurrentHp.Value -= 100);
- mToggle.OnValueChangedAsObservable().SubscribeToInteractable(mButton);
- // Model通过Rx通知更新视图
- enemy.CurrentHp.SubscribeToText(MyText);
- enemy.IsDead.Where(isDead => isDead)
- .Subscribe(_ =>
- {
- mToggle.interactable = mButton.interactable = false;
- });
- }
- }
- // 所有属性的值更改时都会通知
- public class Enemy
- {
- public ReactiveProperty<long> CurrentHp { get; private set; }
- public ReadOnlyReactiveProperty<bool> IsDead { get; private set; }
- public Enemy(int initialHp)
- {
- // 声明属性
- CurrentHp = new ReactiveProperty<long>(initialHp);
- IsDead = CurrentHp.Select(x => x <= 10).ToReadOnlyReactiveProperty();
- }
- }
复制代码 4-6、相应式属性ReactiveProperty
UniRx还有一个很强的属性ReactiveProperty,也就是相应式属性,之以是强大,是因为它让变量的变化过程中可以增长更多的功能更加的灵活。
好比,要监听一个变量值是否发生变化,可以这么写:
- using System;
- using System.Collections;
- using UniRx;
- using UniRx.Diagnostics;
- using UnityEngine;
- using UnityEngine.Networking;
- using UnityEngine.UI;
- public class UniRxTest : MonoBehaviour
- {
- public int mAge;
- public int Age
- {
- get
- {
- return mAge;
- }
- set
- {
- if (mAge != value)
- {
- mAge = value;
- OnAgeChanged();
- }
- }
- }
- public void OnAgeChanged()
- {
- Debug.Log("Value变化了");
- }
- }
复制代码 上述代码固然也可以完成监听变量值变化的功能,但是如果要在外部访问,还需要写一个委托来监听,比较贫苦,如果用UniRx就会简朴许多:
- using System;
- using System.Collections;
- using UniRx;
- using UniRx.Diagnostics;
- using UnityEngine;
- using UnityEngine.Networking;
- using UnityEngine.UI;
- public class UniRxTest : MonoBehaviour
- {
- public ReactiveProperty<int> Age = new ReactiveProperty<int>();
- void Start()
- {
- // 绑定值
- Age.Subscribe(value =>
- {
- Debug.Log("通知值变化");
- });
- // 改变值可以用 变量.value来获取或者更改
- Age.Value = 5;
- }
- }
复制代码 4-7、Animation播放某一帧的动画
代码主要用到了UniRx.Async,背面UniRx.Async被分割成Cysharp/UniTask,需要再导入Cysharp/UniTask包,步骤如下:
(1)Window→Package Manager打开包管理器:

(2)选择Add package from git URL…

(3)添加 https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask 至包管理器

(4)导入完成

(5)修改Api Compatibility Level:

示例代码:
- using Cysharp.Threading.Tasks;
- using UniRx;
- using UnityEngine;
- public class UniRxTest : MonoBehaviour
- {
- private bool stopLoop = false;//动画控制
- void Start()
- {
- }
- /// <summary>
- /// Animation播放指定帧的动画
- /// </summary>
- /// <param name="myAnim">动画组件</param>
- /// <param name="startTimeInt">开始时间</param>
- /// <param name="endTimeInt">结束时间</param>
- private async void PlayAnimation(Animation myAnim, int startTimeInt, int endTimeInt)
- {
- int speed = GetSpeed(startTimeInt, endTimeInt);
- float frame = GetFrame(myAnim);
- float startTime;
- float endTime;
- if (speed == 1)
- {
- startTime = frame * startTimeInt;
- endTime = frame * endTimeInt;
- }
- else
- {
- startTime = frame * endTimeInt;
- endTime = frame * startTimeInt;
- }
- stopLoop = false;
- while (!stopLoop)
- {
- myAnim[myAnim.clip.name].time = startTime;//跳过开始帧
- myAnim[myAnim.clip.name].speed = speed;//正播还是倒播
- myAnim.Play(myAnim.clip.name);//Play()
- await UniTask.DelayFrame(1);//帧延缓,等Play()启动
- await UniTask.WaitUntil(() => myAnim[myAnim.clip.name].time > endTime);//播放到指定的进度点则停止
- myAnim.Stop();//停止播放
- stopLoop = true;//停止播放
- }
- }
- // 判断是正播还是倒播
- int GetSpeed(int startTime, int endTime)
- {
- if (endTime - startTime > 0)
- {
- return 1;
- }
- else if (endTime - startTime < 0)
- {
- return -1;
- }
- else
- {
- return 1;
- }
- }
- // 得到动画的播放帧率
- float GetFrame(Animation myAnim)
- {
- return myAnim[myAnim.clip.name].length / 100;
- }
- }
复制代码 五、跋文
讲解了UniRx插件以及UniTask插件的使用方法,难度有点高,适合渐渐学习(收藏吃灰)。
在学习过程中有什么不懂的都可以在博客主页找到我的联系方式。
你的点赞就是对博主的支持,有题目记得留言:
博主主页有联系方式。
博主还有跟多宝藏文章等待你的发掘哦:
专栏方向简介Unity3D开辟小游戏小游戏开辟教程分享一些使用Unity3D引擎开辟的小游戏,分享一些制作小游戏的教程。Unity3D从入门到进阶入门从自学Unity中获取灵感,总结从零开始学习Unity的蹊径,有C#和Unity的知识。Unity3D之UGUIUGUIUnity的UI体系UGUI全解析,从UGUI的底子控件开始讲起,然后将UGUI的原理,UGUI的使用全面讲授。Unity3D之读取数据文件读取使用Unity3D读取txt文档、json文档、xml文档、csv文档、Excel文档。Unity3D之数据聚集数据聚集数组聚集:数组、List、字典、堆栈、链表等数据聚集知识分享。Unity3D之VR/AR(假造仿真)开辟假造仿真总结博主工作常见的假造仿真需求举行案例讲解。Unity3D之插件插件主要分享在Unity开辟中用到的一些插件使用方法,插件先容等Unity3D之一样平常开辟一样平常记载主要是博主一样平常开辟中用到的,用到的方法本领,开辟思绪,代码分享等Unity3D之一样平常BUG一样平常记载记载在使用Unity3D编辑器开辟项目过程中,遇到的BUG和坑,让后来人可以有些参考。
来源:https://blog.csdn.net/q764424567/article/details/128804678
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |