[Unity学习教程] 【Unity3D插件】UniRx(基于Unity的相应式编程框架)插件教程

[复制链接]
查看639 | 回复0 | 2023-8-23 12:04:38 | 显示全部楼层 |阅读模式 来自 中国北京
保举阅读
  

  • 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:
  1. using System;
  2. using UniRx;
  3. using UnityEngine;
  4. public class UniRxTest : MonoBehaviour
  5. {
  6.     void Start()
  7.     {
  8.         // Observable.EveryUpdate调用协程的yield return null。
  9.         // 它位于Update之后,LateUpdate之前。
  10.         // Where等待操作的事件(当前事件是左键单击)
  11.         var doubleClick = Observable.EveryUpdate()
  12.             .Where(value => Input.GetMouseButtonDown(0));
  13.         // Buffer 添加一个事件
  14.         // Throttle 响应的最大间隔
  15.         // TimeSpan.FromMilliseconds(250) 设置为250毫秒
  16.         // Where 等待操作的事件(当前事件是左键单击)
  17.         // Subscribe 绑定委托
  18.         doubleClick.Buffer(doubleClick.Throttle(TimeSpan.FromMilliseconds(250)))
  19.             .Where(value => value.Count >= 2)
  20.             .Subscribe(value => Debug.Log("双击! 点击次数:" + value.Count));
  21.     }
  22. }
复制代码
运行效果:

这个Demo使用了5行代码就演示了以下功能:


  • Update作为事件流
  • 组合事件流
  • 合并自身流
  • 方便处理基于时间的使用
4-2、定时功能(与协程对比)

在平时项目开辟中,可能会遇到需要颠末一段时间出发某些逻辑的使用,可以用协程这么写:
  1. using System;
  2. using System.Collections;
  3. using UniRx;
  4. using UnityEngine;
  5. public class UniRxTest : MonoBehaviour
  6. {
  7.     void Start()
  8.     {
  9.         // 每5秒调用一次函数
  10.         StartCoroutine(Timer(5, DoSomething));
  11.     }
  12.     // 定时器
  13.     IEnumerator Timer(float seconds, Action callback)
  14.     {
  15.         yield return new WaitForSeconds(seconds);
  16.         callback();
  17.     }
  18.     // 调用函数
  19.     void DoSomething()
  20.     {
  21.         Debug.Log("TODO");
  22.     }
  23. }
复制代码
那么用UniRx怎么写呢:
  1. using System;
  2. using System.Collections;
  3. using UniRx;
  4. using UnityEngine;
  5. public class UniRxTest : MonoBehaviour
  6. {
  7.     void Start()
  8.     {
  9.         // 每5秒调用一次函数
  10.         Observable.Timer(TimeSpan.FromSeconds(5))
  11.            .Subscribe(value => {DoSomething();});
  12.     }
  13.     // 调用函数
  14.     void DoSomething()
  15.     {
  16.         Debug.Log("TODO");
  17.     }
  18. }
复制代码
乃至可以简化成一行代码:
  1. using System;
  2. using System.Collections;
  3. using UniRx;
  4. using UnityEngine;
  5. public class UniRxTest : MonoBehaviour
  6. {
  7.     void Start()
  8.     {
  9.         // 每5秒调用一次函数
  10.         Observable.Timer(TimeSpan.FromSeconds(5)).Subscribe(value => { Debug.Log("TODO"); });
  11.     }
  12. }
复制代码
为了制止this烧毁的时间,流程还没有烧毁的情况,可以加一行代码:
  1. using System;
  2. using System.Collections;
  3. using UniRx;
  4. using UnityEngine;
  5. public class UniRxTest : MonoBehaviour
  6. {
  7.     void Start()
  8.     {
  9.         // 每5秒调用一次函数
  10.         Observable.Timer(TimeSpan.FromSeconds(5))
  11.            .Subscribe(value => { DoSomething(); })
  12.            .AddTo(this);
  13.     }
  14.     // 调用函数
  15.     void DoSomething()
  16.     {
  17.         Debug.Log("TODO");
  18.     }
  19. }
