物品

物品

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

物品到底是什么?

在开始创建物品之前,需要先理解物品究竟是什么,以及它与方块等对象的区别。下面通过一个例子说明:

  • 你在世界中发现一块泥土,想要挖掘它。它是方块,因为它放置在世界中。(准确地说,它是方块状态,详见方块状态。)
    • 不是所有方块破坏后都掉落自身,例如树叶。更多信息请参阅战利品表。
  • 挖掘方块后,它从世界中移除(被空气方块替代),并掉落泥土。掉落的泥土是物品实体。因此,它与猪、僵尸、箭等其他实体一样,可以被水流推动,也可以被火或熔岩烧毁。
  • 拾取泥土物品实体后,它会变成物品栏中的物品堆。简单来说,物品堆是某个物品的实例,并附带堆叠数量等额外信息。
  • 物品堆以相应的物品为基础,而这正是我们要创建的对象。物品保存数据组件,包含所有物品堆初始化时的默认信息,例如每把铁剑的最大耐久均为 250。物品堆可以修改这些组件,因此同一种物品的两个堆可以有不同信息,例如一把铁剑剩余 100 次使用,另一把剩余 200 次。下文会进一步区分哪些操作通过物品完成,哪些通过物品堆完成。
    • 物品与物品堆的关系,大致类似于方块与方块状态:方块状态总是以方块为基础。这并非严格类比,例如物品堆不是单例,但有助于理解基本概念。

创建物品

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

与基础方块类似,对于木棍、糖等不需要特殊功能的物品,可以直接使用 Item 类。在注册时,使用 Item.Properties 参数实例化 Item。原文说明可通过 Item.Properties#of 创建属性,再调用以下方法自定义:

  • stacksTo:通过 DataComponents#MAX_STACK_SIZE 设置最大堆叠数量,默认 64。例如末影珍珠等最多堆叠 16 个的物品会使用它。
  • durability:通过 DataComponents#MAX_DAMAGE 设置耐久度,并通过 DataComponents#DAMAGE 将初始损伤设为 0。默认耐久为 0,表示没有耐久。铁工具的值为 250。设置耐久会自动将最大堆叠数量锁定为 1。
  • craftRemainder:设置合成后留下的物品。原版用它让装有液体的桶在合成后留下空桶。
  • fireResistant:通过 DataComponents#FIRE_RESISTANT,让使用此物品的物品实体免受火和熔岩影响,适用于各种下界合金物品。
  • setNoRepair:禁止在铁砧和合成网格中修复此物品。原版未使用。
  • rarity:通过 DataComponents#RARITY 设置稀有度。目前它只改变物品名称颜色。Rarity 枚举包含 COMMON(白色,默认)、UNCOMMON(黄色)、RARE(青色)和 EPIC(淡紫色)。Mod 可以添加更多稀有度类型。
  • requiredFeatures:设置所需功能标志,主要用于原版小版本中的功能锁定系统。除非要接入原版由功能标志控制的系统,否则不建议使用。
  • food:通过 DataComponents#FOOD 设置 FoodProperties。

示例及 Minecraft 使用的各种属性值,可以查看 Items 类。

食物

Item 已提供食物的默认功能,因此不需要另建类。只需通过 Item.Properties 的 food 方法设置 FoodProperties,就可以让物品食用。

使用 FoodProperties.Builder 创建食物属性,并设置以下选项:

  • nutrition:恢复的饥饿值,按半个饥饿图标计数。例如牛排恢复 8 点饥饿值。
  • saturationMod:计算食用后恢复的饱和度所用的修正值。公式为 min(2 * nutrition * saturationMod, playerNutrition),因此设为 0.5 时,有效饱和度等于营养值。
  • alwaysEdible:是否在饥饿条已满时仍可食用。默认 false;金苹果等除了恢复饥饿还提供额外增益的物品设为 true。
  • fast:是否启用快速食用。默认 false,原版干海带设为 true。
  • effect:添加食用时施加的 MobEffectInstance。第二个参数表示施加概率,例如腐肉有 80%(0.8)概率施加饥饿效果。该方法有两个版本,应使用接收 supplier 的版本;另一个直接接收效果实例,因类加载问题被 NeoForge 弃用。
  • usingConvertsTo:设置使用后转变成的物品。
  • build:完成所有设置后,调用它取得可继续使用的 FoodProperties。

