前提
首先,请确保你已完成 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
在 onInitializeDataGenerator 方法中的 DataGeneratorEntrypoint 中注册此类。
方块状态和方块模型
java
@Override
public void generateBlockStateModels(BlockStateModelGenerator blockStateModelGenerator) {
}1
2
3
2
3
对于方块模型,我们将主要关注 generateBlockStateModels 方法。 请注意参数 BlockStateModelGenerator blockStateModelGenerator——该对象将负责生成所有 JSON 文件。 以下是一些可用于生成所需模型的便捷示例:
简单 Cube All
java
blockStateModelGenerator.registerSimpleCubeAll(ModBlocks.STEEL_BLOCK);1
这是最常用的函数。 它为普通的 cube_all 方块模型生成一个 JSON 模型文件。 所有六个面都使用一个纹理,在本例中我们使用 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 文件。 由于我们没有方块状态属性(例如轴、朝向等),因此一个变体就够了,并且每次放置方块时都会使用。
json
{
"variants": {
"": {
"model": "fabric-docs-reference:block/steel_block"
}
}
}1
2
3
4
5
6
7
2
3
4
5
6
7
单例
registerSingleton 方法根据你传入的 TexturedModel 和单个方块状态变体提供 JSON 模型文件。
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 类并查看 纹理映射!
方块纹理池
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:通过传入“基础方块”来定义纹理,然后附加具有相同纹理的“子方块”。 在这种情况下,我们传入了 RUBY_BLOCK,因此楼梯、台阶和栅栏将使用 RUBY_BLOCK 纹理。
你还可以附加一个 BlockFamily,它将为其所有“子项”生成模型。
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 的内部静态辅助类中声明。
自定义方块模型
创建一个具有 FACING 属性和 SINGLE 布尔属性的 VerticalSlab 方块,类似于 方块状态 教程中的那样。 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() 方法创建一个新的 Model,指向 resources/assets/mod_id/models/block/ 文件夹内的 vertical_slab.json 文件。 TextureKey 将“占位符”(#bottom、#top...) 表示为一个对象。
使用纹理映射
TextureMap 是干什么的? 它实际上提供了指向纹理的标识符。 从技术上讲,它的行为类似于普通映射——将 TextureKey(键)与 Identifier(值)关联起来。
你可以使用原版的,例如 TextureMap.all()(它将所有 TextureKey 与相同的标识符关联),或者创建一个新实例然后用 .put() 将键与值关联起来。
TIP
TextureMap.all() 将所有的 TextureKey 与相同的标识符关联起来,无论它们有多少!
因为我们想要用橡木原木纹理,但是有 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
bottom(底部)和 top(顶部)面使用 oak_log_top.png,侧面则使用 oak_log.png。
WARNING
TextureMap 中的所有 TextureKey 必须与父方块模型中的所有 TextureKey 匹配!
自定义 BlockStateSupplier 方法
BlockStateSupplier 包含所有方块状态变体、旋转以及其他选项(如 uvlock)。
java
private static BlockModelDefinitionCreator createVerticalSlabBlockStates(Block vertSlabBlock, Identifier vertSlabId, Identifier fullBlockId) {
WeightedVariant vertSlabModel = BlockStateModelGenerator.createWeightedVariant(vertSlabId);
WeightedVariant fullBlockModel = BlockStateModelGenerator.createWeightedVariant(fullBlockId);
return VariantsBlockModelDefinitionCreator.of(vertSlabBlock)
.with(BlockStateVariantMap.models(VerticalSlabBlock.FACING, VerticalSlabBlock.SINGLE)
.register(Direction.NORTH, true, vertSlabModel.apply(BlockStateModelGenerator.UV_LOCK))
.register(Direction.EAST, true, vertSlabModel.apply(BlockStateModelGenerator.UV_LOCK).apply(BlockStateModelGenerator.ROTATE_Y_90))
.register(Direction.SOUTH, true, vertSlabModel.apply(BlockStateModelGenerator.UV_LOCK).apply(BlockStateModelGenerator.ROTATE_Y_180))
.register(Direction.WEST, true, vertSlabModel.apply(BlockStateModelGenerator.UV_LOCK).apply(BlockStateModelGenerator.ROTATE_Y_270))
.register(Direction.NORTH, false, fullBlockModel.apply(BlockStateModelGenerator.UV_LOCK))
.register(Direction.EAST, false, fullBlockModel.apply(BlockStateModelGenerator.UV_LOCK))
.register(Direction.SOUTH, false, fullBlockModel.apply(BlockStateModelGenerator.UV_LOCK))
.register(Direction.WEST, false, fullBlockModel.apply(BlockStateModelGenerator.UV_LOCK))
);
}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
首先,我们使用 VariantsBlockStateSupplier.create() 创建一个新的 VariantsBlockStateSupplier。 然后我们创建一个新的 BlockStateVariantMap,它包含方块的所有变体的参数,在本例中是 FACING 和 SINGLE,并将其传递给 VariantsBlockStateSupplier。 指定使用 .register() 时使用哪个模型和哪些变换(uvlock、rotation)。 例如:
- 在第一行,方块朝北,并且是单个的 => 我们使用没有旋转的模型。
- 在第四行,方块朝西,并且是单个的 => 我们将模型沿 Y 轴旋转 270°。
- 在第六行,方块朝东,但不是单个的 => 看起来像普通的橡木原木 => 我们不必旋转它。
自定义 Datagen 方法
最后一步——创建一个可以调用的实际方法并生成 JSON。 但这些参数是用来做什么的呢?
BlockStateModelGenerator generator,与传递到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() 获取单个台阶模型的 Identifier。 然后我们使用 ModelIds.getBlockModelId() 获取完整方块模型的 Identifier,并将这两个模型传递给 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 中的示例测试和此文档的 参考模组 以获取更多信息。
您还可以通过浏览模组的开源代码找到更多使用自定义数据生成方法的示例,例如 Fellteros 的 Vanilla+ Blocks 和 Vanilla+ Verticals。








