UniRx精讲(一):UniRx简介&定时功能实现
1.UniRx 简介
UniRx 是一个 Unity3D 的编程框架。它专注于解决时间上异步的逻辑,使得异步逻辑的实现更加简洁和优雅。
简洁优雅如何体现?
比如,实现一个“只处理第一次鼠标点击事件”这个功能,使用 UniRx 实现如下:
Observable.EveryUpdate()
.Where(_ => Input.GetMouseButtonUp(0))
.First()
.Subscribe(_ => { // do something });
代码做的事情很简单:
- 开启一个 Update 的事件监听。
- 每次 Update 事件被调用时,进行鼠标是否抬起的判断。
- 如果判断通过,则进行计数,并且只获取第一次点击事件。
- 订阅/处理事件。
如果在 Unity 中,使用传统的方式实现如上功能,首先要创建一个成员变量来记录点击次数/是否点击过,然后在脚本中创建一个 Update 方法来监听鼠标抬起的事件。
如果在 Update 方法中,除了实现鼠标事件监听这个功能之外,还要实现其他的功能。那么 Update 里就会充斥着大量的状态判断等逻辑。代码非常不容易阅读。
而 UniRx 提供了一种编程思维,使得平时一些比较难以实现的异步逻辑(比如以上这种),使用 UniRx 轻松搞定,并且不失代码的可读性。
当然 UniRx 的强大不仅仅如此。
它还可以:
- 优雅实现 MVP(MVC)架构模式。
- 对 UGUI/Unity API 提供了增强,很多需要写大量代码的 UI 逻辑,使用 UniRx 优雅实现。
- 轻松完成非常复杂的异步任务处理。
- ......
最最重要的是,它可以提高我们的编码效率,同时还给我们的大脑提供一个强有力的编程模型。而 UniRx 本身的源码非常值得研究学习,就连大名鼎鼎的 uFrame 框架,在 1.6 版本之后,使用 UniRx 进行了大幅重构,其事件/数据绑定层使用 UniRx 强力驱动。
笔者的 QFramework 也同样引入了 UniRx,有一大批的框架用户都是因为支持了 UniRx 慕名而来。
为什么要用 UniRx ?
UniRx 就是 Unity 版本的 Reactive Extensions,Reactive Extensions 中文意思是:响应式扩展,响应式指的是观察者和定时器,扩展指的是 LINQ 的操作符。Reactive Extensions 以擅长处理时间上异步的逻辑、以及极简的 API 风格而闻名。
我们都知道,游戏很多的系统都是在时间上异步的,所以 Unity 开发者所需要实现的异步逻辑是非常多的。这也是为什么 Unity 官方在引擎层实现了 Coroutine(协程)这样的概念。
在游戏中,像动画的播放、声音的播放、网络请求、资源加载/卸载、Tween 动画、场景过渡等都是在时间上异步的逻辑。甚至是游戏循环(Every Update、OnCollisionEnter 等)、传感器数据(Kinect、Leap Motion、VR Input 等)都是时间上异步的逻辑(事件)。
当我们在项目中实现以上的逻辑的时候,往往使用的方式是用大量的回调实现,最终随着项目的扩张会导致传说中的”回调地狱”。
相对较好的方法则是使用消息/事件进行实现,结果导致“消息满天飞”,导致代码非常难以阅读。
以上的任务使用 Coroutine 也是非常不错的,但是 Coroutine 在 Unity 使用的时候,需要定义一个方法。写起来是非常面向过程的。当逻辑稍微复杂一点,就很容易造成 Coroutine 嵌套 Coroutine,代码是非常不容易阅读的(强耦合)。
而 UniRx 的出现刚好解决了这个问题,它介于回调和事件之间。
它有事件的概念,只不过它的事件是像流水一样流过来,而我们要做的则是简单地对这些事件进行组织、变换、过滤、合并就可以得到我们想要的结果了。
它也用掉了回调,只不过它的回调是在事件经过组织之后,只需要调用一次就可以进行事件处理了。
它的原理和 Coroutine (迭代器模式)、LINQ 非常相似,但是比 Coroutine 强大得多。
UniRx 将时间上异步的事件转化为响应式的事件序列,通过 LINQ操作可以很简单地组合起来。
为什么要用 UniRx? 答案就是游戏本身有大量的在时间上异步的逻辑,而 UniRx 恰好擅长处理这类逻辑,使用 UniRx 可以节省我们的时间,同时让代码更简洁易读。
Rx 只是一套标准,其他的语言也有实现,如果在 Unity 中熟悉了这套标准,那么在未来,大家在做别的语言的项目的时候,很容易获得 Rx 的能力。
专栏内容:
- 实践并讲解开发中最常用的 UniRx API。
- 对 UniRx 进行一个全方面的简介。
- 在每个阶段结束后就会结合所学的知识进行项目实践。
- UniRx 操作符大全。
- UniRx 源码阅读。
- UniRx 背后的设计原理及设计模式学习。
- LINQ、Coroutine 底层原理剖析。
- BindingsRx、uFrame 源码阅读。
OK,让我们从下一篇开始,感受一下 UniRx 的魅力吧。
知识地图

2.定时功能实现
在 Unity 开发中,延时是我们经常要实现的功能,这个功能对于有点经验的开发者来说不难。
最常见的实现方式如下:
using UnityEngine;
public class CommonDelayExample : MonoBehaviour
{
private float mStartTime;
void Start()
{
mStartTime = Time.time;
}
void Update()
{
if (Time.time - mStartTime > 5)
{
DoSomething();
// 避免再次执行
mStartTime = float.MaxValue;
}
}
void DoSomething()
{
Debug.Log("DoSomething");
}
}
这是很多初学者刚入门时候的实现方式,而初学者们随着深入学习后来接触到了 Coroutine(协程),使用 Coroutine 实现定时功能会更容易,而且也是更好的选择,实现如下:
using System;
using System.Collections;
using UnityEngine;
public class CoroutineDelayExample : MonoBehaviour
{
void Start()
{
StartCoroutine(Timer(5, DoSomething));
}
IEnumerator Timer(float seconds, Action callback)
{
yield return new WaitForSeconds(seconds);
callback();
}
void DoSomething()
{
Debug.Log("DoSomething");
}
}
协程已经把代码精简了很多,不过接下来有更厉害的,使用 UniRx。
代码如下:
Observable.Timer(TimeSpan.FromSeconds(5)).Subscribe(_ => { /* do something */ });
当然以上代码是没有和 MonoBehaviour 进行生命周期绑定的,也就是说当 MonoBehaviour 销毁了之后,这个定时逻辑可能还在执行,这样就会有造成空指针异常的风险。
要解决也很简单,代码如下:
Observable.Timer(TimeSpan.FromSeconds(5))
.Subscribe(_ => { /* do something */ })
.AddTo(this);
只要加上一个 AddTo(this) 就可以了。
这样,当 this(MonoBehaviour) Destroy 的时候,这个延时逻辑也会销毁掉,从而避免造成空指针异常。
三行代码,写下来大约 20 秒的时间,就搞定了一个实现起来比较麻烦的逻辑。
今天的内容就这些。
知识地图

转载请注明地址:liangxiegame.com
更多内容
QFramework 地址:https://github.com/liangxiegame/QFramework
QQ 交流群:623597263
Unity 进阶小班:

- 权益、授课形式等具体详情请查看《小班产品手册》:https://liangxiegame.com/master/intro
关注公众号:liangxiegame 获取第一时间更新通知及更多的免费内容。

UniRx精讲(一):UniRx简介&定时功能实现的更多相关文章
- UniRx精讲(二):独立的 Update &UniRx 的基本语法格式
独立的 Update 在 UniRx 简介的时候,笔者讲了一种比较麻烦的情况:就是在 MonoBehaviour 的 Update 中掺杂了大量互相无关的逻辑,导致代码非常不容易阅读. 这种情况我们平 ...
- 第三百六十九节,Python分布式爬虫打造搜索引擎Scrapy精讲—elasticsearch(搜索引擎)用Django实现搜索功能
第三百六十九节,Python分布式爬虫打造搜索引擎Scrapy精讲—elasticsearch(搜索引擎)用Django实现搜索功能 Django实现搜索功能 1.在Django配置搜索结果页的路由映 ...
- 第三百六十八节,Python分布式爬虫打造搜索引擎Scrapy精讲—elasticsearch(搜索引擎)用Django实现搜索的自动补全功能
第三百六十八节,Python分布式爬虫打造搜索引擎Scrapy精讲—用Django实现搜索的自动补全功能 elasticsearch(搜索引擎)提供了自动补全接口 官方说明:https://www.e ...
- Linux实战教学笔记18:linux三剑客之awk精讲
Linux三剑客之awk精讲(基础与进阶) 标签(空格分隔): Linux实战教学笔记-陈思齐 快捷跳转目录: * 第1章:awk基础入门 * 1.1:awk简介 * 1.2:学完awk你可以掌握: ...
- 【原创】分布式之redis复习精讲
引言 为什么写这篇文章? 博主的<分布式之消息队列复习精讲>得到了大家的好评,内心诚惶诚恐,想着再出一篇关于复习精讲的文章.但是还是要说明一下,复习精讲的文章偏面试准备,真正在开发过程中, ...
- 转 Redis 总结精讲 看一篇成高手系统-4
转 Redis 总结精讲 看一篇成高手系统-4 2018年05月31日 09:00:05 hjm4702192 阅读数:125633 本文围绕以下几点进行阐述 1.为什么使用redis 2.使用r ...
- Keepalived原理与实战精讲--VRRP协议
. 前言 VRRP(Virtual Router Redundancy Protocol)协议是用于实现路由器冗余的协议,最新协议在RFC3768中定义,原来的定义RFC2338被废除,新协议相对还简 ...
- 分布式之redis复习精讲
看到一片不错的精简的redis文档,转载之,便于复习梳理之用 转自:https://www.cnblogs.com/rjzheng/p/9096228.html ------------------- ...
- Linux高频命令精讲(三)
[教程主题]:2.Linux高频命令精讲 [2.1]Linux的运行方式 图形运行方式 - 本地使用KDE/Gnome集成环境 - 运行X Server远程使用图形环境 命令行(字符运行)方式 - 本 ...
随机推荐
- 案例 (一)如何把python项目部署到linux服务器上
一.背景 用Python写了个脚本,需要部署到Linux环境的服务器上,由于服务器linux系统(centos,redhat等)自带的是python2,现在的python萌新都是从python3开 ...
- Django之ORM对象关系模型
MVC或者MVC框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库,这极大的减轻了开发人员的工作量,不需 ...
- .NET 合并程序集(将 dll 合并到 exe 中)
------------恢复内容开始------------ ------------恢复内容开始------------ 背景:我们的应用程序通常都是由多个程序集组成,例如一个 exe 程序依赖于多 ...
- 可持续字典树 Perfect Security
题目链接 题目大意:给你两个序列,第二个序列可以任意进行排列变换,然后由这两个序列一一异或得到答案序列,要求答案序列的字典序最小. 可持续字典树与第K大可持续线段树的区别主要在于每个节点上 ,它多了一 ...
- CF916C
题目链接:http://codeforces.com/contest/916/problem/C 题目大意: 用\(n\)个结点,\(m\)条边(每条边的权值范围为[1, 109]) 构造出一个无向带 ...
- 【Java】手把手理解CAS实现原理
先来看看概念,[CAS] 全称“CompareAndSwap”,中文翻译即“比较并替换”. 定义:CAS操作包含三个操作数 —— 内存位置(V),期望值(A),和新值(B). 如果内存位置的值与期望值 ...
- Msql 给结果拼接字符串
SELECT CONCAT("内容:",info)AS info FROM 表名;
- spark学习笔记总结
Spark简介 spark 可以很容易和yarn结合,直接调用HDFS.Hbase上面的数据,和hadoop结合.配置很容易. spark发展迅猛,框架比hadoop更加灵活实用.减少了延时处理,提高 ...
- Android_基础之分辨率
常见屏幕分辨率对应尺寸 标屏 分辨率 比例 宽屏 分辨率 比例 QCIF 176X144 11:9 CIF 352X288 11:9 QVGA 320X240 4:3 WQVG ...
- 题解 P4071 【[SDOI2016]排列计数】 (费马小定理求组合数 + 错排问题)
luogu题目传送门! luogu博客通道! 这题要用到错排,先理解一下什么是错排: 问题:有一个数集A,里面有n个元素 a[i].求,如果将其打乱,有多少种方法使得所有第原来的i个数a[i]不在原来 ...