Unity官方开发者社区:详解UniRx插件的使用
创作时间:
作者:
@小白创作中心
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的详细内容可以参考:知乎专栏
热门推荐
盐酸纳洛酮舌下片:解酒神器还是醒酒救星?
美他多辛:解酒界的“超级英雄”
急性失血性贫血怎么补充营养
宣布“解约”的NewJeans,为何说不采取“法律措施”?
NewJeans直播懒人包 一文看懂制作人和母公司的风波
佟丽娅舞蹈剧场:新疆多元文化的艺术盛宴
NewJeans现身解约后首个行程,成员帽子口罩加墨镜成标配
佟丽娅的舞蹈密码:从锡伯族姑娘到舞台女王
冬日暖阳下的最美山水头像
精选山水头像,领略自然瑰宝与壮丽山脉
《食品科学》:江南大学刘元法教授等:油脂组成和结晶特性对奶油品质和搅打性能的影响
佟丽娅《误杀3》路演:温柔比心背后的艺术追求
古镇快线全线通车,中山市民出行更快捷
沙古公路今日通车,古镇到城区只需20分钟
阳宅风水的前世今生揭秘
学之道国学平台教你现代阳宅风水:科学与传统的完美融合
倪海厦&谢咏揭秘:巽山乾向兼巳亥阳宅的风水布局之道
《漂白》抄袭《漂白》?
佟丽娅国家大剧院舞剧视觉盛宴
佟丽娅:在《舞蹈生》中展现舞蹈与表演的完美融合
猪大肠清洗秘籍,老司机带你飞!
面粉+白醋,猪大肠秒变无异味神器
奶白菜汤火遍全网,你学会了吗?
白菜品种大揭秘:从奶白菜到大白菜,你真的分得清吗?
奶白菜PK大白菜:谁才是冬日餐桌的C位?
2025年春节日历表:春节的传统活动有哪些
郑法祥版《闹天宫》:京剧猴戏的巅峰之作
从86版到黑神话:悟空,孙悟空形象的进化史
国漫崛起:孙悟空形象的创意改编与文化新生
《大话西游》里的孙悟空:一个更有人性的英雄