客户端物品

客户端物品

客户端物品(Client Item)在代码中表示 ItemStack 应如何提交给游戏渲染,指定不同状态下使用哪些模型。文件位于 assets 下的 items 子目录,相对位置由 DataComponents#ITEM_MODEL 指定,默认使用对象注册名。例如 minecraft:apple 默认位于 assets/minecraft/items/apple.json。

客户端物品存储于 ModelManager,可通过 Minecraft.getInstance().modelManager 访问。然后用 Identifier 调用 ModelManager#getItemModel 或 getItemProperties 获取相关信息。

警告 不要将客户端物品与游戏中实际烘焙并渲染的模型混淆。

概览

JSON 分为两部分:model 定义在给定上下文中提交物品堆叠渲染时使用哪些模型 JSON;properties 定义渲染器使用的设置。

JSON

// For some item 'examplemod:example_item'
// JSON at 'assets/examplemod/items/example_item.json'
{
    // Defines the model to submit for rendering
    "model": {
        "type": "minecraft:model",
        // Points to a model JSON relative to the 'models' directory
        // Located at 'assets/examplemod/models/item/example_item.json'
        "model": "examplemod:item/example_item"
    },
    // Defines some settings to use during the rendering process
    "properties": {
        // When false, disables the animation where the item is raised
        // up towards its normal position on item swap
        "hand_animation_on_swap": false,
        // When true, allows the model to render outside its defined
        // slot bounds (defined in GuiItemRenderState#bounds) in a GUI
        // instead of being scissored
        "oversized_in_gui": false,
        // Applies the scalar to the height of the hand when swapping
        "swap_animation_scale": 1.0
    }
}

数据生成

// Assume there is some DeferredItem<Item> EXAMPLE_ITEM
// Within an extended ModelProvider
@Override
protected void registerModels(BlockModelGenerators blockModels, ItemModelGenerators itemModels) {
    itemModels.itemModelOutput.register(
        EXAMPLE_ITEM.get(),
        // Defines the model to submit for rendering
        new CuboidItemModelWrapper.Unbaked(
            // Points to a model JSON relative to the 'models' directory
            // Located at 'assets/examplemod/models/item/example_item.json'
            ModelLocationUtils.getModelLocation(EXAMPLE_ITEM.get()),
            Optional.empty(),
            Collections.emptyList()
        ),
        // Defines some settings to use during the rendering process
        new ClientItem.Properties(
            // When false, disables the animation where the item is raised
            // up towards its normal position on item swap
            false,
            // When true, allows the model to render outside its defined
            // slot bounds (defined in GuiItemRenderState#bounds) in a GUI
            // instead of being scissored
            false,
            // Applies the scalar to the height of the hand when swapping
            1.0F
        )
    );
}

关于物品模型如何提交渲染的更多信息,见下文。

基础模型

model 中的 type 决定如何选择提交渲染的模型。最简单的类型是 minecraft:model(即 CuboidItemModelWrapper),直接指定相对于 models 目录的模型 JSON,例如 assets/<namespace>/models/<path>.json。

JSON

// For some item 'examplemod:example_item'
// JSON at 'assets/examplemod/items/example_item.json'
{
    "model": {
        "type": "minecraft:model",
        // Points to a model JSON relative to the 'models' directory
        // Located at 'assets/examplemod/models/item/example_item.json'
        "model": "examplemod:item/example_item"
    }
}

数据生成

// Assume there is some DeferredItem<Item> EXAMPLE_ITEM
// Within an extended ModelProvider
@Override
protected void registerModels(BlockModelGenerators blockModels, ItemModelGenerators itemModels) {
    itemModels.itemModelOutput.accept(
        EXAMPLE_ITEM.get(),
        new CuboidItemModelWrapper.Unbaked(
            // Points to a model JSON relative to the 'models' directory
            // Located at 'assets/examplemod/models/item/example_item.json'
            ModelLocationUtils.getModelLocation(EXAMPLE_ITEM.get()),
            Optional.empty(),
            Collections.emptyList()
        )
    );
}

局部变换

多数客户端物品模型都可以像模型 JSON 一样指定 Transformation。该变换在模型 JSON 对相应显示上下文的变换之后应用,通过 minecraft:model 类型的 transformation 字段设置。

JSON

