添加基本的物品是编写模组的第一步。你将需要创建 Item
对象,注册,并提供纹理。要向物品添加其他行为,你将需要自定义的 Item
类。在本教程以及以后的所有教程中,均使用 tutorial
作为命名空间。如果你有单独的模组 ID,那就直接使用它。
如果是使用的 1.21.2 之后的版本,请直接跳到在 1.21.2+ 中创建物品。
首先,创建 Item
的实例,存储为静态常量字段。Item
的构造方法接受一个 Item.Settings
(或 FabricItemSettings
,除非是 1.20.5 以上版本)对象,该对象用于设置物品属性,例如耐久和堆叠数量。为了简便,就直接在 ExampleMod
中做了,然后存为静态字段。
public class ExampleMod implements ModInitializer { // 新物品的实例 // 对于 1.20.4 以下版本 public static final Item CUSTOM_ITEM = new Item(new FabricItemSettings()); // 对于 1.20.5 之后,1.21.2 之前的版本 public static final Item CUSTOM_ITEM = new Item(new Item.Settings()); [...] }
我们创建了基本的物品,但是在 Minecraft 中还不存在,因为还没有注册。在 Minecraft 中,几乎所有东西都有注册表,物品也不例外。
注册新的内容使用原版的注册表,基本语法为 Registry.register(注册表类型, Identifier, 内容)
。注册表类型存储为 Registries
或 Registry
类的静态字段中,ID(identifier)则是标注你的内容的。内容是你添加的东西的实例。对于物品而言,语法就是 Registry#register(Registries.ITEM, Identifier, Item)
。可以在初始化过程的任何地方调用它,方法本身会返回物品的实例。
对于 1.21 之后的版本,Identifier
是通过 Identifier.of("namespace", "path")
创建的。对于 1.21 之前的版本,是通过 new Identifier("namespace", "path")
或 new Identifier("namespace:path")
。当命名空间或路径包含非法字符(例如大写字母、中文)时,会失败。
public class ExampleMod implements ModInitializer { // 新物品的实例 public static final Item CUSTOM_ITEM = new Item(new Item.Settings()); @Override public void onInitialize() { // 对于 1.21 之前的版本,请将 ''Identifier.of'' 替换为 ''new Identifier'' }
现在新物品已添加到 Minecraft 中,运行“Minecraft Client”运行配置或者 runClient
Gradle任务以查看它的运行情况,在游戏内执行命令 /give @s tutorial:custom_item
。
在上面的代码中,简直创建了一个物品。但是,如果模组有许多物品,则这样并不方块,因为你每次都需要注册、创建一个 Identifier
。所以我们创建一个专门的类存储物品对象,例如 ModItems
或者 TutorialItems
,然后简单的 register
方法便捷地注册物品。这在实际模组开发中很常见。你也可以看看原版的 Items
类以了解 Minecraft 中如何以类似方式完成的。
在这个例子中,创建一个 TutorialItems
类,并在 ModInitializer
中引用这个类。
public final class TutorialItems { private TutorialItems() {} // 新物品的实例 public static final Item CUSTOM_ITEM = register("custom_item", new Item(new Item.Settings())); // 对于 1.21 之前的版本,请将 ''Identifier.of'' 替换为 ''new Identifier'' } public static void initialize() { } }
记得在模组初始化过程中引用模组的一些方法或字段,从而静态加载 TutorialItems
类。
public class ExampleMod implements ModInitializer { @Override public void onInitialize() { TutorialItems.initialize(); } }
注意:一些有经验的用户也可能会决定使用反射来自动注册一个类的所有静态字段。这也可以,但是请小心使用。
从 1.21.2 开始,物品注册重写了。你需要把 RegistryKey
存储到 Item.Settings
中,这样模型和翻译键才会正确地存储在物品组件中。否则,就会看到下面的异常,Minecraft 也不会运行:
java.lang.NullPointerException: Item id not set
要让它正常运行,就要像下面这样写:
public final class TutorialItems { private TutorialItems() { } public static final Item CUSTOM_ITEM = register("custom_item", Item::new, new Item.Settings()); public static Item register(String path, Function<Item.Settings, Item> factory, Item.Settings settings) { final RegistryKey<Item> registryKey = RegistryKey.of(RegistryKeys.ITEM, Identifier.of("tutorial", path)); return Items.register(registryKey, factory, settings); } public static void initialize() { } }
在方法 Items.register
中,注册表键(registry key)会先写到 settings
中,然后使用那个 settings
去创建物品。
如果第一步成功注册了你的物品,就可以成输入命令 /give @s tutorial:custom_item
成功得到你的物品。你会发现纹理缺乏,Minecraft 会像这样报错:
[Server-Worker-1/WARN]: Unable to load model: 'tutorial:custom_item#inventory' referenced from: tutorial:custom_item#inventory: java.io.FileNotFoundException: tutorial:models/item/custom_item.json
这是因为我们还没有给物品提供纹理和模型。所以,你需要定义物品模组并提供纹理图像。你会需要将这些添加到你的资源的目的下,直接路径如下:
为物品注册纹理需要物品模型.json文件和纹理图像文件。 您将需要将它们添加到资源目录中。每个的直接路径是:
…/resources/assets/tutorial/models/item/custom_item.json
…/resources/assets/tutorial/textures/item/custom_item.png
我们将使用这个示例纹理。
一个非常简单的物品模型长这个样子:
{ "parent": "item/generated", "textures": { "layer0": "tutorial:item/custom_item" } }
你的物品模型的 parent
改变了物品在手中以及在物品栏内等情形下的渲染。item/generated
用于许多简单的物品。item/handheld
用于手持其纹理左下角的物品。在 json 中,textures/layer0
是图像文件的位置。
要为物品添加自定义行为,则需要创建一个物品类。其默认的构造方法需要一个 Item.Settings
对象。
public class CustomItem extends Item { public CustomItem(Settings settings) { super(settings); } }
自定义物品类的一个实际用例是使该物品在右击时播放声音:
public class CustomItem extends Item { public CustomItem(Settings settings) { super(settings); } // 1.21.2 之前的版本,请这么写: @Override public TypedActionResult<ItemStack> use(World world, PlayerEntity user, Hand hand) { user.playSound(SoundEvents.BLOCK_WOOL_BREAK, 1.0F, 1.0F); return TypedActionResult.success(playerEntity.getStackInHand(hand)); } // 1.21.2 以及之后的版本,请这么写: @Override public ActionResult use(World world, PlayerEntity user, Hand hand) { user.playSound(SoundEvents.BLOCK_WOOL_BREAK, 1.0F, 1.0F); return ActionResult.SUCCESS; } }
用新物品类的实例替换旧的 Item 对象:
如果你正确执行了所有操作,则使用该物品现在应该会播放声音。
public class ExampleMod implements ModInitializer { // 新物品的实例 // 对于 1.21.2 之前的版本: public static final CustomItem CUSTOM_ITEM = register("custom_item", new CustomItem(new Item.Settings())); // 对于 1.21.2 之后的版本: public static final CustomItem CUSTOM_ITEM = register("custom_item", CustomItem::new, new Item.Settings()); [...] }
有时你会想给物品添加一些默认的物品组件,例如最大堆叠数量或防火。可以通过调用 Item.Settings
的 component
方法来完成。关于物品组件的详细教程,可见 Fabric Docs 上的教程。
这个例子中,物品默认不可破坏,并隐藏关于这一点的物品提示:
// For versions below 1.21.2: public static final CustomItem CUSTOM_ITEM = register("custom_item", new CustomItem(new Item.Settings() .component(DataComponentTypes.UNBREAKABLE, new UnbreakableComponent(true)))); // For versions since 1.21.2: public static final Item CUSTOM_ITEM = register("custom_item", CustomItem::new, new Item.Settings() .component(DataComponentTypes.UNBREAKABLE, new UnbreakableComponent(true)));
特别地,最大堆叠数可使用 Item.Settings
内的 maxCount(int size)
方法(在 1.20.5 之前也有效)来指定。请注意,如果你的物品是有耐久的,那么无法设置最大堆叠数,否则游戏将抛出 RuntimeException
。
public class ExampleMod implements ModInitializer { // 我们新物品的实例,最大堆叠数为 16 // For versions below 1.21.2: public static final CustomItem CUSTOM_ITEM = register("custom_item", new CustomItem(new Item.Settings().maxCount(16))); // For versions since 1.21.2: public static final Item CUSTOM_ITEM = register("custom_item", CustomItem::new, new Item.Settings().maxCount(16)); [...] }
如果需要让物品能作为燃料在熔炉中燃烧,可以使用 FuelRegistry
,例如:
public class ExampleMod implements ModInitializer { [...] // 对于 1.21.2 之前的版本 @Override public void onInitialize() { [...] FuelRegistry.INSTANCE.add(CUSTOM_ITEM, 300); } }
然而,在实践中,你可能有许多要注册的物品,注册大量物品可能会耗费精力而且乱,所以可以考虑把代码放到单独的方法中,而不是上面的写法。
在 1.21.2 之前的版本,需要使用 Fabric API 的 FuelRegistry.INSTANCE
。
public final class TutorialItems { [...] // 对于 1.21.2 之前的版本 public static void registerFuels() { FuelRegistry.INSTANCE.add(CUSTOM_ITEM, 300); } }
而自从 1.21.2 之后,使用的则是 Fabric API 的 FuelRegistryEvents
:
public final class TutorialItems { [...] // 对于 1.21.2 之后的版本 public static void registerFuels() { FuelRegistryEvents.BUILD.register((builder, context) -> { // 可以一次在这个 lambda 中添加多个物品。 builder.add(CUSTOM_ITEM, 300); }); } }
然后在你的 ModInitializer
中提及此方法:
public class ExampleMod implements ModInitializer { [...] @Override public void onInitialize() { [...] TutorialItems.registerFuels(); } }
类似地,你也可以使用 CompostingChanceRegistry
来让物品可以在堆肥桶中堆肥。