物品
与方块一样,物品也是 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。