跳到主要内容

自定义物品

想要创建一个自定义物品,需要包含两个部分:

  1. 插件内成功注册到 Nukkit-MOT
  2. 资源包内定义物品贴图发送给客户端

接下来以 糖果剑 为例,演示自定义物品的步骤。

在插件中注册物品

我们按照以下序列图进行操作

创建新物品的类

在插件中,新建 CandyCaneSword 类,继承 ItemCustom 并重写相应方法

custom/item/CandyCaneSword.java
package cn.nukkitmot.exampleplugin.custom.item;

import cn.nukkit.item.customitem.CustomItemDefinition;
import cn.nukkit.item.customitem.ItemCustom;
import cn.nukkit.item.customitem.data.ItemCreativeCategory;
import cn.nukkit.item.customitem.data.ItemCreativeGroup;
import cn.nukkit.item.customitem.data.RenderOffsets;

public class CandyCaneSword extends ItemCustom {
private static String spacenameId = "nukkit:candy_cane_sword";
private static String textureName = "candy_cane_sword";
private static String name = null;

public CandyCaneSword() {
super(spacenameId, name, textureName);
}

public int scaleOffset() {
return 32; // 需要是16的倍数,如 32、64、128
}

/**
* 该方法设置自定义物品的定义
*/
@Override
public CustomItemDefinition getDefinition() {
return CustomItemDefinition
.simpleBuilder(this, ItemCreativeCategory.EQUIPMENT)
.creativeGroup(ItemCreativeGroup.SWORD)
.allowOffHand(true)
.handEquipped(true)
.renderOffsets(RenderOffsets.scaleOffset(scaleOffset()))
.build();
}

@Override
public int getMaxDurability() {
return 500;
}

@Override
public int getMaxStackSize() {
return 1;
}

@Override
public int getAttackDamage() {
return 4;
}

@Override
public boolean isSword() {
return true;
}
}

ItemCustom 主要方法

引自 cn.nukkit.item.customitem.ItemCustom

我们需要用 @Override 来重写下面的方法。

  • getMaxStackSize() 方法用于设置自定义物品的最大堆叠数量。

  • scaleOffset() 方法用于设置自定义物品的贴图大小,返回值需要是16的倍数,如 32、64、128。

  • getDefinition() 返回一个 CustomItemDefinition 类概括了该物品的基本属性,如 是否允许副手、在创造栏的分类、附魔效果。

CustomItemDefinition 主要方法

引自 cn.nukkit.item.customitem.CustomItemDefinition

常用构建器

  • customBuilder 自定义物品的定义构造器

  • simpleBuilder 简单物品的构建器 (默认使用这个)

  • toolBuilder 工具物品的构建器

  • armorBuilder 盔甲物品的构建器

  • edibleBuilder 食品的构建器

先使用构建器返回一个 CustomItemDefinition

java
CustomItemDefinition.simpleBuilder(ItemCustom item, ItemCreativeCategory creativeCategory);

常用方法

由于方法直接返回 this 因此我们可以使用扁平化的写法。

  • allowOffHand(boolean allowOffHand) 是否允许副手持有。

  • handEquipped(boolean handEquipped) 控制第三人称手持物品的显示方式。

  • foil(boolean foil) 是否有附魔光辉效果,比如附魔书就有。

  • creativeGroup(ItemCreativeGroup creativeGroup) 控制自定义物品在创造栏的分组,例如所有的附魔书都是 ItemCreativeGroup.ENCHANTED_BOOK 组。

  • canDestroyInCreative(boolean value) 控制拿该物品的玩家是否可以在创造模式挖掘方块。

ItemCreativeGroup 主要方法

引自 cn.nukkit.item.customitem.data.ItemCreativeGroup

注册物品

最后,在插件入口类的 onEnable 方法中注册物品

ExamplePlugin.java
import cn.nukkit.item.Item;
import cn.nukkitmot.exampleplugin.custom.item.CandyCaneSword;

public class ExamplePlugin extends PluginBase {
@Override
public void onEnable() {
Item.registerCustomItem(CandyCaneSword.class);
}
}

制作资源包

教程将指导您如何制作资源包,确保正确指向物品贴图,以在游戏内正确显自定义物品。

如果在游戏内创造物品栏中仍然看到空白格子,请检查资源包配置和物品贴图路径是否正确。

详细步骤包括:

  1. 定义资源包的 UUID 和信息。
  2. 在资源包中定义物品贴图路径。
  3. 打包资源包并放入服务器的 resource_packs 文件夹。

资源包目录构成

资源包目录应该包含以下文件:

📁Resource Pack
📄manifest.json
🖼️pack_icon.png
📁textures
📄item_texture.json
📄texture_list.json
📁items
🖼️candy_cane_sword.png

manifest.json

参考 Bedrock Wiki 对 RP Manifest 的讲解。

RP/manifest.json
{
"format_version": 2,
"header": {
"description": "BY.nukkit-mot",
"name": "§7测试资源包",
"uuid": "00000000-0000-0000-0000-000020160300",
"version": [1, 1, 6],
"min_engine_version": [1, 14, 0]
},
"modules": [
{
"type": "resources",
"uuid": "dde211f9-e1a6-435e-9a84-06fa9242f63e",
"version": [1, 0, 0]
}
]
}

item_texture.json

RP/textures/item_texture.json
{
"resource_pack_name": "nukkit-mot",
"texture_name": "atlas.items",
"texture_data": {
"candy_cane_sword": {
"textures": "textures/items/candy_cane_sword"
}
}
}