示例及原版数值可查看 Foods 类。

调用 Item#getFoodProperties(ItemStack, LivingEntity) 获取食物属性。并非所有物品都能食用,因此可能返回 null。要判断是否可食用,应检查该调用结果是否为 null。

更多功能

直接使用 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 Supplier<Item> EXAMPLE_ITEM = ITEMS.registerItem(
    "example_item",
    Item::new, // The factory that the properties will be passed into.
    new Item.Properties() // The properties to use.
);

它在内部把属性参数传给物品工厂(通常为构造函数),最终相当于调用 ITEMS.register("example_item", () -> new Item(new Item.Properties()))。

若使用 Item::new,可以省略工厂,改用 simple 版本:

public static final Supplier<Item> EXAMPLE_ITEM = ITEMS.registerSimpleItem(
    "example_item",
    new Item.Properties() // The properties to use.
);

它与上例完全相同,只是更简短。如果使用 Item 的子类,则需要使用前一种方法。

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

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

另外,还提供创建方块物品的快捷方法:

public static final Supplier<BlockItem> EXAMPLE_BLOCK_ITEM = ITEMS.registerSimpleBlockItem(
    "example_block",
    ExampleBlocksClass.EXAMPLE_BLOCK, new Item.Properties()
);
// Variant that omits the properties parameter:
public static final Supplier<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 Supplier<BlockItem> EXAMPLE_BLOCK_ITEM = ITEMS.registerSimpleBlockItem(
    ExampleBlocksClass.EXAMPLE_BLOCK,
    new Item.Properties()
);
// Variant that omits both the name and the properties:
public static final Supplier<BlockItem> EXAMPLE_BLOCK_ITEM = ITEMS.registerSimpleBlockItem(
    ExampleBlocksClass.EXAMPLE_BLOCK
);

提示 如果将已注册方块放在单独的类中,应先加载方块类,再加载物品类。

资源

注册物品并通过 /give 或创造模式标签页取得后,会发现它缺少正确的模型和纹理,因为这些由 Minecraft 资源系统管理。

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

这些内容都可以参考相似原版方块的文件和数据生成器。

ItemStack

与方块和方块状态类似,多数看似需要 Item 的位置实际使用 ItemStack。它表示物品栏等容器中一个或多个物品组成的堆。同样,应在 Item 中重写方法,在 ItemStack 上调用方法;Item 中许多方法都会收到 ItemStack 实例。

ItemStack 主要由三部分组成:

  • 对应的 Item,通过 ItemStack#getItem 获取。
  • 堆叠数量,通常介于 1 到 64,通过 getCount 获取,通过 setCount 或 shrink 修改。
  • 数据组件映射,存储物品堆专用数据,通过 getComponents 获取。组件值通常通过 has、get、set、update 和 remove 访问或修改。

调用 new ItemStack(Item) 并传入基础物品即可创建物品堆。原文描述,默认数量为 1 且没有 NBT 数据,也有接收数量和 NBT 数据的构造函数重载。

ItemStack 是可变对象,但有时必须将其视为不可变对象。如果需要修改这种物品堆,应使用 #copy 克隆;需要指定数量时,可用 #copyWithCount。

用 ItemStack.EMPTY 表示没有物品的堆,调用 #isEmpty 判断是否为空。

ItemStack 的可变性

调用 #setCount 或任何修改数据组件映射的方法,都会修改 ItemStack 本身。原版广泛使用这种可变性,许多方法依赖它。例如,#split 从当前堆中分出指定数量,既修改调用者,又返回新的 ItemStack。

不过,同时处理多个物品堆时,这可能引发问题。最常见的情况是处理物品栏槽位:既要考虑光标当前选中的物品堆,也要考虑正在插入或提取的物品堆。

建议 不确定时,先使用 #copy 复制物品堆更稳妥。

JSON 表示

在配方等许多场景中,需要用 JSON 对象表示物品堆,格式如下:

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

创造模式标签页

默认情况下,物品只能通过 /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

ItemLike 是原版 Item 和 Block 实现的接口。其 #asItem 方法返回对象的物品表示:Item 返回自身;Block 返回关联的 BlockItem,若不存在则返回 Blocks.AIR。在无需关心物品来源的场景中常使用 ItemLike,例如许多数据生成器。

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


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