ПЕРЕДУМОВИ
Спершу переконайтеся, що ви виконали налаштування datagen.
Налаштування
По-перше, ми повинні створити наш ModelProvider. Створімо клас extends FabricModelProvider. Реалізуйте обидва абстрактні методи: generateBlockStateModels і generateItemModels. Нарешті, створімо конструктор, що відповідає super.
java
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";
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Зареєструйте цей клас у своїй DataGeneratorEntrypoint в рамках методу onInitializeDataGenerator.
Стани та моделі блоку
java
@Override
public void generateBlockStateModels(BlockStateModelGenerator blockStateModelGenerator) {
}1
2
3
2
3
Для моделей блоків ми зосереджуватимемося насамперед на методі generateBlockStateModels. Зверніть увагу на параметр BlockStateModelGenerator blockStateModelGenerator - цей об'єкт відповідатиме за генерацію всіх файлів JSON. Ось декілька зручних прикладів, які можна використовувати для створення бажаних моделей:
Усі прості куби
java
blockStateModelGenerator.registerSimpleCubeAll(ModBlocks.STEEL_BLOCK);1
Це найпоширеніша функція. Він генерує файл моделі JSON для звичайної моделі ,kjrf cube_all. Одна текстура використовується для всіх шести сторін, у цьому випадку ми використовуємо steel_block.
json
{
"parent": "minecraft:block/cube_all",
"textures": {
"all": "fabric-docs-reference:block/steel_block"
}
}1
2
3
4
5
6
2
3
4
5
6
Він також генерує файл JSON зі станом блоку. Оскільки у нас немає властивостей стану блоку (наприклад, Axis, Facing...), достатньо одного варіанту, який використовується кожного разу, коли блок розміщується.
json
{
"variants": {
"": {
"model": "fabric-docs-reference:block/steel_block"
}
}
}1
2
3
4
5
6
7
2
3
4
5
6
7
<0>Блок сталі</0>
Сінглтони
Метод registerSingleton надає файли моделі JSON на основі TexturedModel, який ви передаєте, і єдиного варіанту стану блоку.
java
blockStateModelGenerator.registerSingleton(ModBlocks.PIPE_BLOCK, TexturedModel.END_FOR_TOP_CUBE_COLUMN);1
Цей метод створить моделі для звичайного куба, який використовує файл текстури pipe_block для сторін і файл текстури pipe_block_top для верхньої та нижньої сторін.
json
{
"parent": "minecraft:block/cube_column",
"textures": {
"end": "fabric-docs-reference:block/pipe_block_top",
"side": "fabric-docs-reference:block/pipe_block"
}
}1
2
3
4
5
6
7
2
3
4
5
6
7
TIP
Якщо ви не можете вибрати, яку TextureModel використовувати, відкрийте клас TexturedModel і подивіться на TextureMaps!
Пул текстур блока
java
blockStateModelGenerator.registerCubeAllModelTexturePool(ModBlocks.RUBY_BLOCK)
.stairs(ModBlocks.RUBY_STAIRS)
.slab(ModBlocks.RUBY_SLAB)
.fence(ModBlocks.RUBY_FENCE);1
2
3
4
2
3
4
Іншим корисним методом є registerCubeAllModelTexturePool: визначте текстури, передавши «base block», а потім додайте «children», які матимуть ті самі текстури. У цьому випадку ми передали RUBY_BLOCK, тому сходи, плита та паркан використовуватимуть текстуру RUBY_BLOCK.
WARNING
Він також створить просту модель куба з усіма JSON для «base block», щоб переконатися, що він має модель блоку.
Зверніть увагу на це, якщо ви змінюєте модель блоку цього конкретного блоку, оскільки це призведе до помилки.
Ви також можете додати BlockFamily, який генеруватиме моделі для всіх своїх «children».
java
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();1
2
3
4
5
6
2
3
4
5
6
java
blockStateModelGenerator.registerCubeAllModelTexturePool(ModBlocks.RUBY_BLOCK).family(ModBlocks.RUBY_FAMILY);1
Двері та люки
java
blockStateModelGenerator.registerDoor(ModBlocks.RUBY_DOOR);
blockStateModelGenerator.registerTrapdoor(ModBlocks.RUBY_TRAPDOOR);
// blockStateModelGenerator.registerOrientableTrapdoor(ModBlocks.RUBY_TRAPDOOR);1
2
3
2
3
Двері та люки трохи відрізняються. Тут ви повинні створити три нові текстури - дві для дверей і одну для люка.
- Двері:
- Він має дві частини - верхню половину і нижню половину. Кожному потрібна власна текстура: у цьому випадку
ruby_door_topдля верхньої половини таruby_door_bottomдля нижньої. - Метод registerDoor() створить моделі для всіх орієнтацій дверей, як відкритих, так і закритих.
- Вам також потрібна текстура предмета! Покладіть її в теку
assets/mod_id/textures/item/.
- Люк:
- Тут вам потрібна лише одна текстура, у цьому випадку під назвою
ruby_trapdoor. Він буде використовуватися для всіх сторін. - Оскільки
TrapdoorBlockмає властивістьFACING, ви можете використовувати закоментований метод для генерації файлів моделі з повернутими текстурами = люк буде "орієнтованим". В іншому випадку він виглядатиме однаково незалежно від того, у якому напрямку він дивиться.
Власні моделі блоку
У цьому розділі ми створимо моделі для вертикальної дубової колоди з текстурами дубової колоди.
Точка 2. - 6. оголошуються у внутрішньому статичному допоміжному класі під назвою CustomBlockStateModelGenerator._
Власний клас блоку
Створіть блок VerticalSlab з властивостями FACING і булевою властивістю SINGLE, як у підручнику Block States. SINGLE вкаже, чи є обидві плити. Тоді вам слід перевизначити getOutlineShape і getCollisionShape, щоб контур промальовувався правильно, а блок мав правильну форму колізії.
java
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);1
2
3
4
2
3
4
java
@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);
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Також замініть метод canReplace(), інакше ви не зможете зробити плиту повним блоком.
java
@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;
}1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
І готово! Тепер ви можете протестувати блок і помістити його в гру.
Батьківська модель блоку
Давайте но створимо батьківську модель блоку. Він визначатиме розмір, положення в руці чи інших слотах, а також координати x і y текстури. Для цього рекомендується використовувати такий редактор, як Blockbench, оскільки створення вручну є справді виснажливим процесом. Це має виглядати приблизно так:
json
{
"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
}
}
}
]
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
Перегляньте як форматуються стани блоків, щоб дізнатися більше. Зверніть увагу на ключові слова #bottom, #top, #side. Вони діють як змінні, які можуть бути встановлені моделями, які мають цю як батьківську:
json
{
"parent": "minecraft:block/cube_bottom_top",
"textures": {
"bottom": "minecraft:block/sandstone_bottom",
"side": "minecraft:block/sandstone",
"top": "minecraft:block/sandstone_top"
}
}1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
Значення bottom замінить заповнювач #bottom і так далі. Помістіть його в теку resources/assets/mod_id/models/block/.
Власна модель
Ще нам знадобиться екземпляр класу Model. Він представлятиме фактичну батьківську модель блоку у нашому моді.
java
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);
}1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
Метод 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, нам потрібно створити нову.
java
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));
}1
2
3
4
5
6
7
2
3
4
5
6
7
Для нижньої та верхньої граней використовуватиметься oak_log_top.png, а з боків — oak_log.png.
WARNING
Усі TextureKeys у TextureMap мають збігатися з усіма TextureKeys у вашій моделі батьківського блоку!
Власний метод BlockStateSupplier
BlockStateSupplier містить усі варіанти стану блоку, їх rotation та інші параметри, як-от uvlock.
java
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)));
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
Спочатку ми створюємо новий VariantsBlockStateSupplier за допомогою VariantsBlockStateSupplier.create(). Потім ми створюємо новий BlockStateVariantMap, який містить параметри для всіх варіантів блоку, в цьому випадку FACING і SINGLE, і передаємо його в VariantsBlockStateSupplier. Укажіть, яка модель і які перетворення (uvlock, rotation) використовуються під час використання .register(). Наприклад:
- На першому рядку блок дивиться на північ і є єдиним => ми використовуємо модель без повороту.
- На четвертому рядку блок дивиться на захід і є одинарним => ми повертаємо модель по осі Y на 270°.
- На шостому рядку блок дивиться на схід, але не одинарний => він виглядає як звичайна дубова колода => нам не потрібно його обертати.
Власний метод datagen
Останній крок – створення фактичного методу, який можна викликати, і який генеруватиме JSON. Але для чого ці параметри?
Generator BlockStateModelGenerator, той самий, який передано вgenerateBlockStateModels.Block vertSlabBlock— це блок, для якого ми будемо генерувати файли JSON.Block fullBlock- це модель, яка використовується, коли властивістьSINGLEмає значення false = блок плити виглядає як повний блок.TextureMap texturesвизначає фактичні текстури, які використовує модель. Див. розділ використання мапи текстур.
java
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);
}1
2
3
4
5
6
7
2
3
4
5
6
7
Спочатку ми отримуємо ідентифікатор моделі однієї плити за допомогою VERTICAL_SLAB.upload(). Потім ми отримуємо ідентифікатор моделі повного блоку за допомогою ModelIds.getBlockModelId() і передаємо ці дві моделі в createVerticalSlabBlockStates. BlockStateSupplier передається в blockStateCollector, так що файли JSON фактично генеруються. Крім того, ми створюємо модель для предмета вертикальної плити за допомогою BlockStateModelGenerator.registerParentedItemModel().
І це все! Тепер все, що залишилося зробити, це викликати наш метод у нашому ModelProvider:
java
CustomBlockStateModelGenerator.registerVerticalSlab(
blockStateModelGenerator,
ModBlocks.VERTICAL_OAK_LOG_SLAB,
Blocks.OAK_LOG,
CustomBlockStateModelGenerator.blockAndTopForEnds(Blocks.OAK_LOG)
);1
2
3
4
5
6
2
3
4
5
6
Джерела та посилання
Ви можете переглянути приклади тестів у Fabric API та в цій документації Reference Mod для отримання додаткової інформації.
Ви також можете знайти більше прикладів використання власних методів даних, переглянувши відкритий вихідний код модів, наприклад Vanilla+ Blocks і Vanilla+ Verticals від Fellteros.







