物品

物品

物品与方块都是 Minecraft 的核心组成部分。方块构成周围的世界,物品则存在于物品栏中。

什么是物品

创建物品之前,需要先理解物品究竟是什么,以及它与方块等对象的区别。以下用泥土说明:

  • 你在世界中遇到一个泥土方块并准备挖掘。它是方块,因为它放置在世界中。更准确地说,它是方块状态,详见方块状态。
    • 并非所有方块被破坏后都会掉落自身,例如树叶。详情见战利品表。
  • 挖掉方块后,它会被移除(替换为空气),并掉落泥土。掉落的泥土是一个物品**实体**,因此与猪、僵尸、箭等其他实体一样,可以被水推动,也会被火和熔岩烧毁。
  • 捡起泥土物品实体后,它会成为物品栏中的物品堆叠。物品堆叠可以简单理解为带有额外信息的物品实例,例如堆叠数量。
  • 物品堆叠以对应的物品为基础,这也是我们要创建的对象。物品持有数据组件,为所有堆叠提供初始化时的默认信息,例如铁剑的最大耐久度为 250。物品堆叠可以修改这些组件,因此同一种物品的不同堆叠可以具有不同信息,例如一把铁剑剩余 100 次使用次数,另一把剩余 200 次。下文会进一步说明物品与物品堆叠各自负责的内容。
    • 物品与物品堆叠的关系,大致类似于方块与方块状态:方块状态总是以某个方块为基础。这个类比并不完全准确,例如物品堆叠不是单例,但有助于理解概念。

创建物品

理解物品后,就可以创建它了。

与基础方块类似,不需要特殊功能的物品,例如木棍、糖等,可以直接使用 Item 类。注册时传入 Item.Properties 参数来实例化 Item。该参数可以用 Item.Properties#of 创建,再调用以下方法进行配置:

  • setId:设置物品的资源键。
    • 每个物品都必须设置,否则会抛出异常。
  • overrideDescription:设置物品的翻译键。创建的 Component 存储于 DataComponents#ITEM_NAME。
  • useBlockDescriptionPrefix:便捷方法,以 block.<modid>.<registry_name> 为翻译键调用 overrideDescription。所有 BlockItem 都应调用它。
  • requiredFeatures:设置物品所需的特性标志。主要用于原版在小版本中锁定特性的系统。除非与原版受特性标志限制的系统集成,否则不建议使用。
  • stacksTo:通过 DataComponents#MAX_STACK_SIZE 设置最大堆叠数量,默认 64。例如末影珍珠等物品只能堆叠到 16。
  • durability:通过 DataComponents#MAX_DAMAGE 设置耐久度,同时通过 DataComponents#DAMAGE 将初始损伤设为 0。默认值为 0,表示没有耐久度,例如铁制工具的值为 250。设置耐久度会自动将最大堆叠数量限制为 1。
  • fireResistant:通过 DataComponents#FIRE_RESISTANT 让该物品对应的物品实体免疫火和熔岩伤害,用于各种下界合金物品。
  • rarity:通过 DataComponents#RARITY 设置稀有度,目前只是改变物品名称颜色。Rarity 枚举有 COMMON(白色,默认)、UNCOMMON(黄色)、RARE(青色)和 EPIC(淡紫色)四个值。Mod 可能添加更多稀有度类型。
  • setNoCombineRepair:禁用砂轮及合成栏修复,原版未使用。
  • jukeboxPlayable:设置物品放入唱片机后播放的数据包 JukeboxSong 的资源键。
  • food:通过 DataComponents#FOOD 设置物品的 FoodProperties。

若要查看示例或原版使用的具体数值,可查阅 Items 类。

剩余物与冷却

物品还可以设置使用时应用的属性,或在一段时间内阻止再次使用:

  • craftRemainder:设置合成剩余物,原版用于装满液体的桶,合成后留下空桶。
  • usingConvertsTo:设置通过 Item#use、IItemExtension#finishUsingItem 或 Item#releaseUsing 完成使用后返回的物品,其 ItemStack 存储于 DataComponents#USE_REMAINDER。
  • useCooldown:通过 DataComponents#USE_COOLDOWN 设置再次使用前需要等待的秒数。

工具与盔甲

一些物品作为工具或盔甲使用。它们通过一系列物品属性构建,只有部分行为交由对应类处理:

  • enchantable:通过 DataComponents#ENCHANTABLE 设置堆叠的最大附魔值,使物品可以附魔。
  • repairable:通过 DataComponents#REPAIRABLE 设置可用于修复耐久度的物品或标签。物品必须具有耐久度组件,且不能含 DataComponents#UNBREAKABLE。
  • equippable:通过 DataComponents#EQUIPPABLE 设置物品可装备的槽位。
  • equippableUnswappable:与 equippable 相同,但禁用通过使用物品按钮(默认右键)快速替换装备。

