常用右键检测
右键检测是数据包的经典问题。随着更新,能够实现右键检测的方法也越来越多。
在这里简单介绍一下几个常用的右键检测方法。它们各有优势,在实际开发中可以选择适合的方法使用。
当然我也知道没有人喜欢看大段的代码,因此我制作了一个示例数据包,感兴趣的玩家可以下载并拆包研究,在正文里我只简述原理,尽量少展示大段代码。
基础右键检测
胡萝卜钓竿法
- 适用版本:1.13+
- 特点:最简单,通用,但是会吸引猪
- 适用场景:简单右键点按
原理简述
胡萝卜钓竿(以及诡异菌钓竿)是最通用的右键检测方案。
原理为按下使用后,即便不对着猪使用,used:carrot_on_a_stick
统计项也会累计,对应的计分项会同步累计。
实际负载中,可以通过复合两个判据来判定玩家使用了指定道具:
- 玩家持有特定nbt的物品
- 玩家的计分板有计数
因此我们可以新建一个used:carrot_on_a_stick
准则的计分板,在tick函数中检测该计分板的变化。当检测到计分板变化时,代表玩家持有钓竿按下了右键。此时可以加入判断玩家手中的物品,如果是指定物品,就执行按下物品后的函数操作。
其他讨论
- 当我们长按胡萝卜钓竿的时候,计分板会在4tick后再次触发(实际上是0.2s,更改tick rate后依然是0.2s触发);因此胡萝卜钓竿检测长按是不连续的,在大部分情况下并不很适合。
- 但是胡萝卜钓竿有一个很有趣但不一定实用的特性:在右键时不会被视为“使用”物品。这里的“使用”是指吃食物之类的操作,可能有一些额外的动作和音效,以及在使用物品时不能同时使用左键挖掘方块;
- 因此胡萝卜钓竿可能是唯一一个可以在检测右键同时不阻塞挖掘方块操作的物品,这在一些极端情况下可能有用。
交互实体
- 适用版本:1.19.4+
- 特点:可以同时检测左右键,但是会吞其他操作
- 适用场景:固定位置按钮,完全重写左右键操作
原理简述
交互实体,一看就知道是用来处理交互事件的。交互实体在被玩家左键或右键点击后,会把玩家的UUID和点击时的时间戳存储在attack
(左键)和interaction
(右键)标签下。
原理:使用player_hurt_entity
(左键)和player_interacted_with_entity
(右键)这两个进度触发器来监听玩家与交互实体互动,并触发一系列的行为。在进度内可以检查交互实体是否具有特定的数据来标记不同的交互实体。
如果我们只需要以玩家为执行者触发一系列操作,那么到这里就可以结束了。但是如果需要以交互实体为执行者,我们还需要找到被交互的交互实体,这就需要额外的步骤。
我们将玩家的UUID储存在临时storage中,并以玩家附近交互实体为执行者,检查交互实体对应NBT里的UUID,如果匹配,则以该交互实体为执行者执行对应的指令。
匹配UUID:将存储里的玩家UUID通过
/data
复制到交互实体中,如果修改成功(execute store result
返回1)则不同,若失败(返回0)则表示相同。
当然最后要记得把nbt里的UUID清除掉(设为[0,0,0,0])。
其他讨论
- 展示实体毕竟不像物品,它是独立于玩家的另一个实体。因此更适合作为npc或者按钮,固定在地图的特定位置使用。
- 如果想要像物品一样使用,一个简单的方法就是套在头上,通过tp等方式和玩家位置同步;
- 但是这样会造成一个问题,就是如果玩家还可以进行其他可以左右键的操作,此时会优先触发交互实体的操作,从而其他操作无法触发;
- 因此这种做法适合那种高度自定义化的,把各种操作全部重写的地图,作为左右键事件的触发接口使用。
- 交互实体的右键行为和胡萝卜钓竿一致,4tick触发一次;左键行为和攻击其他生物的交互行为一致,点按一下触发一次,长按无效;
- 因此过去检测左键的方法,如在玩家脸上生成村民什么的,可以被替代了。
consumable组件
- 适用版本:1.21+
- 特点:较为灵活,1tick触发,需要处理返还物品
- 适用场景:各种点按、长按场景
原理简述
1.21新增的consumable
组件,可以用来检测玩家使用物品。具有该组件的物品可以按右键使用,并在指定的时间后被消耗。
消耗物品的行为可用consume_item
进度触发器检测,因此,我们可以将consume_seconds
设为0,并使用use_remainder
组件设置一个特殊的返回物品,这样我们就得到了一个可以被瞬间消耗的物品。然后,我们使用上述进度触发器监听消耗行为,并同时触发右键相关函数和替换函数,检测返还物品并把它换回来。示例数据包里使用了一个简单的方法。
但是需要注意,在进度触发瞬间,返还物品还没有到达玩家背包,直接替换将会失败;需要使用/schedule
指令延时1tick才能成功。
其他讨论
- 在过去没有这个组件的时候,我们会使用末影之眼来做右键检测。
- 原理是在没有地牢的时候,末影之眼无法被扔出,但可以在手中被持续使用。
- 这时
using_item
进度触发器就可以抓到使用行为并触发相关的函数。 - 而现在
consumable
组件可以完全替代这一工作,于是在有地牢的环境下也可以使用这个方法来检测了。
- 实际上,通过设置
consume_seconds
的时长,我们还可以使用这一组件进行长按的检测。我们马上就会讨论到。
进阶交互检测
得益于consumable
组件,我们可以进行各种各样丰富的右键检测。比如长按等。
长按触发
当我们把consume_seconds
设为比较长的一个数值,此时使用using_item
进度触发器就可以持续的监听玩家的长按行为;该进度每一tick都会触发一次,可以用来制作产生粒子特效等行为;
长按蓄力
有时候我们可能希望一个道具需要玩家长按一段时间才能触发,在按满指定时间之前松开就无效果,这时候我们可以使用和上面点按时相似的方法,唯一的区别在于把consume_seconds
设为希望蓄力的时长,这样就可以在按住指定时间后才触发效果。
长按松发
另一个需求就是类似弓箭蓄力,在松开时触发效果;这种效果稍微有点复杂。我们需要设置两个计分板,其中一个保存本tick的长按状态(记为cur),另一个保存上一tick玩家的长按状态(记为lt);
实现上,我们可以在tick函数的末尾将cur的值赋值给lt;
在正常状态下,我们将cur的值设置为0;玩家按下右键的状态下(使用using_item
进度触发器检测),我们将cur的值设为1;
然后在tick中检测:当cur的值为0(当前不按下)而lt的值为1(上一tick按下)时,我们就认为玩家松开了道具,此时触发相应的函数。
当然,以上几种检测手段互相不冲突,我们可以视情况,将它们组合起来使用。比如说这期视频中,松开长按或者按住时长达到5秒均会触发弹射。
示例数据包使用方法
示例数据包下载:sample.zip
每一个命名空间都对应了文章中讲到的一个模块;例如carrot_on_a_stick
对应胡萝卜钓竿的模块;
在命名空间内,使用give函数获取对应的物品,在物品的lore中描述了该物品的行为;对于交互实体,则是summon函数,行为则使用tellraw命令展示在对话框中。
其他部分则是功能实现,可以研究原理,或者直接抄来使用。尽量写了一点注释,可能不够全面,还是建议结合本文研究。