🖌️ 画笔#

此示例演示了如何使用 Bookshelf 库在 Minecraft 中创建画笔。画笔会将方块的材质更改为匹配玩家手中的物品,同时保留朝向、浸水和其他方块状态等重要属性。例如,如果玩家手持橡木木板,白桦木楼梯将转变为橡木楼梯。


🎯 我们要构建什么#

我们的画笔系统将:

  1. 检测玩家何时使用画笔与方块交互

  2. 从玩家手中的物品确定方块类型

  3. 识别目标方块

  4. 将目标方块的材质替换为手持物品的材质


📦 要求#

在开始之前,请确保你有:

  • Minecraft Java版

  • 数据包开发的基础知识,包括函数、标签和记分项

  • Bookshelf bs.blockbs.view 模块,安装方法参见快速开始

    • @require bookshelf.module.block

    • @require bookshelf.module.view


🛠️ 逐步实现#

1. Prepare the Datapack#

首先,我们需要一个函数在数据包加载时初始化我们的系统。创建一个名为 paintbrush:load 的函数并将其注册到 minecraft:load 函数标签中。


➔ 创建 load 函数

@function paintbrush:load

scoreboard objectives add data dummy

此记分项将存储画笔的数据,主要是命令返回的值。

➔ 在 load 标签中注册它

@function_tag minecraft:load

{
  "values": [
    "paintbrush:load"
  ]
}

这确保了在数据包启用或重新加载时 paintbrush:load 会自动运行。


2. Detect Player Interaction#

首先,我们需要创建一个玩家可以用来绘制方块的物品。为此,我们将使用刷子物品。然后,当玩家使用刷子与方块交互时,我们将使用进度来触发我们的系统。


➔ 创建 use 进度

@advancement paintbrush:use

{
  "criteria": {
    "requirement": {
      "trigger": "minecraft:using_item",
      "conditions": {
        "player": [],
        "item": {
          "items": [
            "minecraft:brush"
          ],
          "predicates": {
            "minecraft:custom_data": "{\"paintbrush\":true}"
          }
        }
      }
    }
  },
  "rewards": {
    "function": "paintbrush:use"
  }
}

当使用刷子时,奖励函数使我们能够触发特定的函数。我们将在此处使用 paintbrush:use 函数。

请注意,此进度仅在刷子具有自定义数据 {paintbrush: true} 时才会触发函数,确保原版刷子继续正常运作。

➔ 创建 use 函数

@function paintbrush:use

say Paintbrush used!

➔ 创建 give 函数

@function paintbrush:give

give @s minecraft:brush[ \
  minecraft:custom_data={ paintbrush: true }, \
  minecraft:item_name=["",{text:"MAGIC BRUSH",color:"light_purple",bold:true,italic:true},{text:" - Right click to use",color:"gray"}], \
  minecraft:lore=[{text:"A brushstroke of creation.",color:"dark_gray",italic:false},"",{text:"Imbue the aimed block with the properties",color:"gray",italic:false},{text:"of the block held in your offhand.",color:"gray",italic:false}], \
]

接下来,通过运行 function paintbrush:give 给自己一个画笔来测试。现在……右键点击,你会在聊天栏中看到“Paintbrush used!”。但是,如果你再次尝试,什么也不会发生。这是因为玩家现在已经获得了 painterbrush:use 进度,这会阻止它再次触发。由于进度每个玩家只能触发一次,我们需要在每次使用画笔时重置它。

➔ 更新 use 函数

@function paintbrush:use

advancement revoke @s only paintbrush:use
say Paintbrush used!

再次尝试(记得在测试前手动重置一次进度),你会看到每次使用画笔时消息都会显示。


3. Replace the Targeted Block#

我们已成功设置了检测玩家使用画笔右键点击的功能。然而,现在的问题是我们的函数在玩家位置执行。我们实际想要做的是转换玩家瞄准的方块。虽然 Minecraft 没有提供获取此信息的直接方法,但 Bookshelf 库提供了!


我们可以使用 bs.view 模块,特别是 at_aimed_block 函数,它允许我们直接在玩家瞄准的方块位置执行命令。

➔ 更新 use 函数

@function paintbrush:use

advancement revoke @s only paintbrush:use
function #bs.view:at_aimed_block { run: "function paintbrush:replace_block", with: {} }

➔ 创建 replace_block 函数

@function paintbrush:replace_block

setblock ~ ~ ~ minecraft:bookshelf

现在,每次玩家使用画笔时,paintbrush:use 函数都会在瞄准的方块位置执行 paintbrush:replace_block,将其替换为书架方块。

你可能已经注意到一个空的 with 宏变量。我们不能直接将可选的宏变量传递给函数,但使用 with 允许我们为它们设置默认值。在这种情况下,我们不需要传递任何可选变量,所以将其留空。


4. Paint the Targeted Block#