打包好资源包放入服务器的 resource_packs 文件夹。

进入服务器,正常情况下您会看见资源包正常显示。

物品材质未能正常显示?

很抱歉听到这个消息,但请保持冷静。并按下列情形进行排查:

  1. 若非插件内置资源包,资源包打包时是以根目录压缩的吗?

也就是点开压缩包就能看到 manifest.json 文件。

  1. 资源包后缀是否从 .zip 改为 .mcpack 后缀?

非 zip 格式的压缩算法客户端不支持。

  1. 更新资源包后,是否有清理客户端缓存?

设置 → 存储 → 资源包 存有资源包缓存时,客户端不会向服务器请求更新的资源包。

深入了解

插件内置资源包

得益于 Nukkit-MOT 的设计,我们可以在插件开发中非常方便的编辑资源包。

只需要在插件的 resources 创建 assets/resource_pack 文件夹,并在其中放置资源包。

示例插件就是这么做的。

示例插件的 resources 目录结构

📁resources
📄plugin.yml
📁language
📁assets
📁resource_pack
📄manifest.json
🖼️pack_icon.png
📁textures
📄item_texture.json
📄texture_list.json
📁items
🖼️candy_cane_sword.png

与 Bedrock Wiki 的对照关系

如果 CustomItemDefinition 中没有您想要的封装方法,该怎么做呢?

我们可以直接参照 Bedrock Wiki 的 ItemComponents 文档!

以允许副手举例。

在 Nukkit-MOT 的 CustomItemDefinition 类中的内容如下

public class CustomItemDefinition {
public static class SimpleBuilder {
/**
* 是否允许副手持有
*/
public SimpleBuilder allowOffHand(boolean allowOffHand) {
this.nbt.getCompound("components")
.getCompound("item_properties")
.putBoolean("allow_off_hand", allowOffHand);
return this;
}
}
}

而 Bedrock Wiki 中是这么介绍 Allow Off Hand 的:

## Allow Off Hand

Determines whether an item can be placed in the off-hand slot of the inventory.
minecraft:item > components
"minecraft:allow_off_hand": {
"value": true
}

SimpleBuilder#allowOffHand 方法中的 this.nbt 在构建器中被创建,可参考 Nukkit-MOT 的 CustomItemDefinition.java#L208

    public static class SimpleBuilder {
/**
* 是否允许副手持有
*/
public SimpleBuilder allowOffHand(boolean allowOffHand) {
this.nbt.getCompound("components")
.getCompound("item_properties")
.putBoolean("allow_off_hand", allowOffHand);
return this;
}
}
备注

并不是所有方法都能通过简单的添加 nbt 就达成其效果的,譬如 minecraft:cooldown 就需要服务器来处理 PlayerStartItemCoolDownPacket 以实现物品的使用冷却行为。

可以在 cn.nukkit.network.protocol.ProtocolInfo 中查看协议支持的所有数据包。

RenderOffsets 渲染偏移

渲染偏移的知识较为庞大、复杂,当前也没有可视化工具所以需要一定的空间想象能力。

引用:

通过观察下面的高亮部分代码可知,需要向 RenderOffsets 类传入 4 个 Offset 对象。

cn.coolloong.amethyst_equipment.Item.AmethystSpear 节选
    @Override
public CustomItemDefinition getDefinition() {
return CustomItemDefinition
.toolBuilder(this, ItemCreativeCategory.EQUIPMENT)
.addRepairItems(List.of(Item.fromString("minecraft:amethyst_shard")), 100)
.addRepairItems(List.of(Item.fromString("yes:amethyst_spear")), 400)
.renderOffsets(new RenderOffsets(
Offset.builder()
.position(0.48f, -0.128f, -0.946f)
.rotation(11.696f, -64.536f, 79.413f)
.scale(0.038f, 0.037f, 0.038f),
Offset.builder()
.position(0.258f, 0.979f, -0.541f)
.rotation(-63.268f, -43.969f, 144.041f)
.scale(0.094f, 0.094f, 0.094f),
Offset.builder()
.position(-1.053f, 0.136f, -0.803f)
.rotation(27.273f, 67.731f, -64.494f)
.scale(0.063f, 0.063f, 0.063f),
Offset.builder()
.position(0.258f, 0.979f, -0.541f)
.rotation(-63.268f, -43.969f, 144.041f)
.scale(0.094f, 0.094f, 0.094f)
)
)
.creativeGroup("itemGroup.name.sword")
.allowOffHand(false)
.handEquipped(true)
.customBuild(nbt -> {
nbt.getCompound("components")
.putCompound("minecraft:cooldown", new CompoundTag()
.putString("category", "amethyst_spear")
.putFloat("duration", 3f))
.getCompound("item_properties").putBoolean("animates_in_toolbar", true)
.getCompound("item_properties").putInt("use_duration", 640);
});
}

在 RenderOffsets 类的定义中

public RenderOffsets(@Nullable Offset mainHandFirstPerson, @Nullable Offset mainHandThirdPerson, @Nullable Offset offHandFirstPerson, @Nullable Offset offHandThirdPerson) {}

可以知道四个参数分别为:

  • mainHandFirstPerson: 主手第一人称
  • mainHandThirdPerson: 主手第三人称
  • offHandFirstPerson: 副手第一人称
  • offHandThirdPerson: 副手第三人称

需要更多的发现...