// For some item 'examplemod:example_item'
// JSON at 'assets/examplemod/items/example_item.json'
{
    "model": {
        "type": "minecraft:model",
        // Points to 'assets/examplemod/models/item/example_item.json'
        "model": "examplemod:item/example_item",
        // The transformations to apply after the model JSON transforms.
        "transformation": {
            // The translation of the client item, specified as `[x, y, z]`.
            "translation": [
                0.5,
                0.0,
                0.5
            ],
            // The initial rotation of the client item, specified as:
            // - `[x, y, z, w]`
            // - { angle, [x, y, z] rotation axis }
            "left_rotation": [
                1.0,
                0.0,
                0.0,
                0.0
            ],
            // The scale of the client item, specified as `[x, y, z]`.
            "scale": [
                1.0,
                1.0,
                1.0
            ],
            // The rotation of the client item after scaling, specified as:
            // - `[x, y, z, w]`
            // - { angle, [x, y, z] rotation axis }
            "right_rotation": {
                "angle": 0,
                "axis": [
                    0.0,
                    0.0,
                    0.0
                ]
            }
        }
    }
}

数据生成

// Assume there is some DeferredItem<Item> EXAMPLE_ITEM
// Within an extended ModelProvider
@Override
protected void registerModels(BlockModelGenerators blockModels, ItemModelGenerators itemModels) {
    itemModels.itemModelOutput.accept(
        EXAMPLE_ITEM.get(),
        new CuboidItemModelWrapper.Unbaked(
            // Points to 'assets/examplemod/models/item/example_item.json'
            ModelLocationUtils.getModelLocation(EXAMPLE_ITEM.get()),
            // The transformations to apply after the model JSON transforms.
            Optional.of(new Transformation(
                // The translation of the client item.
                new Vector3f(0.5f, 0f, 0.5f),
                // The initial rotation of the client item.
                new Quaternionf(1f, 0f, 0f, 0f),
                // The scale of the client item.
                new Vector3f(1f, 1f, 1f),
                // The rotation of the client item after scaling.
                new Quaternionf(new AxisAngle4f(0f, 0f, 0f, 0f))
            )),
            Collections.emptyList()
        )
    );
}

着色

与多数模型一样,客户端物品可以根据堆叠属性改变指定纹理的颜色。minecraft:model 的 tints 字段定义应用的不透明颜色,称为 ItemTintSource,已有类型定义在 ItemTintSources 中。它们也通过 type 指定具体来源,在列表中的位置就是应用的 tintindex。

JSON

// For some item 'examplemod:example_item'
// JSON at 'assets/examplemod/items/example_item.json'
{
    "model": {
        "type": "minecraft:model",
        // Points to 'assets/examplemod/models/item/example_item.json'
        "model": "examplemod:item/example_item",
        // A list of tints to apply
        "tints": [
            {
                // For when tintindex: 0
                "type": "minecraft:constant",
                // 0x00FF00 (or pure green)
                "value": 65280
            },
            {
                // For when tintindex: 1
                "type": "minecraft:dye",
                // 0x0000FF (or pure blue)
                // Only is called if `DataComponents#DYED_COLOR` is not set
                "default": 255
            }
        ]
    }
}

数据生成

// Assume there is some DeferredItem<Item> EXAMPLE_ITEM
// Within an extended ModelProvider
@Override
protected void registerModels(BlockModelGenerators blockModels, ItemModelGenerators itemModels) {
    itemModels.itemModelOutput.accept(
        EXAMPLE_ITEM.get(),
        new CuboidItemModelWrapper.Unbaked(
            // Points to 'assets/examplemod/models/item/example_item.json'
            ModelLocationUtils.getModelLocation(EXAMPLE_ITEM.get()),
            Optional.empty(),
            // A list of tints to apply
            List.of(
                // For when tintindex: 0
                new Constant(
                    // Pure green
                    0x00FF00
                ),
                // For when tintindex: 1
                new Dye(
                    // Pure blue
                    // Only is called if `DataComponents#DYED_COLOR` is not set
                    0x0000FF
                )
            )
        )
    );
}

创建自定义 ItemTintSource 与其他基于 codec 的注册对象类似:实现该接口,创建负责对象编码与解码的 MapCodec,再通过 Mod 事件总线上的 RegisterColorHandlersEvent.ItemTintSources 注册 codec。接口只有 calculate 方法,接收当前堆叠、堆叠所在世界及持有实体,返回 ARGB 不透明颜色,最高 8 位为 0xFF。