更多信息见对应页面。

添加更多功能

直接使用 Item 只能创建很基础的物品。若要添加右键交互等功能,需要创建继承 Item 的自定义类。Item 提供了许多可重写的方法,详见 Item 和 IItemExtension。

物品最常见的两类操作是左键和右键。由于它们比较复杂,而且涉及其他系统,因此在独立的交互文档中说明。

DeferredRegister.Items

注册表都使用 DeferredRegister 注册内容,物品也不例外。由于添加物品是绝大多数 Mod 的核心功能,NeoForge 提供了继承 DeferredRegister<Item> 的 DeferredRegister.Items,并加入物品专用的便捷方法:

public static final DeferredRegister.Items ITEMS = DeferredRegister.createItems(ExampleMod.MOD_ID);

public static final DeferredItem<Item> EXAMPLE_ITEM = ITEMS.registerItem(
    "example_item",
    Item::new, // The factory that the properties will be passed into.
    props -> props // A unary operator of the properties to use.
);

它在内部把属性传给指定的物品工厂(通常是构造函数),实际相当于调用 ITEMS.register("example_item", registryName -> new Item(new Item.Properties().setId(ResourceKey.create(Registries.ITEM, registryName)))),并在属性上设置 ID。

若使用 Item::new,可以省略工厂参数,改用 simple 变体:

public static final DeferredItem<Item> EXAMPLE_ITEM = ITEMS.registerSimpleItem(
    "example_item",
    props -> props // A unary operator of the properties to use.
);

这与前一示例完全等价,只是更简短。如果使用的是 Item 的子类,则仍需使用前一个方法。

这两个方法也都有省略 new Item.Properties() 参数的重载:

public static final DeferredItem<Item> EXAMPLE_ITEM = ITEMS.registerItem("example_item", Item::new);

// Variant that also omits the Item::new parameter
public static final DeferredItem<Item> EXAMPLE_ITEM = ITEMS.registerSimpleItem("example_item");

最后还有方块物品的便捷方法。除 setId 外,它们还会调用 useBlockDescriptionPrefix,使翻译键与方块一致:

public static final DeferredItem<BlockItem> EXAMPLE_BLOCK_ITEM = ITEMS.registerSimpleBlockItem(
    "example_block",
    ExampleBlocksClass.EXAMPLE_BLOCK,
    props -> props
);

// Variant that omits the properties parameter:
public static final DeferredItem<BlockItem> EXAMPLE_BLOCK_ITEM = ITEMS.registerSimpleBlockItem(
    "example_block",
    ExampleBlocksClass.EXAMPLE_BLOCK
);

// Variant that omits the name parameter, instead using the block's registry name:
public static final DeferredItem<BlockItem> EXAMPLE_BLOCK_ITEM = ITEMS.registerSimpleBlockItem(
    // Must be an instance of `Holder<Block>`
    // DeferredBlock<T> also works
    ExampleBlocksClass.EXAMPLE_BLOCK,
    props -> props
);

// Variant that omits both the name and the properties:
public static final DeferredItem<BlockItem> EXAMPLE_BLOCK_ITEM = ITEMS.registerSimpleBlockItem(
    // Must be an instance of `Holder<Block>`
    // DeferredBlock<T> also works
    ExampleBlocksClass.EXAMPLE_BLOCK
);

提示 如果已注册的方块位于独立类中,应先加载方块类,再加载物品类。

资源

注册并通过 /give 或创造模式物品栏获得物品后,会发现它缺少正确的模型和纹理,因为模型和纹理由 Minecraft 的资源系统处理。

每个物品都应添加或生成以下 JSON 文件:

编写这些内容时,可以参考类似原版方块的文件和数据生成器。

ItemStack

与方块和方块状态类似,许多看似需要 Item 的地方实际上使用 ItemStack。它表示容器(例如物品栏)中一个或多个物品组成的堆叠。方法通常在 Item 中重写,通过 ItemStack 调用,且许多 Item 方法会收到一个 ItemStack 参数。

一个 ItemStack 主要由三部分组成:

  • 对应的 Item,可通过 ItemStack#getItem 取得,也可通过 getItemHolder 取得 Holder<Item>。
  • 堆叠数量,通常在 1 到 64 之间,可通过 getCount 读取,通过 setCount 或 shrink 修改。
  • 存储堆叠专用数据的数据组件映射,可通过 getComponents 读取,通常用 has、get、set、update 和 remove 访问或修改组件值。

调用 new ItemStack(Item) 并传入对应物品即可创建堆叠,默认数量为 1,且没有 NBT 数据;需要时也可使用接受数量和 NBT 数据的构造函数重载。注意,在组件绑定或世界存在之前,不能创建 ItemStack;此时应使用下文的 ItemStackTemplate。

ItemStack 是可变对象,但有时需要按不可变对象处理。若要修改这种堆叠,可先用 #copy 复制,或用 #copyWithCount 指定新数量并复制。