复制代码
AddTo(this)之后,就会将延长和this(MonoBehaviour)绑定在一起了,当this被烧毁的时间,界说的流程也会被烧毁。
4-3、GET和POST使用

一般写法:
  1. using System;
  2. using System.Collections;
  3. using UniRx;
  4. using UnityEngine;
  5. using UnityEngine.Networking;
  6. public class UniRxTest : MonoBehaviour
  7. {
  8.     void Start()
  9.     {
  10.         StartCoroutine(RequestData("www.baidu.com", new WWWForm(), ReturnValue));
  11.     }
  12.     //回调函数
  13.     private void ReturnValue(string value)
  14.     {
  15.         Debug.Log(value);
  16.     }
  17.     /// <summary>
  18.     /// 数据请求与发送
  19.     /// </summary>
  20.     /// <param name="url">请求的url</param>
  21.     /// <param name="form">表单</param>
  22.     /// <param name="dele">返回数据</param>
  23.     /// <returns></returns>
  24.     private IEnumerator RequestData(string url, WWWForm form, Action<string> dele = null)
  25.     {
  26.         UnityWebRequest req = UnityWebRequest.Post(url, form);
  27.         yield return req.SendWebRequest();
  28.         if (req.result == UnityWebRequest.Result.ProtocolError)
  29.         {
  30.             dele?.Invoke(req.error);
  31.         }
  32.         if (req.isDone)
  33.         {
  34.             dele?.Invoke(req.downloadHandler.text);
  35.         }
  36.     }
  37. }
复制代码
用UniRx写法:
  1. using System;
  2. using System.Collections;
  3. using UniRx;
  4. using UnityEngine;
  5. using UnityEngine.Networking;
  6. public class UniRxTest : MonoBehaviour
  7. {
  8.     void Start()
  9.     {
  10.         var request = ObservableWWW.Post("www.baidu.com", new WWWForm())
  11.             .Subscribe(value => Debug.Log(value))
  12.             .AddTo(this);
  13.     }
  14. }
复制代码
  留意:不是讨论那个写法好,那么写法欠好,只是使用UniRx更加简洁,更保举是用UnityWebRequest,因为UnityWebRequest功能更完善,更加有效。
  4-4、加载场景-AsyncOperation

在异步加载资源大概异步加载场景的时间通常会用到 AsyncOperation。
UniRx 对 AsyncOperation 做了支持。使得加载进度可以很容易地监听。
示例代码如下:
  1. using UniRx;
  2. using UnityEngine;
  3. using UnityEngine.SceneManagement;
  4. namespace UniRxLesson
  5. {
  6.     public class AsyncOperationExample : MonoBehaviour
  7.     {
  8.         void Start()
  9.         {
  10.             var progressObservable = new ScheduledNotifier();
  11.             SceneManager.LoadSceneAsync(0).AsAsyncOperationObservable(progressObservable)
  12.                         .Subscribe(asyncOperation =>
  13.                         {
  14.                             Debug.Log("load done");
  15.                             Resources.LoadAsync("TestCanvas").AsAsyncOperationObservable()
  16.                                      .Subscribe(resourceRequest =>
  17.                                      {
  18.                                          Instantiate(resourceRequest.asset);
  19.                                      });
  20.                         });
  21.             progressObservable.Subscribe(progress =>
  22.             {
  23.                 Debug.LogFormat("加载了:{0}", progress);
  24.             });
  25.         }
  26.     }
  27. }
复制代码
4-5、UGUI支持

