本篇教程的视频:

本篇教程源代码

GitHub地址:Tutorial Mod - Block

本篇教程目标

  • 理解原版方块注册
  • 学会方块注册

查看源代码

注册语句

和前面的物品以及物品栏一样,我们也需要查看源代码来了解方块的注册方法。

首先,我们来查看Blocks这个类(注意是Minecraft包中的类),找一个方块,比如STONE

1
2
3
public static final Block STONE = register(
"stone", BlockBehaviour.Properties.of().mapColor(MapColor.STONE).instrument(NoteBlockInstrument.BASEDRUM).requiresCorrectToolForDrops().strength(1.5F, 6.0F)
);

这里我们先来看看BlockBehaviour.Properties这些东西,它和Item.Properties类似,是定义我们方块的属性的

  • of:创建属性的起始方法;
  • mapColor:定义方块在地图上的颜色;
  • instrument:当音符盒放在方块上时,能发出的声音;
  • requiresCorrectToolForDrops:方块是否需要正确的工具才能破坏,这个我们会在后面在战利品教程中讲;
  • strength:方块的破坏时间和抗爆强度

当然这也只是一部分,未来随着教程的进行我们也会看到一些其他的属性

注册方法

接下来,我们来看register方法。

1
2
3
4
5
6
7
8
9
10
11
12
private static Block register(final String id, final BlockBehaviour.Properties properties) {
return register(id, Block::new, properties);
}

private static Block register(final String id, final Function<BlockBehaviour.Properties, Block> factory, final BlockBehaviour.Properties properties) {
return register(vanillaBlockId(id), factory, properties);
}

public static Block register(final ResourceKey<Block> id, final Function<BlockBehaviour.Properties, Block> factory, final BlockBehaviour.Properties properties) {
Block block = (Block)factory.apply(properties.setId(id));
return Registry.register(BuiltInRegistries.BLOCK, id, block);
}

register方法现在也有了很多的重载方法,和物品一样,用了Function来创建方块,添加方块的ID(不然就会出现Block id not set的错误)

vanillaBlockId方法与前面物品相似

1
2
3
private static ResourceKey<Block> vanillaBlockId(final String name) {
return ResourceKey.create(Registries.BLOCK, Identifier.withDefaultNamespace(name));
}

只不过这里用的是Registries.BLOCK,也就是方块的注册表

方块物品注册

方块这个东西它拥有两种状态,一个是你将方块放到这个世界中,那么它是方块;而当它在物品栏中时,它是物品(方块物品)

所以说方块两种状态都需要进行注册,现在我们来看Items中剩余的注册方法,还是以STONE为例。

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
public static final Item STONE = registerBlock(Blocks.STONE);

private static Item registerBlock(final Block block) {
return registerBlock(block, BlockItem::new);
}

private static Item registerBlock(final Block block, final BiFunction<Block, Item.Properties, Item> itemFactory) {
return registerBlock(block, itemFactory, new Item.Properties());
}

private static Item registerBlock(final Block block, final BiFunction<Block, Item.Properties, Item> itemFactory, final Item.Properties properties) {
return registerItem(
blockIdToItemId(block.builtInRegistryHolder().key()),
p -> (Item)itemFactory.apply(block, p),
properties.useBlockDescriptionPrefix().requiredFeatures(block.requiredFeatures())
);
}

private static Item registerItem(final ResourceKey<Item> key, final Function<Item.Properties, Item> itemFactory, final Item.Properties properties) {
Item item = (Item)itemFactory.apply(properties.setId(key));
if (item instanceof BlockItem blockItem) {
blockItem.registerBlocks(Item.BY_BLOCK, item);
}

return Registry.register(BuiltInRegistries.ITEM, key, item);
}

虽然它是层层叠叠,但最终还是回到了最后一个registerItem方法

而与一般物品不同的,方块物品是实例化BlockItem,而不是Item

另外在属性中,额外添加了useBlockDescriptionPrefix,这个方法就是让我们物品的名字直接沿用方块的名字,这样语言文件就不用写两遍了

其他的倒是都差不多

注册方块

创建ModBlocks类

首先我们创建一个ModBlocks

1
2
3
public class ModBlocks {

}

方块注册方法

按照原版的注册方法,改一下其中的命名空间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static Block register(final String name, final Function<BlockBehaviour.Properties, Block> factory, final BlockBehaviour.Properties properties, boolean shouldRegisterItem) {
ResourceKey<Block> id = ResourceKey.create(Registries.BLOCK, Identifier.fromNamespaceAndPath(TutorialMod.MOD_ID, name));
Block block = (Block)factory.apply(properties.setId(id));

if (shouldRegisterItem) {
registerBlockItem(name, block);
}

return Registry.register(BuiltInRegistries.BLOCK, id, block);
}

public static Block register(final String name, final BlockBehaviour.Properties properties, boolean shouldRegisterItem) {
return register(name, Block::new, properties, shouldRegisterItem);
}

