Skip to content

更好的右键检测:减半法

伊桑桑桑桑桑

伊桑桑桑桑桑

让我们来看一个右键检测的性能改进方案:

右键检测(减半法)

在开始之前我们先做个实验:假如一个记分板数值一开始为0,每次都先加上4,再整除以2,看看会发生什么:

  • 初始:0
  • 0+4=44÷2=2
  • 2+4=66÷2=3
  • 3+4=77÷2=3
  • 3+4=77÷2=3
  • ...

你会发现,最后的值似乎一直是3
如果我们不继续加4,会怎么样?

  • 初始:0
  • 0+4=44÷2=2
  • 2+4=66÷2=3
  • 3+4=77÷2=3
  • 3+4=77÷2=3
  • 3÷2=1
  • 1÷2=0
  • 0÷2=0
  • ...

如果我们不继续加4,这个值就会从3变为1,然后回到一开始的0 有没有发现什么蹊跷?
我们直接看结果的数字:

0
2
3
3
3
1
0
0

会发现,在值加4的这段时间里,这个值的样子就会是23331,也就是开始为2,中间为3,结束为1。 也就是说,我们只要在按下右键的时候加上4,之后不断整除以2,就可以得到右键的状态。这就是“减半法”名字的由来。

但是熟悉命令的玩家就会说,右键检测进度这个东西很不稳定,经常会断触。 这样在长按期间就会因为断触导致检测到结束后又立即开始 那么这个方法就不能用了吗? 并不是,我们可以把流程里的加4改成加6

0
3
4
5
5
2
1
0

如果值为3,就说明开始长按右键 如果值为1,就说明松开右键 否则只要不是0,就说明正在长按右键 但是又有人会说,那我按一下立刻松开会怎么样?

0
0
3 🕹️
1
0
0

按的时间再长一点呢?

0
0
3 🕹️
4 🕹️
2
1
0
0

如果断触呢?

0
0
3 🕹️
4 🕹️
5 🕹️
2
5 🕹️
2
1
0

你会发现,只要+4或者+6÷2,无论怎么做,这个值都是以23开始,以1结束的。只有整除以2才能满足这个性质,这也是为什么它叫做“减半法”。
这是为什么呢?在这个过程中,只有32整除以2会得到1,其他的数字都不会。而其他的数字在多次整除以2之后,最终一定会回到2
篇幅限制,本期视频不进行严格的数学证明。如果感兴趣可以自行证明。

接下来贴出完整的代码,不做进一步的解释了

bash
# example:rmb/using_item
# ...右键运行example:rmb/run的进度

# example:load
scoreboard objectives add rmb_flag dummy
scoreboard objectives add example dummy
scoreboard players set 2 example 2

# example:rmb/run
scoreboard players add @s rmb_flag 4
advancement revoke @s only example:rmb/using_item

# example:tick
execute as @a at @s run function example:rmb/player_tick

# example:rmb/player_tick
scoreboard players operation @s rmb_flag /= 2 example
execute if score @s rmb_flag matches 2 run say 开始长按右键
execute if score @s rmb_flag matches 3 run say 正在长按右键
execute if score @s rmb_flag matches 1 run say 松开右键

小结

本期教程我们学习了一种高性能的右键检测方案——减半法。涉及到的知识点有:

  • 减半法:

    • 利用整数除法的特性,通过 (+)÷2 的公式,在一个变量内完成状态流转
    • 如果需要断触保护,只需要修改数字,不需要添加新的逻辑
  • 状态映射表

    方案含义
    +4减半2开始按下右键
    3正在长按右键
    1松开右键
    0无操作
    +6减半3开始按下右键
    1松开右键
    0无操作
    其它值正在长按右键
  • 计分板运算

    • 常量设置:计分板运算不支持直接除以数字,必须先设置一个“常量分数”(如设置example分数为2)作为除数。
    • 运算指令:
      • 输入信号:scoreboard players add @s rmb_flag 4(在进度触发的函数中执行)
      • 状态衰减:scoreboard players operation @s rmb_flag /= 2 example(在Tick函数中执行)

Powered by Vitepress and Github Pages