示例代码:
  1. using System;
  2. using System.Collections;
  3. using UniRx;
  4. using UniRx.Diagnostics;
  5. using UnityEngine;
  6. using UnityEngine.Networking;
  7. using UnityEngine.UI;
  8. public class UniRxTest : MonoBehaviour
  9. {
  10.     public Button mButton;
  11.     public Toggle mToggle;
  12.     public InputField mInput;
  13.     public Text mText;
  14.     public Slider mSlider;
  15.     void Start()
  16.     {
  17.         // Button 按钮绑定事件
  18.         mButton.onClick.AsObservable().Subscribe(_ => Debug.Log("clicked"));
  19.         // Toggle 控制其他UI对象的激活
  20.         mToggle.OnValueChangedAsObservable().SubscribeToInteractable(mButton);
  21.         // mInput Where筛选值不等于空的情况 绑定Text组件
  22.         mInput.OnValueChangedAsObservable()
  23.         .Where(x => x != null)
  24.         .SubscribeToText(mText);
  25.         // mSlider 绑定Text组件
  26.         mSlider.OnValueChangedAsObservable()
  27.             .SubscribeToText(mText, x => Math.Round(x, 2).ToString());
  28.     }
  29. }
复制代码
  可以看出来,UniRx去绑定UI照旧很好用的,但不但于此。
  使用 UniRx可以很容易地实现 MVP(MVRP)计划模式。
为什么应该用 MVP模式而不是 MVVM模式?Unity 没有提供 UI 绑定机制,创建一个绑定层过于复杂而且会对性能造成影响(使用反射)。只管云云,视图照旧需要更新。 Presenters层知道 View 组件而且能更新它们。
固然没有真的绑定,但 Observables 可以关照订阅者,功能上也差不多。这种模式叫做 Reactive Presenter 计划模式,示例代码如下:
  1. using System;
  2. using System.Collections;
  3. using UniRx;
  4. using UniRx.Diagnostics;
  5. using UnityEngine;
  6. using UnityEngine.Networking;
  7. using UnityEngine.UI;
  8. public class UniRxTest : MonoBehaviour
  9. {
  10.     public Button mButton;
  11.     public Toggle mToggle;
  12.     public Text MyText;
  13.     // 状态更改Model
  14.     Enemy enemy = new Enemy(1000);
  15.     void Start()
  16.     {
  17.         // 以响应式的方式从视图和模型中提供用户事件
  18.         mButton.OnClickAsObservable().Subscribe(_ => enemy.CurrentHp.Value -= 100);
  19.         mToggle.OnValueChangedAsObservable().SubscribeToInteractable(mButton);
  20.         // Model通过Rx通知更新视图
  21.         enemy.CurrentHp.SubscribeToText(MyText);
  22.         enemy.IsDead.Where(isDead => isDead)
  23.             .Subscribe(_ =>
  24.             {
  25.                 mToggle.interactable = mButton.interactable = false;
  26.             });
  27.     }
  28. }
  29. // 所有属性的值更改时都会通知
  30. public class Enemy
  31. {
  32.     public ReactiveProperty<long> CurrentHp { get; private set; }
  33.     public ReadOnlyReactiveProperty<bool> IsDead { get; private set; }
  34.     public Enemy(int initialHp)
  35.     {
  36.         // 声明属性
  37.         CurrentHp = new ReactiveProperty<long>(initialHp);
  38.         IsDead = CurrentHp.Select(x => x <= 10).ToReadOnlyReactiveProperty();
  39.     }
  40. }
复制代码
4-6、相应式属性ReactiveProperty

