🇺🇦 Українська (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», щоб переконатися, що він має модель блоку.
Він також створить просту модель куба з усіма 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 для отримання додаткової інформації.
Ви також можете знайти більше прикладів використання власних методів даних, переглянувши відкритий вихідний код модів, наприклад Vanilla+ Blocks і Vanilla+ Verticals від Fellteros.