Skip to content
主页博客推荐博客

[UniRx] 操作符 - 转换可观察对象(Transforming Observables) 第二篇

  • #UniRx
  • #教程
Read time: 1 minute
Yaoming
示例代码

代码由GPT-4o等多种AI参与生成,并通过人工审查确保可执行.

登录后下载

UniRx中,Transforming Observables,是指对Observable数据流进行转换的操作。通过这些操作符,可以将一个Observable的数据流转换为另一种形式,或者对数据进行处理、筛选、组合等操作。这些操作符是响应式编程的核心,允许你以声明式的方式处理数据流,而无需手动管理复杂的状态或回调逻辑。

1. Buffer

Buffer操作符会定期收集Observable中的值,并将它们作为一个集合发射,而不是逐个发射。

使用场景
  • 批量处理数据,例如每 3 次用户点击后统一处理一次。

示例:

每 3 次鼠标点击作为一组发射

Observable.EveryUpdate()  
    .Where(_ => Input.GetMouseButtonDown(0)) // 检测鼠标点击  
    .Buffer(3) // 每 3 次点击作为一组  
    .Subscribe(clicks =>  
    {  
        Debug.Log($"Mouse clicked {clicks.Count} times: {string.Join(", ", clicks)}");  
    });

输出:

每 3 次点击后打印:

Mouse clicked 3 times: 0, 0, 0  
Mouse clicked 3 times: 0, 0, 0

示例:玩家连击检测

在游戏中,玩家快速点击攻击按钮可以触发连击技能。

 Observable.EveryUpdate()  
    .Where(_ => Input.GetKeyDown(KeyCode.Space)) // 检测玩家按下空格键  
    .Buffer(System.TimeSpan.FromSeconds(1)) // 收集 1 秒内的所有按键  
    .Subscribe(clicks =>  
    {  
        if (clicks.Count >= 3) // 如果 1 秒内按下 3 次或更多  
        {  
            Debug.Log("连击被触发!");  
        }  
        else  
        {  
            Debug.Log($"普通攻击 ({clicks.Count} clicks)");  
        }  
    });

输出:

如果玩家在 1 秒内按下空格键 3 次或更多:

连击被触发

如果按键次数不足 3 次:

普通攻击 (2 clicks) 

2. FlatMap

FlatMap, (在 UniRx 中对应 , code, SelectMany, )会将Observable, 中的每个值映射为一个新的Observable, 的值合并为一个流。

使用场景

  • 处理嵌套的异步操作,例如从多个数据源加载内容

示例:生成敌人波次,在游戏中,每次生成一波敌人,每波敌人包含多个单位

Observable.Range(1, 3) // 模拟 3 波敌人  
    .SelectMany(wave =>  
    {  
        Debug.Log($"第 {wave} 波敌人开始!");  
        return Observable.Range(1, 5) // 每波生成 5 个敌人  
            .Delay(System.TimeSpan.FromSeconds(0.5f)); // 每个敌人间隔 0.5 秒生成  
    })  
    .Subscribe(enemy =>  
    {  
        Debug.Log($"敌人 {enemy} 生成!");  
    });

输出:

第1波敌人开始!
敌人 1 生成!  
敌人 2 生成!  
...  
第2波敌人开始!  
敌人 1 生成!  
...

3. GroupBy

GroupBy会将Observable, 中的值按键分组,并为每个组创建一个新的Observable。

使用场景:

  • 对数据进行分组处理,例如按奇偶分组处理数字流。

示例:按敌人类型分组处理,假设游戏中有不同类型的敌人(比如近战和远程),我们可以按类型分组处理。

var enemies = new[] { "近战", "远程", "近战", "远程", "近战" };  

Observable.FromArray(enemies) // 模拟敌人生成事件  
    .GroupBy(type => type) // 按敌人类型分组  
    .Subscribe(group =>  
    {  
        group.Subscribe(enemy =>  
        {  
            Debug.Log($"敌人类型: {group.Key}, 敌人: {enemy}");  
        });  
    });

输出:

敌人类型: 近战, 敌人: 近战  
敌人类型: 近战, 敌人: 近战  
敌人类型: 近战, 敌人: 近战  
敌人类型: 远程, 敌人: 远程  
敌人类型: 远程, 敌人: 远程

4. Map (Select)

Map, (在 UniRx 中对应Select)会将Observable, 中的每个值通过函数映射为另一个值。

使用场景:

  • 数据流的转换,例如将数字转换为字符串。

示例:玩家输入映射为动作,将玩家按键映射为具体的游戏动作。

Observable.EveryUpdate()  
  .Where(_ => Input.anyKeyDown) // 检测玩家按键  
  .Select(_ =>  
  {  
      if (Input.GetKeyDown(KeyCode.W)) return "Move Up";  
      if (Input.GetKeyDown(KeyCode.S)) return "Move Down";  
      if (Input.GetKeyDown(KeyCode.A)) return "Move Left";  
      if (Input.GetKeyDown(KeyCode.D)) return "Move Right";  
      return "Unknown Action";  
  })  
  .Subscribe(action => Debug.Log($"Player Action: {action}"));

输出:

//玩家按下W键:
Player Action: Move Up
//玩家按下D键:
Player Action: Move Right

5. Scan

Scan会对数据流中的值进行累积计算,并发射每次计算的结果。

使用场景:

  • 实现累加器、状态管理等

示例:玩家分数累加

玩家每次击杀敌人都会增加分数。

var enemyKills = Observable.Range(1, 5); // 模拟玩家击杀 5 个敌人  

enemyKills  
  .Scan((totalScore, kill) => totalScore + 10) // 每次击杀增加 10 分  
  .Subscribe(score => Debug.Log($"玩家分数: {score}"));

输出:

玩家分数: 10
玩家分数: 20
玩家分数: 30
玩家分数: 40
玩家分数: 50

总结

通过这些操作符,你可以轻松实现复杂的数据流处理逻辑,同时保持代码的简洁和可读性。

这里是RuntimeCube.com,下一篇,我们来了解一下Filtering Observables(过滤可观察对象)

示例代码

代码由GPT-4o等多种AI参与生成,并通过人工审查确保可执行.

登录后下载