private static void registerBlockItem(String name, Block block) {
ResourceKey<Item> id = ResourceKey.create(Registries.ITEM, Identifier.fromNamespaceAndPath(TutorialMod.MOD_ID, name));
BlockItem blockItem = new BlockItem(block, new Item.Properties().setId(id).useBlockDescriptionPrefix());
Registry.register(BuiltInRegistries.ITEM, id, blockItem);
}

这里的第一个register方法是按照原版改的,不过加了一个布尔值,用来判断是否注册物品

因为在未来,有些方块的物品需要分离注册,比如作物作物种子

第二个方法待会是我们用的,简化过的注册方法,直接实例化Block

第三个方法,是注册方块物品的,在注册方块的同时,它也会被调用,从而注册物品,这样我们就不用两头跑去注册方块物品了

注册方块

接下来我们来注册方块

1
2
3
4
5
6
public static final Block ICE_ETHER_BLOCK = register("ice_ether_block",
BlockBehaviour.Properties.of().strength(1.0f, 3.0f),true);
public static final Block RAW_ICE_ETHER_BLOCK = register("raw_ice_ether_block",
BlockBehaviour.Properties.ofFullCopy(Blocks.STONE),true);
public static final Block ICE_ETHER_ORE = register("ice_ether_ore",
BlockBehaviour.Properties.of().strength(1.0f, 3.0f).requiresCorrectToolForDrops(),true);

这里我们注册了3个方块,其中,第二个我们直接使用原版STONE的属性,最后一个我们加上了requiresCorrectToolForDrops方法

至于这两个我们会在下一篇教程中详细展开

初始化注册方法

1
2
3
public static void register() {
TutorialMod.LOGGER.info("Registering Mod Blocks for " + TutorialMod.MOD_ID);
}

那么这个方法同样需要在主类调用

1
ModBlocks.register();

添加到物品栏

记得将我们的方块添加到物品栏

1
2
3
output.accept(ModBlocks.ICE_ETHER_BLOCK);
output.accept(ModBlocks.RAW_ICE_ETHER_BLOCK);
output.accept(ModBlocks.ICE_ETHER_ORE);

数据文件

方块状态文件

方块的具体状态是由方块状态文件来决定的,比如按钮的按下状态、的开关状态、楼梯的各种形态等等

具体可以看原版的方块状态文件,这里我们就简单写一下方块状态文件

这些文件都是放在assets/tutorial/blockstates/文件夹下的
ice_ether_block.json

1
2
3
4
5
6
7
{
"variants": {
"": {
"model": "tutorial:block/ice_ether_block"
}
}
}

ice_ether_ore.json

1
2
3
4
5
6
7
{
"variants": {
"": {
"model": "tutorial:block/ice_ether_ore"
}
}
}

raw_ice_ether_block.json

1
2
3
4
5
6
7
{
"variants": {
"": {
"model": "tutorial:block/raw_ice_ether_block"
}
}
}

这个文件是assets/tutorial/blockstates/raw_ice_ether_block.json

方块状态文件里的model就是我们的模型文件

方块模型文件

方块模型文件的路径是assets/tutorial/models/block/

ice_ether_block.json

1
2
3
4
5
6
{
"parent": "minecraft:block/cube_all",
"textures": {
"all": "tutorial:block/ice_ether_block"
}
}

ice_ether_ore.json

1
2
3
4
5
6
{
"parent": "minecraft:block/cube_all",
"textures": {
"all": "tutorial:block/ice_ether_ore"
}
}

raw_ice_ether_block.json

1
2
3
4
5
6
{
"parent": "minecraft:block/cube_all",
"textures": {
"all": "tutorial:block/raw_ice_ether_block"
}
}

这里使用的是cube_all这个模型,即所有的面都是同一个材质

all代表的是材质文件

材质文件

文件路径是src/main/resources/assets/tutorial/textures/block/xxx.png

和你的方块注册名一样

物品描述文件

现在的版本中,可以不写方块物品的模型文件了,但我们之前讲过的物品描述文件(客户端物品)还得写

文件路径依旧是assets/tutorial/items/

ice_ether_block.json

1
2
3
4
5
6
{
"model": {
"type": "minecraft:model",
"model": "tutorial:block/ice_ether_block"
}
}

ice_ether_ore.json

1
2
3
4
5
6
{
"model": {
"type": "minecraft:model",
"model": "tutorial:block/ice_ether_ore"
}
}

raw_ice_ether_block.json

1
2
3
4
5
6
{
"model": {
"type": "minecraft:model",
"model": "tutorial:block/raw_ice_ether_block"
}
}

都指向我们方块的模型文件

语言文件

1
2
3
4
5
{
"block.tutorial.ice_ether_block": "Ice Ether Block",
"block.tutorial.ice_ether_ore": "Ice Ether Ore",
"block.tutorial.raw_ice_ether_block": "Raw Ice Ether Block"
}

与物品的类似,不过它的前缀是block

接下来我们即可以进入游戏进行测试了