🇺🇦 Українська (Ukrainian - Ukraine)
🇺🇦 Українська (Ukrainian - Ukraine)
Зовнішній вигляд
🇺🇦 Українська (Ukrainian - Ukraine)
🇺🇦 Українська (Ukrainian - Ukraine)
Зовнішній вигляд
Ця сторінка написана для версії:
1.21.4
Ця сторінка написана для версії:
1.21.4
ПЕРЕДУМОВИ
Спершу переконайтеся, що ви виконали налаштування datagen.
По-перше, ми повинні створити наш ModelProvider. Створімо клас extends FabricModelProvider
. Реалізуйте обидва абстрактні методи: generateBlockStateModels
і generateItemModels
. Нарешті, створімо конструктор, що відповідає super.
public class FabricDocsReferenceModelProvider extends FabricModelProvider {
public FabricDocsReferenceModelProvider(FabricDataOutput output) {
super(output);
}
@Override
public void generateBlockStateModels(BlockStateModelGenerator blockStateModelGenerator) {
}
@Override
public void generateItemModels(ItemModelGenerator itemModelGenerator) {
}
@Override
public String getName() {
return "FabricDocsReference Model Provider";
}
}
Зареєструйте цей клас у своїй DataGeneratorEntrypoint
в рамках методу onInitializeDataGenerator
.
@Override
public void generateBlockStateModels(BlockStateModelGenerator blockStateModelGenerator) {
}
Для моделей блоків ми зосереджуватимемося насамперед на методі generateBlockStateModels
. Зверніть увагу на параметр BlockStateModelGenerator blockStateModelGenerator
- цей об'єкт відповідатиме за генерацію всіх файлів JSON. Ось декілька зручних прикладів, які можна використовувати для створення бажаних моделей:
blockStateModelGenerator.registerSimpleCubeAll(ModBlocks.STEEL_BLOCK);
Це найпоширеніша функція. Він генерує файл моделі JSON для звичайної моделі ,kjrf cube_all
. Одна текстура використовується для всіх шести сторін, у цьому випадку ми використовуємо steel_block
.
{
"parent": "minecraft:block/cube_all",
"textures": {
"all": "fabric-docs-reference:block/steel_block"
}
}
Він також генерує файл JSON зі станом блоку. Оскільки у нас немає властивостей стану блоку (наприклад, Axis, Facing...), достатньо одного варіанту, який використовується кожного разу, коли блок розміщується.
{
"variants": {
"": {
"model": "fabric-docs-reference:block/steel_block"
}
}
}
<0>Блок сталі</0>
Метод registerSingleton
надає файли моделі JSON на основі TexturedModel
, який ви передаєте, і єдиного варіанту стану блоку.
blockStateModelGenerator.registerSingleton(ModBlocks.PIPE_BLOCK, TexturedModel.END_FOR_TOP_CUBE_COLUMN);
Цей метод створить моделі для звичайного куба, який використовує файл текстури pipe_block
для сторін і файл текстури pipe_block_top
для верхньої та нижньої сторін.
{
"parent": "minecraft:block/cube_column",
"textures": {
"end": "fabric-docs-reference:block/pipe_block_top",
"side": "fabric-docs-reference:block/pipe_block"
}
}
TIP
Якщо ви не можете вибрати, яку TextureModel
використовувати, відкрийте клас TexturedModel
і подивіться на TextureMaps
!
blockStateModelGenerator.registerCubeAllModelTexturePool(ModBlocks.RUBY_BLOCK)
.stairs(ModBlocks.RUBY_STAIRS)
.slab(ModBlocks.RUBY_SLAB)
.fence(ModBlocks.RUBY_FENCE);
Іншим корисним методом є registerCubeAllModelTexturePool
: визначте текстури, передавши «base block», а потім додайте «children», які матимуть ті самі текстури. У цьому випадку ми передали RUBY_BLOCK
, тому сходи, плита та паркан використовуватимуть текстуру RUBY_BLOCK
.
WARNING
Він також створить просту модель куба з усіма JSON для «base block», щоб переконатися, що він має модель блоку.
Зверніть увагу на це, якщо ви змінюєте модель блоку цього конкретного блоку, оскільки це призведе до помилки.
Ви також можете додати BlockFamily
, який генеруватиме моделі для всіх своїх «children».
public static final BlockFamily RUBY_FAMILY =
new BlockFamily.Builder(ModBlocks.RUBY_BLOCK)
.stairs(ModBlocks.RUBY_STAIRS)
.slab(ModBlocks.RUBY_SLAB)
.fence(ModBlocks.RUBY_FENCE)
.build();
blockStateModelGenerator.registerCubeAllModelTexturePool(ModBlocks.RUBY_BLOCK).family(ModBlocks.RUBY_FAMILY);
blockStateModelGenerator.registerDoor(ModBlocks.RUBY_DOOR);
blockStateModelGenerator.registerTrapdoor(ModBlocks.RUBY_TRAPDOOR);
// blockStateModelGenerator.registerOrientableTrapdoor(ModBlocks.RUBY_TRAPDOOR);
Двері та люки трохи відрізняються. Тут ви повинні створити три нові текстури - дві для дверей і одну для люка.
ruby_door_top
для верхньої половини та ruby_door_bottom
для нижньої.assets/<mod_id>/textures/item/
.ruby_trapdoor
. Він буде використовуватися для всіх сторін.TrapdoorBlock
має властивість FACING
, ви можете використовувати закоментований метод для генерації файлів моделі з повернутими текстурами = люк буде "орієнтованим". В іншому випадку він виглядатиме однаково незалежно від того, у якому напрямку він дивиться.У цьому розділі ми створимо моделі для вертикальної дубової колоди з текстурами дубової колоди.
Точка 2. - 6. оголошуються у внутрішньому статичному допоміжному класі під назвою CustomBlockStateModelGenerator
._
Створіть блок VerticalSlab
з властивостями FACING
і булевою властивістю SINGLE
, як у підручнику Block States. SINGLE
вкаже, чи є обидві плити. Тоді вам слід перевизначити getOutlineShape
і getCollisionShape
, щоб контур промальовувався правильно, а блок мав правильну форму колізії.
public static final VoxelShape NORTH_SHAPE = Block.createCuboidShape(0.0, 0.0, 0.0, 16.0, 16.0, 8.0);
public static final VoxelShape SOUTH_SHAPE = Block.createCuboidShape(0.0, 0.0, 8.0, 16.0, 16.0, 16.0);
public static final VoxelShape WEST_SHAPE = Block.createCuboidShape(0.0, 0.0, 0.0, 8.0, 16.0, 16.0);
public static final VoxelShape EAST_SHAPE = Block.createCuboidShape(8.0, 0.0, 0.0, 16.0, 16.0, 16.0);
@Override
protected VoxelShape getSidesShape(BlockState state, BlockView world, BlockPos pos) {
boolean type = state.get(SINGLE);
Direction direction = state.get(FACING);
VoxelShape voxelShape;
if (type) {
switch (direction) {
case WEST -> voxelShape = WEST_SHAPE.asCuboid();
case EAST -> voxelShape = EAST_SHAPE.asCuboid();
case SOUTH -> voxelShape = SOUTH_SHAPE.asCuboid();
case NORTH -> voxelShape = NORTH_SHAPE.asCuboid();
default -> throw new MatchException(null, null);
}
return voxelShape;
} else {
return VoxelShapes.fullCube();
}
}
@Override
protected VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return this.getSidesShape(state, world, pos);
}
@Override
protected VoxelShape getCollisionShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return this.getSidesShape(state, world, pos);
}
Також замініть метод canReplace()
, інакше ви не зможете зробити плиту повним блоком.
@Override
protected boolean canReplace(BlockState state, ItemPlacementContext context) {
Direction direction = state.get(FACING);
if (context.getStack().isOf(this.asItem()) && state.get(SINGLE)) {
if (context.canReplaceExisting()) {
return context.getSide().getOpposite() == direction;
}
}
return false;
}
І готово! Тепер ви можете протестувати блок і помістити його в гру.
Давайте но створимо батьківську модель блоку. Він визначатиме розмір, положення в руці чи інших слотах, а також координати x
і y
текстури. Для цього рекомендується використовувати такий редактор, як Blockbench, оскільки створення вручну є справді виснажливим процесом. Це має виглядати приблизно так:
{
"parent": "minecraft:block/block",
"textures": {
"particle": "#side"
},
"display": {
"gui": {
"rotation": [
30,
-135,
0
],
"translation": [
-1.5,
0.75,
0
],
"scale": [
0.625,
0.625,
0.625
]
},
"firstperson_righthand": {
"rotation": [
0,
-45,
0
],
"translation": [
0,
2,
0
],
"scale": [
0.375,
0.375,
0.375
]
},
"firstperson_lefthand": {
"rotation": [
0,
315,
0
],
"translation": [
0,
2,
0
],
"scale": [
0.375,
0.375,
0.375
]
},
"thirdperson_righthand": {
"rotation": [
75,
-45,
0
],
"translation": [
0,
0,
2
],
"scale": [
0.375,
0.375,
0.375
]
},
"thirdperson_lefthand": {
"rotation": [
75,
315,
0
],
"translation": [
0,
0,
2
],
"scale": [
0.375,
0.375,
0.375
]
}
},
"elements": [
{
"from": [
0,
0,
0
],
"to": [
16,
16,
8
],
"faces": {
"down": {
"uv": [
0,
8,
16,
16
],
"texture": "#bottom",
"cullface": "down",
"tintindex": 0
},
"up": {
"uv": [
0,
0,
16,
8
],
"texture": "#top",
"cullface": "up",
"tintindex": 0
},
"north": {
"uv": [
0,
0,
16,
16
],
"texture": "#side",
"cullface": "north",
"tintindex": 0
},
"south": {
"uv": [
0,
0,
16,
16
],
"texture": "#side",
"tintindex": 0
},
"west": {
"uv": [
0,
0,
8,
16
],
"texture": "#side",
"cullface": "west",
"tintindex": 0
},
"east": {
"uv": [
8,
0,
16,
16
],
"texture": "#side",
"cullface": "east",
"tintindex": 0
}
}
}
]
}
Перегляньте як форматуються стани блоків, щоб дізнатися більше. Зверніть увагу на ключові слова #bottom
, #top
, #side
. Вони діють як змінні, які можуть бути встановлені моделями, які мають цю як батьківську:
{
"parent": "minecraft:block/cube_bottom_top",
"textures": {
"bottom": "minecraft:block/sandstone_bottom",
"side": "minecraft:block/sandstone",
"top": "minecraft:block/sandstone_top"
}
}
Значення bottom
замінить заповнювач #bottom
і так далі. Помістіть його в теку resources/assets/mod_id/models/block/
.
Ще нам знадобиться екземпляр класу Model
. Він представлятиме фактичну батьківську модель блоку у нашому моді.
public static final Model VERTICAL_SLAB = block("vertical_slab", TextureKey.BOTTOM, TextureKey.TOP, TextureKey.SIDE);
//helper method for creating Models
private static Model block(String parent, TextureKey... requiredTextureKeys) {
return new Model(Optional.of(Identifier.of(FabricDocsReference.MOD_ID, "block/" + parent)), Optional.empty(), requiredTextureKeys);
}
//helper method for creating Models with variants
private static Model block(String parent, String variant, TextureKey... requiredTextureKeys) {
return new Model(Optional.of(Identifier.of(FabricDocsReference.MOD_ID, "block/" + parent)), Optional.of(variant), requiredTextureKeys);
}
Метод block()
створює нову модель
, вказуючи на файл vertical_slab.json
у теці resources/assets/mod_id/models/block/
. TextureKey
представляють "заповнювачі" (#bottom
, #top
, ...) як об'єкт.
Що робить TextureMap
? Він фактично надає ідентифікатори, які вказують на текстури. Технічно вона поводиться як звичайна мапа – ви пов’язуєте TextureKey
(ключ) з ідентифікатором
(значення).
Ви можете використати стандартні, як-от TextureMap.all()
(який пов’язує всі TextureKeys з тим самим ідентифікатором), або створити новий, створивши новий екземпляр, а потім використавши .put()
для зв’язування ключів зі значеннями.
TIP
TextureMap.all()
пов'язує всі TextureKeys з одним ідентифікатором, незалежно від того, скільки їх є!
Оскільки ми хочемо використовувати текстури дубової колоди, але маємо BOTTOM
, TOP
і SIDE
TextureKey
, нам потрібно створити нову.
public static TextureMap blockAndTopForEnds(Block block) {
return new TextureMap()
.put(TextureKey.TOP, ModelIds.getBlockSubModelId(block, "_top"))
.put(TextureKey.BOTTOM, ModelIds.getBlockSubModelId(block, "_top"))
.put(TextureKey.SIDE, ModelIds.getBlockModelId(block));
}
Для нижньої
та верхньої
граней використовуватиметься oak_log_top.png
, а з боків — oak_log.png
.
WARNING
Усі TextureKey
s у TextureMap мають збігатися з усіма TextureKey
s у вашій моделі батьківського блоку!
BlockStateSupplier
BlockStateSupplier
містить усі варіанти стану блоку, їх rotation та інші параметри, як-от uvlock.
private static BlockStateSupplier createVerticalSlabBlockStates(Block vertSlabBlock, Identifier vertSlabId, Identifier fullBlockId) {
VariantSetting<Boolean> uvlock = VariantSettings.UVLOCK;
VariantSetting<VariantSettings.Rotation> yRot = VariantSettings.Y;
return VariantsBlockStateSupplier.create(vertSlabBlock).coordinate(BlockStateVariantMap.create(VerticalSlabBlock.FACING, VerticalSlabBlock.SINGLE)
.register(Direction.NORTH, true, BlockStateVariant.create().put(VariantSettings.MODEL, vertSlabId).put(uvlock, true))
.register(Direction.EAST, true, BlockStateVariant.create().put(VariantSettings.MODEL, vertSlabId).put(uvlock, true).put(yRot, VariantSettings.Rotation.R90))
.register(Direction.SOUTH, true, BlockStateVariant.create().put(VariantSettings.MODEL, vertSlabId).put(uvlock, true).put(yRot, VariantSettings.Rotation.R180))
.register(Direction.WEST, true, BlockStateVariant.create().put(VariantSettings.MODEL, vertSlabId).put(uvlock, true).put(yRot, VariantSettings.Rotation.R270))
.register(Direction.NORTH, false, BlockStateVariant.create().put(VariantSettings.MODEL, fullBlockId).put(uvlock, true))
.register(Direction.EAST, false, BlockStateVariant.create().put(VariantSettings.MODEL, fullBlockId).put(uvlock, true))
.register(Direction.SOUTH, false, BlockStateVariant.create().put(VariantSettings.MODEL, fullBlockId).put(uvlock, true))
.register(Direction.WEST, false, BlockStateVariant.create().put(VariantSettings.MODEL, fullBlockId).put(uvlock, true)));
}
Спочатку ми створюємо новий VariantsBlockStateSupplier
за допомогою VariantsBlockStateSupplier.create()
. Потім ми створюємо новий BlockStateVariantMap
, який містить параметри для всіх варіантів блоку, в цьому випадку FACING
і SINGLE
, і передаємо його в VariantsBlockStateSupplier
. Укажіть, яка модель і які перетворення (uvlock, rotation) використовуються під час використання .register()
. Наприклад:
Останній крок – створення фактичного методу, який можна викликати, і який генеруватиме JSON. Але для чого ці параметри?
Generator BlockStateModelGenerator
, той самий, який передано в generateBlockStateModels
.Block vertSlabBlock
— це блок, для якого ми будемо генерувати файли JSON.Block fullBlock
- це модель, яка використовується, коли властивість SINGLE
має значення false = блок плити виглядає як повний блок.TextureMap textures
визначає фактичні текстури, які використовує модель. Див. розділ використання мапи текстур.public static void registerVerticalSlab(BlockStateModelGenerator generator, Block vertSlabBlock, Block fullBlock, TextureMap textures) {
Identifier slabModel = VERTICAL_SLAB.upload(vertSlabBlock, textures, generator.modelCollector);
Identifier fullBlockModel = ModelIds.getBlockModelId(fullBlock);
generator.blockStateCollector.accept(createVerticalSlabBlockStates(vertSlabBlock, slabModel, fullBlockModel));
generator.registerParentedItemModel(vertSlabBlock, slabModel);
}
Спочатку ми отримуємо ідентифікатор
моделі однієї плити за допомогою VERTICAL_SLAB.upload()
. Потім ми отримуємо ідентифікатор
моделі повного блоку за допомогою ModelIds.getBlockModelId()
і передаємо ці дві моделі в createVerticalSlabBlockStates
. BlockStateSupplier
передається в blockStateCollector
, так що файли JSON фактично генеруються. Крім того, ми створюємо модель для предмета вертикальної плити за допомогою BlockStateModelGenerator.registerParentedItemModel()
.
І це все! Тепер все, що залишилося зробити, це викликати наш метод у нашому ModelProvider
:
CustomBlockStateModelGenerator.registerVerticalSlab(
blockStateModelGenerator,
ModBlocks.VERTICAL_OAK_LOG_SLAB,
Blocks.OAK_LOG,
CustomBlockStateModelGenerator.blockAndTopForEnds(Blocks.OAK_LOG)
);
Ви можете переглянути приклади тестів у Fabric API та в цій документації [Reference Mod] (https://github.com/FabricMC/fabric-docs/tree/main/reference) для отримання додаткової інформації.
Ви також можете знайти більше прикладів використання власних методів даних, переглянувши відкритий вихідний код модів, наприклад Vanilla+ Blocks і [Vanilla+ Verticals](https://github .com/Fellteros/vanillavsplus) від Fellteros.