UniRx还有一个很强的属性ReactiveProperty,也就是相应式属性,之以是强大,是因为它让变量的变化过程中可以增长更多的功能更加的灵活。
好比,要监听一个变量值是否发生变化,可以这么写:
  1. using System;
  2. using System.Collections;
  3. using UniRx;
  4. using UniRx.Diagnostics;
  5. using UnityEngine;
  6. using UnityEngine.Networking;
  7. using UnityEngine.UI;
  8. public class UniRxTest : MonoBehaviour
  9. {
  10.     public int mAge;
  11.     public int Age
  12.     {
  13.         get
  14.         {
  15.             return mAge;
  16.         }
  17.         set
  18.         {
  19.             if (mAge != value)
  20.             {
  21.                 mAge = value;
  22.                 OnAgeChanged();
  23.             }
  24.         }
  25.     }
  26.     public void OnAgeChanged()
  27.     {
  28.         Debug.Log("Value变化了");
  29.     }
  30. }
复制代码
上述代码固然也可以完成监听变量值变化的功能,但是如果要在外部访问,还需要写一个委托来监听,比较贫苦,如果用UniRx就会简朴许多:
  1. using System;
  2. using System.Collections;
  3. using UniRx;
  4. using UniRx.Diagnostics;
  5. using UnityEngine;
  6. using UnityEngine.Networking;
  7. using UnityEngine.UI;
  8. public class UniRxTest : MonoBehaviour
  9. {
  10.     public ReactiveProperty<int> Age = new ReactiveProperty<int>();
  11.     void Start()
  12.     {
  13.         // 绑定值
  14.         Age.Subscribe(value =>
  15.         {
  16.             Debug.Log("通知值变化");
  17.         });
  18.         // 改变值可以用 变量.value来获取或者更改
  19.         Age.Value = 5;
  20.     }
  21. }
复制代码
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:

示例代码:
  1. using Cysharp.Threading.Tasks;
  2. using UniRx;
  3. using UnityEngine;
  4. public class UniRxTest : MonoBehaviour
  5. {
  6.     private bool stopLoop = false;//动画控制
  7.     void Start()
  8.     {
  9.     }
  10.     /// <summary>
  11.     /// Animation播放指定帧的动画
  12.     /// </summary>
  13.     /// <param name="myAnim">动画组件</param>
  14.     /// <param name="startTimeInt">开始时间</param>
  15.     /// <param name="endTimeInt">结束时间</param>
  16.     private async void PlayAnimation(Animation myAnim, int startTimeInt, int endTimeInt)
  17.     {
  18.         int speed = GetSpeed(startTimeInt, endTimeInt);
  19.         float frame = GetFrame(myAnim);
  20.         float startTime;
  21.         float endTime;
  22.         if (speed == 1)
  23.         {
  24.             startTime = frame * startTimeInt;
  25.             endTime = frame * endTimeInt;
  26.         }
  27.         else
  28.         {
  29.             startTime = frame * endTimeInt;
  30.             endTime = frame * startTimeInt;
  31.         }
  32.         stopLoop = false;
  33.         while (!stopLoop)
  34.         {
  35.             myAnim[myAnim.clip.name].time = startTime;//跳过开始帧
  36.             myAnim[myAnim.clip.name].speed = speed;//正播还是倒播
  37.             myAnim.Play(myAnim.clip.name);//Play()
  38.             await UniTask.DelayFrame(1);//帧延缓,等Play()启动                                                               
  39.             await UniTask.WaitUntil(() => myAnim[myAnim.clip.name].time > endTime);//播放到指定的进度点则停止        
  40.             myAnim.Stop();//停止播放
  41.             stopLoop = true;//停止播放
  42.         }
  43.     }
  44.     // 判断是正播还是倒播
  45.     int GetSpeed(int startTime, int endTime)
  46.     {
  47.         if (endTime - startTime > 0)
  48.         {
  49.             return 1;
  50.         }
  51.         else if (endTime - startTime < 0)
  52.         {
  53.             return -1;
  54.         }
  55.         else
  56.         {
  57.             return 1;
  58.         }
  59.     }
  60.     // 得到动画的播放帧率
  61.     float GetFrame(Animation myAnim)
  62.     {
  63.         return myAnim[myAnim.clip.name].length / 100;
  64.     }
  65. }
复制代码
五、跋文

讲解了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
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则