[BE教程]附加包教程:47.自定义组件(二)

2025-11-25 08:43:21

Cat_A***Cat_A***当前离线UID82897性别保密经验 EP铁粒 粒回帖0主题精华在线时间 小时注册时间2021-7-23最后登录1970-1-1查看:1774|回复:3

发表于 2024-8-21 07:00:59 来自手机|只看该作者|正序浏览|阅读模式 IP:山西省 本帖最后由 Cat_Anchor 于 2024-8-22 09:25 编辑 注意:此页面所述功能是方块和物品事件的新版本,也就是假日创作者实验移除后的正式特性。由于作者刚学 JavaScript,内容可能有误,我会标注代码的游戏内效果和漏洞。前言上期,我们学习了如何注册自定义组件。这期,我们将列举所有自定义组件的触发器,并写出一些例子。触发器以下是所有可用的方块和物品的触发器与它们的解释。触发器解释beforeOnPlayerPlace玩家放置方块前onEntityFallOn实体掉落在方块上后 ①②onPlace方块被放置后onPlayerDestroy玩家破坏方块后 ③onPlayerInteract玩家右键点击方块后onRandomTick方块收到随机刻更新后onStepOff实体走出方块后 ②onStepOn实体走上方块后 ②onTick方块收到计划刻更新后 ④onBeforeDurabilityDamage物品受到耐久损耗前onCompleteUse物品使用完成后onConsume物品被食用后onHitEntity物品使实体受伤后onMineBlock物品挖掘方块后onUse物品使用后onUseOn物品在方块上使用后①:此触发器需要 minecraft:entity_fall_on 组件。

②:此触发器需要方块在 Y 轴有至少 4 像素的碰撞箱。

③:玩家必须在生存模式下,而且必须完全破坏方块。

④:此触发器需要 minecraft:tick 组件。每个触发器能访问的数据可能是不一样的,比如方块触发器能访问方块本身和方块所处的维度,涉及到玩家的触发器还能访问玩家,而物品触发器能访问物品本身和使用物品的玩家等。示例这是上期最后的代码。import { world, system, BlockComponentPlayerInteractEvent, BlockComponentTickEvent, EquipmentSlot, ItemStack, Player, EntityComponentTypes } from '@minecraft/server';const blockComponents = [{ id: "supplementary:invert_texture_shadow", trigger: { onPlayerInteract: (e) => { 我是代码 } }},{ id: "supplementary:up_connected_detector", trigger: { onTick: (e) => { 我也是代码 } }}];world.beforeEvents.worldInitialize.subscribe((i) => { for (const c of blockComponents) { i.blockComponentRegistry.registerCustomComponent(c.id, c.trigger); }});复制代码

这期,我们就来填充“我是代码”部分。我们先不看上面代码里的第二个自定义组件,而先看第一个自定义组件,它的作用是检测手中的物品并设置方块状态。具体来说,假如我手中的物品是木棍,那么将布尔方块状态 supplementary:texture_inverted_bit 设为它目前的相反值。所以只要稍微改造一下这个代码,就能变成骨粉催熟植物之类的功能,非常方便。

首先,我们来检测一下触发这个事件的实体是不是玩家,如果不是就直接不执行下面的代码,防止非预期的作用。有时候为了性能考虑,使用某些触发器后可以不检测玩家,也就是不用写这一行。if (!e.player) return;复制代码

然后,我们关注于这个组件要实现的功能:检测物品和设置方块状态。那么首先,我们要获取需要检测的物品和目前的方块状态值,方便以后直接设为相反值。

所以我们定义两个常量,stateValue 和 mainhandItem。const stateValue = e.block.permutation.getState('supplementary:texture_inverted_bit');const mainhandItem = e.player.getComponent('equippable').getEquipment("Mainhand");复制代码

我们首先来分析一下 stateValue。它被赋值为后面那一长串,我们来拆解一下:e 代表事件数据,e.block 就是被玩家点击的方块。那么 e.block.permutation 就是这个方块的附加数据——方块状态。这就是我们想要的!继续向后看,e.block.permutation.getState 中的 getState 就是获取方块状态的方法。它只有一个参数,那就是方块状态的名称,这里是 supplementary:texture_inverted_bit,所以 stateValue 被赋值为目前这个方块状态的值。

