Unity官方开发者社区:详解UniRx插件的使用
创作时间:
2025-03-15 12:04:57
作者:
@小白创作中心
Unity官方开发者社区:详解UniRx插件的使用
引用
CSDN
1.
https://unity.csdn.net/677b6d2861f6a61b9464ef74.html
UniRx是Unity官方提供的一个响应式编程库,它通过观察者模式简化了事件和异步操作的管理。本文将详细介绍UniRx的核心概念、常用API,并通过具体代码示例展示如何使用UniRx改写传统的协程实现。
一、定义
UniRx是一个用于Unity的响应式编程库,它通过响应式编程的模式来简化事件和异步操作的管理。
二、核心组成
- Observable(可观察的):表示一个数据流或事件流。
- Operators(操作符):用于转换、组合和过滤Observable的工具。
- Subscription(订阅):观察者订阅一个Observable来接收事件。
三、常用API
(1)常用UI事件
button.OnClickAsObservable()
用于监听UI中按钮被按下事件。
// UniRx 方式(Observable)
button.OnClickAsObservable()
.Subscribe(_ => Debug.Log("Button clicked"));
_ =>
是一个简化的Lambda表达式语法,表示在这个订阅的过程中不需要使用传入的参数。如果关心事件传递的参数,可以使用其他名称来接收这个参数。比如:
button.OnClickAsObservable()
.Subscribe(button => Debug.Log("Button clicked: " + button.name));
(2)操作符
- Select:用于映射Observable中的数据
Observable.Range(1, 5)
.Select(x => x * 2)
.Subscribe(x => Debug.Log(x));
- Where:用于过滤Observable中的数据
Observable.Range(1, 5)
.Where(x => x % 2 == 0)
.Subscribe(x => Debug.Log(x)); // 输出:2, 4
- TakeWhile:当条件不满足时停止流
(3)订阅与取消订阅
- Subscribe:订阅Observable,事件发生后,执行某个操作
Observable.Range(1, 5)
.Subscribe(x => Debug.Log(x));
- AddTo:将订阅与MonoBehaviour或GameObject生命周期绑定,自动取消订阅
IDisposable subscription = Observable.EveryUpdate()
.Subscribe(_ => Debug.Log("Every frame"))
.AddTo(this);
四、优势
UniRx避免了传统回调函数的复杂性,并且可以通过AddTo()
自动管理生命周期,有效防止内存泄漏。
五、注意
订阅应该放在Start()
中,而不是Update()
中,因为后者意味着每一帧都会创建一个新的订阅。但大多数的订阅只需要在对象被激活时创建。
六、使用UniRx改写协程
源代码
每帧检测是否满足调用协程的条件:
void Update()
{
//Enable lazer
if (Input.GetMouseButtonDown(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
//如果射线检测到了Enemy层
if (Physics.Raycast(ray, out RaycastHit hit, Mathf.Infinity,layer))
{
targetEnemy = hit.collider.transform.root.gameObject;
Debug.Log($"目标敌军是:{targetEnemy.name}");
StartCoroutine(AimAtEnemy());
}
}
}
协程实现:
private IEnumerator AimAtEnemy()
{
// 确保 MultiHingeJoint 实例存在
if (MultiHingeJoint.Instance == null)
{
Debug.LogError("MultiHingeJoint 实例不存在");
yield break;
}
// 瞄准目标
bool isYAligned = false;
bool isXAligned = false;
MultiHingeJoint.Instance.RotateAroundY(aimPart1, FirePoint, targetEnemy);
// 等待炮筒对准目标
while (!isYAligned )
{
// 检查当前是否已经对准
isYAligned = CheckIfAimed(aimPart1, targetEnemy.transform, "Y");
yield return null; // 等待下一帧
}
MultiHingeJoint.Instance.RotateAroundX(aimPart2, FirePoint, targetEnemy);
while (!isXAligned)
{
isXAligned = CheckIfAimed(aimPart2, targetEnemy.transform, "X");
yield return null; // 等待下一帧
}
Debug.Log("炮筒对准完成,发射激光!");
}
代码改写
判断逻辑改写:
void Start()
{
// 使用 UniRx 订阅每帧更新
Observable.EveryUpdate()
.Where(_ => Input.GetMouseButtonDown(0)) // 检测鼠标左键点击
.Select(_ => Camera.main.ScreenPointToRay(Input.mousePosition)) // 计算射线
.Where(ray => Physics.Raycast(ray, out RaycastHit hit, Mathf.Infinity, layer)) // 射线检测是否击中目标
.Subscribe(hit =>
{
targetEnemy = hit.collider.transform.root.gameObject; // 获取目标敌人
Debug.Log($"目标敌军是:{targetEnemy.name}");
AimAtEnemy().Subscribe().AddTo(this); // 启动瞄准过程
})
.AddTo(this); // 确保在对象销毁时取消订阅
}
协程部分改写:
private IObservable<Unit> AimAtEnemy()
{
// 确保 MultiHingeJoint 实例存在
if (MultiHingeJoint.Instance == null)
{
Debug.LogError("MultiHingeJoint 实例不存在");
return Observable.Empty<Unit>(); // 返回空的 Observable,表示终止操作
}
// 瞄准目标
return Observable.Create<Unit>(observer =>
{
bool isYAligned = false;
bool isXAligned = false;
// 旋转炮筒对准 Y 轴
MultiHingeJoint.Instance.RotateAroundY(aimPart1, FirePoint, targetEnemy);
// 等待炮筒对准 Y 轴
Observable.EveryUpdate()
.Where(_ => !isYAligned) // 只在未对准时继续检查
.TakeWhile(_ => !isYAligned) // 直到条件不满足时停止
.Subscribe(_ =>
{
isYAligned = CheckIfAimed(aimPart1, targetEnemy.transform, "Y");
});
// 旋转炮筒对准 X 轴
MultiHingeJoint.Instance.RotateAroundX(aimPart2, FirePoint, targetEnemy);
// 等待炮筒对准 X 轴
Observable.EveryUpdate()
.Where(_ => !isXAligned) // 只在未对准时继续检查
.TakeWhile(_ => !isXAligned) // 直到对准完成
.Subscribe(_ =>
{
isXAligned = CheckIfAimed(aimPart2, targetEnemy.transform, "X");
});
// 等待两个轴都对准完成
Observable.EveryUpdate()
.Where(_ => isYAligned && isXAligned) // 检查是否两个轴都对准
.Take(1) // 只需要触发一次
.Subscribe(_ =>
{
Debug.Log("炮筒对准完成,发射激光!");
EmitLaser(); // 发射激光
observer.OnCompleted(); // 结束 Observable 操作
});
return Disposable.Empty; // 结束 Observable
});
}
知识点
- Observable.Create
:用来创建一个自定义的Observable。 Observable.Create
让我们能够手动控制数据流的发出。observer
是一个对象,用来控制Observable的状态。 - Disposable.Empty:表示返回一个空的Disposable对象,这意味着没有其他资源需要清理。这个方法在Observable执行完后自动终止。一般和create成对存在。
- observer.OnCompleted():通知观察者,表示Observable数据流已经完成。这意味着所有数据已经发送完毕,没有更多的数据将会被发出。
更多关于UniRx的详细内容可以参考:知乎专栏
热门推荐
银行的信用卡还款方式的选择与资金流动性?
如何定位和更换汽车发动机传感器?传感器故障对发动机性能有何影响?
传统二十四节气之大雪:气候特征与民俗文化
桂花红茶:红茶与桂花的完美邂逅
陈皮白茶的正确泡法?记住三大要点!
传媒专业包括哪些方向
开源协议与商业许可:选择与遵循
斩断非法代孕链条
项目管理费发票怎么开具
喝黑咖啡的危害和副作用
电动汽车“原地掉头”技术解析
未进行食品留样如何处罚?专家详解相关法律法规及处罚措施
头孢多久能代谢掉
利用绘本激发幼儿阅读兴趣的方法
从2到N的GPU互联:NVLink与NVSwitch技术详解
CNN的多重含义:从全球新闻媒体到深度学习技术的应用解析
八皇后问题的两种解法:回溯法与位运算优化
中国游泳运动员药检集体阳性?系食物污染!早已结案定性
深蹲的好处功效与作用
【柳叶刀】剖宫产史女性福音!自主选择分娩方式竟能降低28%并发症风险?
焰灵姬:《天行九歌》中的神秘火焰使者
奔四女人早春如何穿出好气质?建议运用这些搭配技巧,实用不踩雷
《滤镜》原著作者揭秘:罗小葶打造的奇幻爱情故事
四六级考试备考指南:五大学科关卡通关秘籍
江门新会以监督守护村集体“三资”
误删软件别急!这份“找回秘籍”让你轻松复原
腊梅花适合种在北方户外吗?这些种植要点请收好
朋友之间最好的相处方式,不是吃喝玩乐,而是做好这4件事!
被成见掩埋的人生,你我都是受害者?
家里没在用水,为什么水表却在转?