AssetStore链接:

UniRx - Reactive Extensions for Unity | Integration | Unity Asset Store

一、定义

UniRx 是一个用于 Unity 的响应式编程库,它通过响应式编程的模式来简化事件和异步操作的管理。

二、核心组成

· Observable(可观察的):表示一个数据流或事件流。

· Operators(操作符):用于转换、组合和过滤 Observable 的工具。

· Subscription(订阅):观察者订阅一个 Observable 来接收事件。

三、常用API

(1)常用ui事件

·button.OnClickAsObservable():ui中按钮被按下事件

// UniRx 方式(Observable)
button.OnClickAsObservable()
    .Subscribe(_ => Debug.Log("Button clicked"));

_ => 是一个简化的 Lambda 表达式语法,表示在这个订阅的过程中不需要使用传入的参数。如果关心事件传递的参数,可以使用其他名称来接收这个参数。比如:button 参数是按钮对象,可以访问它的属性(如 name)来获得更多信息。

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

· Takewhere:当条件不满足时停止流

(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);

 四、优势

避免传统回调函数的复杂性,并且可以通过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
        });
    }

知识点:

 (1)Observable.Create<Unit> 来创建一个自定义的 Observable。Observable.Create 让我们能够手动控制数据流的发出。observer 是一个对象,用来控制 Observable 的状态

(2)Disposable.Empty 表示返回一个空的 Disposable 对象,这意味着没有其他资源需要清理。这个方法在 Observable 执行完后自动终止。一般和create成对存在

(3)observer.OnCompleted() 是 通知观察者,表示 Observable 数据流已经完成。这意味着所有数据已经发送完毕,没有更多的数据将会被发出。

网上写unixr比较详细的文章:https://zhuanlan.zhihu.com/p/420299920

Logo

分享前沿Unity技术干货和开发经验,精彩的Unity活动和社区相关信息

更多推荐