自定义盾牌 26.2
了解如何创建你自己的盾牌并配置其属性。
盾牌可以用来防御来自敌人的攻击。要向游戏中添加新的盾牌,你需要一个 Item、两个物品模型、一个客户端物品、配方、物品标签以及一个专用渲染器。
创建物品
前置知识
有关更多信息,请参阅关于创建物品的文档。
在此示例中,我们将使用在自定义盔甲和自定义工具页面中使用的相同修复物品标签。我们定义标签引用如下:
java
public static final TagKey<Item> REPAIRS_GUIDITE_ARMOR = TagKey.create(BuiltInRegistries.ITEM.key(), ExampleMod.id("repairs_guidite_armor"));1
然后,我们创建一个物品 ID 并注册包含以下组件的物品。
- 旗帜图案:创建一个带有空旗帜图案集合的物品。
- 可修复:创建一个可以使用给定物品标签进行修复的物品。
- 可装备/不可交换:在 GUI 中,按住 Shift 键点击物品会将其装备到副手。在世界中,右键点击它不会装备该物品。
- 格挡攻击:创建一个可以格挡攻击的物品。本例使用了原版盾牌的数值。
- 这是一个_延迟组件_,意味着它在世界加载后才会加载,从而允许其引用数据包对象(如标签)。
- 损坏声音:当物品损坏时,它将播放指定的音效。
java
public static final ResourceKey<Item> GUIDITE_SHIELD = create("guidite_shield");1
java
public static final Item GUIDITE_SHIELD = register(
ModItemIds.GUIDITE_SHIELD,
ShieldItem::new,
new Item.Properties().durability(336)
.component(DataComponents.BANNER_PATTERNS, BannerPatternLayers.EMPTY)
.repairable(GuiditeArmorMaterial.REPAIRS_GUIDITE_ARMOR)
.equippableUnswappable(EquipmentSlot.OFFHAND)
.delayedComponent(DataComponents.BLOCKS_ATTACKS, (context) -> new BlocksAttacks(0.25F, 1.0F, List.of(new BlocksAttacks.DamageReduction(90.0F, Optional.empty(), 0.0F, 1.0F)), new BlocksAttacks.ItemDamageFunction(3.0F, 1.0F, 1.0F), Optional.of(context.getOrThrow(DamageTypeTags.BYPASSES_SHIELD)), Optional.of(SoundEvents.SHIELD_BLOCK), Optional.of(SoundEvents.SHIELD_BREAK)))
.component(DataComponents.BREAK_SOUND, SoundEvents.SHIELD_BREAK)
);1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
如果你想从创造模式物品栏中访问它,请记得将它添加到创造模式标签页中!
java
CreativeModeTabEvents.modifyOutputEvent(CreativeModeTabs.COMBAT)
.register((creativeTab) -> creativeTab.accept(ModItems.GUIDITE_SHIELD));1
2
2
创建特殊渲染器
我们将使用专用渲染器来渲染盾牌,而不是普通的物品模型。
首先,我们将创建一个模型层位置,指向盾牌模型所在的位置:
java
public static final ModelLayerLocation GUIDITE_SHIELD =
new ModelLayerLocation(ExampleMod.id("guidite_shield"), "main");1
2
2
然后,在你的客户端初始化器中注册该图层:
java
SpecialModelRenderers.ID_MAPPER.put(ExampleMod.id("guidite_shield"), GuiditeShieldSpecialRenderer.Unbaked.MAP_CODEC);
ModelLayerRegistry.registerModelLayer(GuiditeShieldLayers.GUIDITE_SHIELD, ShieldModel::createLayer);1
2
2
接着,我们将为该物品创建一个专用渲染器。这个渲染器基于原版的 ShieldSpecialRenderer,并进行了修改,使其能够从客户端物品中接收自定义精灵图。我们将在下一节中为渲染器提供这些精灵图。
该渲染器较为复杂,因此我们将对其进行拆解分析。
构造函数
渲染器的构造函数接受四个参数:
- 一个可以通过
Identifier提供精灵图的SpriteGetter接口。 - 我们将使用的模型,在本例中为
ShieldModel。 - 作为
SpriteId提供的基础白色纹理(在客户端物品中提供)。 - 在没有染色或旗帜图案时使用的纹理,作为
SpriteId提供。
构造函数将所有四个参数存储为字段,以便我们后续使用。
java
public class GuiditeShieldSpecialRenderer implements SpecialModelRenderer<DataComponentMap> {
// The offset applied to the model by default
public static final Transformation DEFAULT_TRANSFORMATION = new Transformation(null, null, new Vector3f(1.0F, -1.0F, -1.0F), null);
// Maps Identifiers to their Sprites
private final SpriteGetter sprites;
// What model should be used.
private final ShieldModel model;
// The base white texture (provided in the client item)
private final SpriteId baseSprite;
// The texture used when no dye or banner patterns are present (based on the path provided in the client item).
private final SpriteId baseSpriteNoPattern;
public GuiditeShieldSpecialRenderer(final SpriteGetter sprites, final ShieldModel model, final SpriteId baseSprite, final SpriteId baseSpriteNoPattern) {
this.sprites = sprites;
this.model = model;
this.baseSprite = baseSprite;
this.baseSpriteNoPattern = baseSpriteNoPattern;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
提取
提取待渲染数据时,我们需要一份不可变的数据副本,其中仅包含渲染物品所需的信息。我们可以在 extractArgument 中通过将其 DataComponentMap 转换为不可变映射,从而从 ItemStack 获取该数据:
java
public @Nullable DataComponentMap extractArgument(final ItemStack stack) {
return stack.immutableComponents();
}1
2
3
2
3
范围
我们还将设置模型的边界范围,定义模型的包围盒,用于模型中的渲染和动画:
java
public void getExtents(final Consumer<Vector3fc> output) {
PoseStack poseStack = new PoseStack();
this.model.root().getExtentsForGui(poseStack, output);
}1
2
3
4
2
3
4
提交
提交过程处理“渲染_什么_”的逻辑。盾牌渲染器的逻辑执行以下操作:
- 获取盾牌的旗帜图案并存储在
patterns中。如果盾牌没有旗帜图案,则将其设置为BannerPatternLayers.EMPTY。 - 获取盾牌的染料颜色并存储在
baseColor中。如果盾牌没有染料颜色,则该变量设置为null。 - 如果盾牌带有旗帜图案或经过染色,则使用
base纹理。如果否,则使用base_nopattern纹理。 - 使用提供的参数和纹理提交要渲染的盾牌模型。
- 如果盾牌带有旗帜图案,也一并提交。
- 如果盾牌附有魔咒,则提交附魔光泽(glint)。
java
public void submit(final @Nullable DataComponentMap components, final PoseStack poseStack, final SubmitNodeCollector submitNodeCollector, final int lightCoords, final int overlayCoords, final boolean hasFoil, final int outlineColor) {
BannerPatternLayers patterns = components != null ? components.getOrDefault(DataComponents.BANNER_PATTERNS, BannerPatternLayers.EMPTY) : BannerPatternLayers.EMPTY;
DyeColor baseColor = components != null ? components.get(DataComponents.BASE_COLOR) : null;
boolean hasPatterns = !patterns.layers().isEmpty() || baseColor != null;
SpriteId sprite = hasPatterns ? this.baseSprite : this.baseSpriteNoPattern;
submitNodeCollector.submitModel(this.model, Unit.INSTANCE, poseStack, lightCoords, overlayCoords, -1, sprite, this.sprites, outlineColor, null);
if (hasPatterns) {
BannerRenderer.submitPatterns(this.sprites, poseStack, submitNodeCollector, lightCoords, overlayCoords, this.model, Unit.INSTANCE, false, Objects.requireNonNullElse(baseColor, DyeColor.WHITE), patterns, null);
}
if (hasFoil) {
submitNodeCollector.submitModel(this.model, Unit.INSTANCE, poseStack, RenderTypes.entityGlint(), lightCoords, overlayCoords, -1, this.sprites.get(sprite), 0, null);
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
未烘焙模型
你还需要一个未烘焙模型,用于引用模型渲染器并向模型提供精灵图。
java
public record Unbaked(Identifier base, Identifier noPattern) implements SpecialModelRenderer.Unbaked<DataComponentMap> {
public static final MapCodec<Unbaked> MAP_CODEC = RecordCodecBuilder.mapCodec((i) -> i.group(
Identifier.CODEC.fieldOf("texture").forGetter(Unbaked::base),
Identifier.CODEC.fieldOf("no_pattern_texture").forGetter(Unbaked::noPattern)
).apply(i, Unbaked::new));
public MapCodec<Unbaked> type() {
return MAP_CODEC;
}
public GuiditeShieldSpecialRenderer bake(final SpecialModelRenderer.BakingContext context) {
return new GuiditeShieldSpecialRenderer(context.sprites(), new ShieldModel(context.entityModelSet()
.bakeLayer(GuiditeShieldLayers.GUIDITE_SHIELD)), Sheets.SHIELD_MAPPER.apply(this.base), Sheets.SHIELD_MAPPER.apply(this.noPattern));
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
创建模型
前置知识
更多信息,请参阅有关生成物品模型的文档。
我们将创建两个物品模型(一个用于普通状态,另一个用于盾牌格挡状态),以及一个带有自定义纹理的盾牌条件客户端物品:
INFO
这些模型是通过数据生成创建的。更多信息,请参阅有关生成物品模型的文档。
java
// Paths to the vanilla shield and modded shield
Identifier vanillaShieldModelLocation = ModelLocationUtils.getModelLocation(Items.SHIELD);
Identifier modelLocation = ModelLocationUtils.getModelLocation(ModItems.GUIDITE_SHIELD);
// Item models
ModelTemplate shieldTemplate = new ModelTemplate(Optional.of(vanillaShieldModelLocation), Optional.empty(), TextureSlot.PARTICLE);
shieldTemplate.create(modelLocation, TextureMapping.singleSlot(TextureSlot.PARTICLE, new Material(ModelLocationUtils.getModelLocation(Blocks.ACACIA_PLANKS))), itemModelGenerator.modelOutput);
ModelTemplate blockingShieldTemplate = new ModelTemplate(Optional.of(vanillaShieldModelLocation.withSuffix("_blocking")), Optional.empty(), TextureSlot.PARTICLE);
blockingShieldTemplate.create(modelLocation.withSuffix("_blocking"), TextureMapping.singleSlot(TextureSlot.PARTICLE, new Material(ModelLocationUtils.getModelLocation(Blocks.ACACIA_PLANKS))), itemModelGenerator.modelOutput);
// Client Item
GuiditeShieldSpecialRenderer.Unbaked specialRenderer = new GuiditeShieldSpecialRenderer.Unbaked(
ExampleMod.id("guidite_shield_base"),
ExampleMod.id("guidite_shield_base_nopattern")
);
itemModelGenerator.itemModelOutput.accept(ModItems.GUIDITE_SHIELD, ItemModelUtils.conditional(GuiditeShieldSpecialRenderer.DEFAULT_TRANSFORMATION, ItemModelUtils.isUsingItem(),
ItemModelUtils.specialModel(modelLocation.withSuffix("_blocking"), specialRenderer),
ItemModelUtils.specialModel(modelLocation, specialRenderer)
));1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
创建饰纹盾牌配方
前置知识
有关更多信息,请参阅生成配方的文档。
在生存模式中有两种获得我们盾牌的方法:合成普通盾牌,或者使用旗帜图案来装饰盾牌。
基础盾牌的合成配方可以由你任意指定。另一方面,装饰盾牌配方可以在配方提供程序中像这样创建:
java
SpecialRecipeBuilder.special(
() -> new ShieldDecorationRecipe(
this.tag(ItemTags.BANNERS),
Ingredient.of(ModItems.GUIDITE_SHIELD),
new ItemStackTemplate(ModItems.GUIDITE_SHIELD))
)
.save(this.output, "shield_decoration");1
2
3
4
5
6
7
2
3
4
5
6
7
定义此配方后,你现在就可以在盾牌上套用旗帜图案了:

为盾牌物品添加标签
前置知识
更多信息,请参阅有关生成物品标签的文档。
你还应该将盾牌放入适当的物品标签中:
ItemTags.DURABILITY_ENCHANTABLE,允许其附魔经验修补和耐久;ConventionalItemTags.SHIELD_TOOLS,Mod 开发者可用其实现盾牌特有的行为(如自定义盾牌附魔)。
在你的物品标签提供程序中,将以下代码添加到 addTags 函数中:
java
builder(ConventionalItemTags.SHIELD_TOOLS)
.add(ModItemIds.GUIDITE_SHIELD);
builder(ItemTags.DURABILITY_ENCHANTABLE)
.add(ModItemIds.GUIDITE_SHIELD);1
2
3
4
2
3
4
这样就差不多了!进入游戏后,你应该可以在创造模式物品栏菜单的“战斗”标签页中看到你的盾牌。