public record DamageBar(int defaultColor) implements ItemTintSource {

    // The map codec to register
    public static final MapCodec<DamageBar> MAP_CODEC = ExtraCodecs.RGB_COLOR_CODEC.fieldOf("default")
        .xmap(DamageBar::new, DamageBar::defaultColor);

    public DamageBar(int defaultColor) {
        // Make sure the passed in color is opaque
        this.defaultColor = ARGB.opaque(defaultColor);
    }

    @Override
    public int calculate(ItemStack stack, @Nullable ClientLevel level, @Nullable LivingEntity entity) {
        return stack.isDamaged() ? ARGB.opaque(stack.getBarColor()) : defaultColor;
    }

    @Override
    public MapCodec<DamageBar> type() {
        return MAP_CODEC;
    }
}

// In some event handler class
@SubscribeEvent // on the mod event bus only on the physical client
public static void registerItemTintSources(RegisterColorHandlersEvent.ItemTintSources event) {
    event.register(
        // The name to reference as the type
        Identifier.fromNamespaceAndPath("examplemod", "damage_bar"),
        // The map codec
        DamageBar.MAP_CODEC
    )
}

JSON

// For some item 'examplemod:example_item'
// JSON at 'assets/examplemod/items/example_item.json'
{
    "model": {
        "type": "minecraft:model",
        // Points to 'assets/examplemod/models/item/example_item.json'
        "model": "examplemod:item/example_item",
        // A list of tints to apply
        "tints": [
            {
                // For when tintindex: 0
                "type": "examplemod:damage_bar",
                // 0x00FF00 (or pure green)
                "default": 65280
            }
        ]
    }
}

数据生成

// Assume there is some DeferredItem<Item> EXAMPLE_ITEM
// Within an extended ModelProvider
@Override
protected void registerModels(BlockModelGenerators blockModels, ItemModelGenerators itemModels) {
    itemModels.itemModelOutput.accept(
        EXAMPLE_ITEM.get(),
        new CuboidItemModelWrapper.Unbaked(
            // Points to 'assets/examplemod/models/item/example_item.json'
            ModelLocationUtils.getModelLocation(EXAMPLE_ITEM.get()),
            Optional.empty(),
            // A list of tints to apply
            List.of(
                // For when tintindex: 0
                new DamageBar(
                    // Pure green
                    0x00FF00
                )
            )
        )
    );
}

组合模型

有时需要为一个物品注册多个模型。虽然可以使用组合模型加载器,但物品有专用 minecraft:composite 类型,接收提交渲染的模型列表。

JSON

// For some item 'examplemod:example_item'
// JSON at 'assets/examplemod/items/example_item.json'
{
    "model": {
        "type": "minecraft:composite",

        // The models to submit for rendering
        // Will be drawn in the order they appear in the list
        "models": [
            {
                "type": "minecraft:model",
                // Points to 'assets/examplemod/models/item/example_item_1.json'
                "model": "examplemod:item/example_item_1"
            },
            {
                "type": "minecraft:model",
                // Points to 'assets/examplemod/models/item/example_item_2.json'
                "model": "examplemod:item/example_item_2"
            }
        ]
    }
}

数据生成

// Assume there is some DeferredItem<Item> EXAMPLE_ITEM
// Within an extended ModelProvider
@Override
protected void registerModels(BlockModelGenerators blockModels, ItemModelGenerators itemModels) {
    itemModels.itemModelOutput.accept(
        EXAMPLE_ITEM.get(),
        new CompositeModel.Unbaked(
            // The models to submit for rendering
            // Will be drawn in the order they appear in the list
            List.of(
                new CuboidItemModelWrapper.Unbaked(
                    // Points to 'assets/examplemod/models/item/example_item_1.json'
                    Identifier.fromNamespaceAndPath("examplemod", "item/example_item_1"),
                    Optional.empty(),
                    Collections.emptyList()
                ),
                new CuboidItemModelWrapper.Unbaked(
                    // Points to 'assets/examplemod/models/item/example_item_2.json'
                    Identifier.fromNamespaceAndPath("examplemod", "item/example_item_2"),
                    Optional.empty(),
                    Collections.emptyList()
                )
            )
        )
    );
}

来源: NeoForged/Documentation(26.1,原文第 4–451 行)。中文翻译依据提交 4a680cdd4d15。