然后来看看 mainhandItem。与之前一样,e 代表事件数据,e.player 就是点击方块的玩家。玩家有很多组件,我们需要获取装备栏组件,也就是 getComponent('equippable')。获取了装备栏之后,我们需要获取装备栏中的主手栏,也就是 getEquipment("Mainhand"),它会返回一个物品堆栈。所以这一长串的意思就是将 mainhandItem 赋值为玩家手上的物品堆栈。

接下来,我们可以实现逻辑了。有很多情况导致执行失败,比如方块状态被改为其他类型而不是布尔型,或者方块状态还没有定义,或者手中没有物品等。还有一种情况,那就是手中物品不是木棍。这些情况下,我们需要停止执行接下来的代码,所以写:if (stateValue === undefined || typeof stateValue !== 'boolean' || !mainhandItem || mainhandItem.typeId !== 'minecraft:stick') return;复制代码

多个条件之间是“或”的关系时,我们可以用 || 连接,这个符号表示逻辑上的“或”,只要有一个通过就通过。

如果 stateValue 返回了 undefined(也就是未定义),那么达成条件。这里的 === 是严格相等,只有类型相等才继续判断。

如果 mainhandItem 返回了 undefined(也就是手中没有物品),那么它会被转换为 false。前面加个 ! 会让它变成 true,达成条件。

而 typeof 关键字返回值的类型,这里它应该返回 boolean 字符串。这里的 !== 是严格不相等,只要类型不相等就不相等,如果返回了其他字符串,那么方块状态已经被修改了,最后返回 true,达成条件。

最后一个是主要的逻辑检测。mainhandItem 是一个物品堆栈,它有一个属性叫 typeId,它返回物品的 ID(字符串)。我们可以用 mainhandItem.typeId === 或 !== 'ID' 检测,这里就是检测手中物品是否不是木棍。这个就相当于以前的 q.is_item_name_any('slot.weapon.mainhand',0,'物品 ID') Molang 查询。不过更常见的可能是它的已弃用的前身——q.get_equipped_item_name == '物品 ID' Molang 查询。

总之,这串检测不仅保证了代码不会异常执行,还完成了主要逻辑检测。

接下来,为了保险,我们再写一个条件执行,如下:else if (mainhandItem.typeId === 'minecraft:stick') {}复制代码

现在就可以设置方块状态了。e.block.setPermutation(e.block.permutation.withState('supplementary:texture_inverted_bit', !stateValue));复制代码

又是一大串代码,我们来分解一下:e.block 代表事件涉及到的方块,setPermutation 是它的方法,用于设置方块状态。它只需要一个参数,那就是方块状态。e.block.permutation 里正好有一个 withState 方法,它的作用就是指定方块状态名称和值并返回方块状态。它有两个参数,方块状态的名称和它的值。名称是固定的,supplementary:texture_inverted_bit,而值就是之前获取的 stateValue 的相反值,所以在前面加 !。这一大串下来,就完成了把方块状态设为目前的相反值——也就是切换方块状态的功能。

设置完方块状态就返回,所以代码成了这样:else if (mainhandItem.typeId === 'minecraft:stick') { e.block.setPermutation(e.block.permutation.withState('supplementary:texture_inverted_bit', !stateValue)); return;}复制代码

现在我们实现了所有功能——检测手中物品并设置方块状态。不过它有个漏洞,那就是拿着方块点击时不会放置,我不知道怎么修复。

接下来我们看第二个自定义组件,它的作用是根据上方的方块设置方块状态。首先,它是根据计划刻触发事件的,所以需要一个 minecraft:tick 组件告诉引擎这个方块多久触发一次计划刻,以及是不是循环触发。这个组件的语法基本相当于以前的 minecraft:queued_ticking 组件去掉了 on_tick 字段,如下:"minecraft:tick": { "looping": true, "interval_range": [ 5, 5 ]}复制代码

以上组件的意思是每 5 刻(0.25 秒)触发一次,而且要循环触发。

现在可以专注于脚本了,我们来看看要实现的逻辑:检测上方一格的方块是否有指定标签并设置方块状态。

