Власні щити 26.2
Дізнайтеся, як створити власні щити та налаштовувати їхні властивості.
ПЕРЕДУМОВА
Спершу потрібно зрозуміти, як створити інструмент. У цьому посібнику також йдеться про генерацію даних для рецептів, моделей і теґів предметів.
Щити здатні захистити вас від атак. Щоб додати до гри новий щит, вам знадобляться Item, дві моделі предмета, клієнтський предмет, рецепти, теґи предмета та спеціальний рендерер.
Створення предмета
ПЕРЕДУМОВА
Подробиці див. у документації щодо створення предметів.
Для цього прикладу ми використаємо той самий теґ предмета для лагодження, що й на сторінках власних обладунків та інструментів. Ми визначаємо посилання на теґ наступним чином:
java
public static final TagKey<Item> REPAIRS_GUIDITE_ARMOR = TagKey.create(BuiltInRegistries.ITEM.key(), ExampleMod.id("repairs_guidite_armor"));1
Потім ми створюємо id предмета та реєструємо його із такими компонентами.
- Візерунок стяга: Створює предмет із порожнім набором візерунків для стяга.
- Можна полагодити: Створює предмет, який можна полагодити за допомогою вказаного теґу предмета.
- Можна спорядити / Не можна замінити: В інтерфейсі натискання на предмет із затиснутою клавішею 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, але зі змінами, що дозволяють використовувати власні спрайти з клієнтського предмета. Ми передамо ці спрайти рендереру в наступному розділі.
Рендерер складний, тому ми розберемо його по частинах.
Конструктор
Конструктор рендерера приймає чотири параметри:
SpriteGetterінтерфейс, що надає спрайти зIdentifier.- Модель, яку ми будемо використовувати в нашому випадку це
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
Витягування
Під час витягування даних для рендера нам потрібна незмінна копія даних, що містить лише інформацію, необхідну для рендера предмета. Ми можемо отримати це з ItemStack, перетворивши його DataComponentMap на незмінну версію в методі extractArgument:
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. - Надсилання моделі щита для рендера, використовуючи надані параметри та текстуру.
- Якщо на щиті є візерунки, надсилання їх.
- Якщо щит зачарований, показує ефект блиску.
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— теґ, який розробники модів можуть використовувати для реалізації специфічної для щитів поведінки, наприклад, унікальних зачарувань.
У вашому постачальнику теґів предмета додайте такі рядки до 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
Це майже все! Якщо ви зайдете в гру, то маєте побачити свій щит на вкладці «Бойове приладдя» у меню інвентарю творчості.