现在,让我们开始绘制!我们将使用 bs.block 模块将副手中物品的方块应用到目标方块。此过程包括三个步骤:

  1. 将目标方块加载到存储(bs:out block)中作为“虚拟方块”

  2. 使用各种操作转换虚拟方块(本例中为 mix_type

  3. 从虚拟方块生成最终结果,可以是方块、物品、粒子、方块展示实体等(这里我们将它转换后再替换方块)。


设置转换#

mix_type 函数使用映射注册表来确定如何转换方块。Bookshelf 提供了两个内置注册表:

  • bs.colors:该注册表通过颜色属性实现方块转换功能。例如,当手持染色方块时,会尝试将颜色转换应用于目标方块。

  • bs.shapes:此注册表用于形状转换方块。例如,如果手持方块是橡木木板,目标方块是白桦木楼梯,此注册表会将橡木木板映射到相应的方块形状,即橡木楼梯。这非常适合在具有相似结构形状的方块之间进行转换。

可以通过这些注册表更精确地控制方块转换。在此示例中,我们将使用 bs.shapes。要实现转换,我们需要指定 mapping_registry 并将玩家副手方块的 type 存储到某个命令存储中。

➔ 更新 replace_block 函数

@function paintbrush:replace_block

# Prepare the input of the mix_type function
data remove storage painterbrush:input type
data modify storage painterbrush:input mapping_registry set value "bs.shapes"
execute store success score #success data run data modify storage painterbrush:input type set from entity @s equipment.offhand.id
execute if score #success data matches 0 run return fail

在这里,我们使用分数来检查命令是否成功。如果失败,我们立即返回以防止意外行为。


加载目标方块#

#bs.view:at_aimed_block 函数确保执行已经在目标方块位置,因此我们可以直接使用 get_block 加载它。

➔ 更新 replace_block 函数

@function paintbrush:replace_block

# Prepare the input of the mix_type function
data remove storage painterbrush:input type
data modify storage painterbrush:input mapping_registry set value "bs.shapes"
execute store success score #success data run data modify storage painterbrush:input type set from entity @s equipment.offhand.id
execute if score #success data matches 0 run return fail

# Load the aimed block
function #bs.block:get_block

应用转换#

现在方块已加载且转换输入已就绪,我们可以应用 mix_type

➔ 更新 replace_block 函数

@function paintbrush:replace_block

# Prepare the input of the mix_type function
data remove storage painterbrush:input type
data modify storage painterbrush:input mapping_registry set value "bs.shapes"
execute store success score #success data run data modify storage painterbrush:input type set from entity @s equipment.offhand.id
execute if score #success data matches 0 run return fail

# Load the aimed block
function #bs.block:get_block

# Apply the transformation
execute store success score #success data run function #bs.block:mix_type with storage painterbrush:input
execute if score #success data matches 0 run return fail

mix_type 函数直接用转换后的值覆盖 bs:out 存储中的虚拟方块。如果返回0,则表示未找到转换,因此我们返回失败。


放置转换后的方块#

最后,我们使用 set_block 将转换后的方块应用到世界中。

➔ 更新 replace_block 函数

@function paintbrush:replace_block

# Prepare the input of the mix_type function
data remove storage painterbrush:input type
data modify storage painterbrush:input mapping_registry set value "bs.shapes"
execute store success score #success data run data modify storage painterbrush:input type set from entity @s equipment.offhand.id
execute if score #success data matches 0 run return fail

# Load the aimed block
function #bs.block:get_block

# Apply the transformation
execute store success score #success data run function #bs.block:mix_type with storage painterbrush:input
execute if score #success data matches 0 run return fail

# Produce the new block
data modify storage bs:in block.set_block set from storage bs:out block
function #bs.block:set_block

这就完成了转换过程。如果你现在尝试绘制方块,你会看到方块被转换了,完美!


5. Going Further#

如果你在告示牌上尝试此操作,你会发现方块并没有转换为匹配手持方块的材质。这是因为 using_item 进度条件不会在某些方块(如告示牌)上触发。要解决此问题,我们可以使用记分板记分项来补充我们的方法,以跟踪玩家何时使用画笔。我们需要在分数增加时触发 use 函数。为此,我们将添加一个循环运行的新 tick 函数,并在 paintbrush:use 函数中重置分数。


➔ 更新 load 函数

@function paintbrush:load

scoreboard objectives add data dummy
scoreboard objectives add use_brush minecraft.used:minecraft.brush
schedule function paintbrush:tick 1t

➔ 创建 tick 函数

@function paintbrush:tick

execute as @a[scores={use_brush=1..}] run function paintbrush:use
schedule function paintbrush:tick 1t

➔ 更新 use 函数

@function paintbrush:use

scoreboard players reset @s use_brush
advancement revoke @s only paintbrush:use
function #bs.view:at_aimed_block { run: "function paintbrush:replace_block", with: {} }

就是这样!你现在可以使用绘画工具绘制方块了,如你所见,这真的很简单!


✔️ 结语#

恭喜!你已成功使用 Bookshelf 创建了一个功能性画笔!此项目演示了如何:

  • 使用 Bookshelf 的 view 模块来获取瞄准的方块

  • 使用 Bookshelf 的 block 模块来操作方块

画笔不仅是一个有用的工具,也是展示 Bookshelf 如何简化复杂数据包开发的绝佳示例。请随意尝试代码并添加你自己的功能,使其变得更强大!


💬 这对你有帮助吗?

欢迎在下方留下你的问题和反馈!