所以我们先获取上方的方块,定义一个常量 topBlock 并赋值,如下:const topBlock = e.block.above();复制代码

这里的 e.block 是事件涉及到的方块,而后面的 above 方法会获取上方一格的方块。它还有个可选的参数,指定获取上方多少格的方块,不过默认为一,我们就省略了。

还有两种方法获取周围的方块,第一种是 block 类中的 offset 方法,获取相对坐标系中的方块。第二种是 dimension 类中的 getBlock 方法,获取绝对坐标系中的方块。这两个方法都需要定义一个向量数组,比如 const topBlockLocation = [{x:0,y:0,z:0}];,太麻烦了,这里用不到。

接下来,我们就能判断这个方块到底有没有指定标签了。if (topBlock && topBlock.hasTag('valley_tile_bracket')) {}复制代码

block 类中有一个 hasTag 方法,用于判断这个方块有没有特定标签,它的唯一一个参数是标签名,我们正好可以用一下。

上面的第一个 topBlock 判断首先保证了这个方块不是空的,然后判断是否有 valley_tile_bracket 标签,两个条件都满足后才设置方块状态,如下:if (topBlock && topBlock.hasTag('valley_tile_bracket')) { e.block.setPermutation(e.block.permutation.withState('supplementary:connected_bit', true)); return;}复制代码

这里的 e.block.setPermutation 之前讲过了,不再赘述。

接下来,如果上面没有方块,或者没有指定标签,就要把这个方块的方块状态设为 false,如下:else if (!topBlock || !topBlock.hasTag('valley_tile_bracket')) { e.block.setPermutation(e.block.permutation.withState('supplementary:connected_bit', false)); return;}复制代码

第二个自定义组件也填写完成了,我们实现了所有功能。我暂时没有找到这个组件的漏洞,不过没有检测方块状态是一个潜在的漏洞。

以上所有代码综合起来,再放在数组里,是下面这样的:const blockComponents = [{ id: "supplementary:invert_texture_shadow", trigger: { onPlayerInteract: (e) => { if (!e.player) return; const stateValue = e.block.permutation.getState('supplementary:texture_inverted_bit'); const mainhandItem = e.player.getComponent('equippable').getEquipment("Mainhand"); if (stateValue === undefined || typeof stateValue !== 'boolean' || !mainhandItem || mainhandItem.typeId !== 'minecraft:stick') return; else if (mainhandItem.typeId === 'minecraft:stick') { e.block.setPermutation(e.block.permutation.withState('supplementary:texture_inverted_bit', !stateValue)); return; } } }}, { id: "supplementary:up_connected_detector", trigger: { onTick: (e) => { const topBlock = e.block.above(); if (topBlock && topBlock.hasTag('valley_tile_bracket')) { e.block.setPermutation(e.block.permutation.withState('supplementary:connected_bit', true)); return; } else if (!topBlock || !topBlock.hasTag('valley_tile_bracket')) { e.block.setPermutation(e.block.permutation.withState('supplementary:connected_bit', false)); return; } } }}]复制代码总结这一期,我解释了两个自定义组件。我的代码可能有误,或者不完善,或者可以优化,欢迎指出错误并提出建议。第四十六期 第四十七期 第四十八期分享到: QQ好友和群收藏2支持3帖子永久地址: 点击复制苦力怕论坛 - 论坛版权1、本主题所有言论和图片纯属会员个人意见,与本论坛立场无关

2、本站所有主题由该帖子作者发表,该帖子作者享有帖子相关版权

3、其他单位或个人使用、转载或引用本文时必须同时征得该帖子作者的同意

4、帖子作者须承担一切因本文发表而直接或间接导致的民事或刑事法律责任

5、本帖部分内容转载自其它媒体,但并不代表本站赞同其观点和对其真实性负责

6、如本帖侵犯到任何版权问题,请立即告知本站,本站将及时予与删除并致以最深的歉意

7、Minecraft(我的世界)苦力怕论坛管理员和版主有权不事先通知发贴者而删除本文苦力怕论坛,感谢有您~回复使用道具举报

提升卡观察者

Copyright © 2022 角动游戏活动平台 - 全网网游动态实时追踪 All Rights Reserved.