用 ItemStack.EMPTY 表示没有物品的堆叠,用 #isEmpty 检查堆叠是否为空。

ItemStack 的可变性

ItemStack 是可变对象。调用 #setCount 或数据组件映射的修改方法会直接改变该对象。原版大量利用这一点,某些方法也依赖这种行为。例如,#split 会从当前堆叠分离指定数量,既修改调用方,又返回新的 ItemStack。

同时处理多个 ItemStack 时,这有时会造成问题。常见情况是操作物品栏槽位:既要考虑鼠标光标选中的堆叠,也要考虑正在插入或取出的堆叠。

建议 不能确定时,先调用 #copy 复制堆叠更稳妥。

ItemStackTemplate

ItemStackTemplate 是 ItemStack 的不可变形式,通常用于配方等不可变上下文。模板包含构成堆叠的基本元素:持有的 Item holder、数量,以及以补丁形式存储的数据组件。

通过 new ItemStackTemplate(...) 构造函数传入 Item 和其他所需元素即可创建模板;实际需要堆叠时,再调用 ItemStackTemplate#create 创建 ItemStack。

JSON 表示

在配方等场景中,需要把 ItemStackTemplate 表示为 JSON 对象,格式如下:

{
    // The item ID. Required.
    "id": "minecraft:dirt",
    // The item stack count [1, 99]. Optional, defaults to 1.
    "count": 4,
    // A map of data components. Optional, defaults to an empty map.
    "components": {
        "minecraft:enchantment_glint_override": true
    }
}

ItemInstance

ItemInstance 是 ItemStack 和 ItemStackTemplate 共同实现的父接口。堆叠和模板一般用于各自独立的上下文,但某些操作可以同时接受两者,例如读取物品数量。这时使用 ItemInstance,而不是某一种具体类型。

它提供检查物品的 #is、获取数量的 count,以及通过 DataComponentGetter 读取组件的方法。

创造模式物品栏标签页

默认情况下,物品只能通过 /give 获得,不会出现在创造模式物品栏中。

添加方式取决于目标标签页。

已有标签页

提示 此方法用于向 Minecraft 或其他 Mod 的标签页添加物品。添加到自己的标签页,见下文。

可通过 BuildCreativeModeTabContentsEvent 向已有 CreativeModeTab 添加物品。该事件在 Mod 事件总线上触发,且仅在逻辑客户端触发。调用 event#accept 添加物品。

//MyItemsClass.MY_ITEM is a Supplier<? extends Item>, MyBlocksClass.MY_BLOCK is a Supplier<? extends Block>
@SubscribeEvent // on the mod event bus
public static void buildContents(BuildCreativeModeTabContentsEvent event) {
    // Is this the tab we want to add to?
    if (event.getTabKey() == CreativeModeTabs.INGREDIENTS) {
        event.accept(MyItemsClass.MY_ITEM.get());
        // Accepts an ItemLike. This assumes that MY_BLOCK has a corresponding item.
        event.accept(MyBlocksClass.MY_BLOCK.get());
    }
}

事件还提供额外信息,例如 getFlags 可读取启用的特性标志,hasPermissions 可检查玩家是否有权查看管理员物品标签页。

自定义标签页

CreativeModeTab 属于注册表对象,因此自定义标签页必须注册。通过 CreativeModeTab#builder 获取构建器,设置标题、图标、默认物品等属性。NeoForge 还提供了自定义图片、标签与槽位颜色、排序位置等方法。

//CREATIVE_MODE_TABS is a DeferredRegister<CreativeModeTab>
public static final Supplier<CreativeModeTab> EXAMPLE_TAB = CREATIVE_MODE_TABS.register("example", () -> CreativeModeTab.builder()
    //Set the title of the tab. Don't forget to add a translation!
    .title(Component.translatable("itemGroup." + MOD_ID + ".example"))
    //Set the icon of the tab.
    .icon(() -> new ItemStack(MyItemsClass.EXAMPLE_ITEM.get()))
    //Add your items to the tab.
    .displayItems((params, output) -> {
        output.accept(MyItemsClass.MY_ITEM.get());
        // Accepts an ItemLike. This assumes that MY_BLOCK has a corresponding item.
        output.accept(MyBlocksClass.MY_BLOCK.get());
    })
    .build()
);

ItemLike

原版的 Item 和 Block 都实现了 ItemLike 接口。该接口定义 #asItem 方法,用于返回对象对应的物品表示:Item 返回自身;Block 返回关联的 BlockItem,若不存在则返回 Blocks.AIR。当物品的“来源”并不重要时,通常会使用 ItemLike,例如许多数据生成器。

自定义对象也可以实现 ItemLike,只需重写 #asItem。


来源: NeoForged/Documentation(26.1)。中文翻译依据提交 4a680cdd4d15。