本篇教程的视频

本篇教程的源代码

GitHub地址:

柱类方块类

这里我们直接来写这个柱类方块,因为我们之前已经写过一个可连接方块了,而这一次写的柱子要比之前的沙发还要简单

之前的沙发判断的是左右,而这一次,我们只要判断上下的方位就好了

ModPillarBlock

创建ModPillarBlock类,继承自Block

1
2
3
4
5
public class ModPillarBlock extends Block {
public ModPillarBlock(Properties pProperties) {
super(pProperties);
}
}

方块属性

同样的,我们来写一个方块属性的枚举类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public enum Type implements StringRepresentable {
SINGLE("single"),
TOP("top"),
BOTTOM("bottom"),
MIDDLE("middle");

private final String id;

Type(String id) {
this.id = id;
}

@Override
public String getSerializedName() {
return this.id;
}
}

同样还是实现StringRepresentable这个接口,然后按照之前的写法来写4个不同的状态

声明方块属性

1
public static final EnumProperty<Type> TYPE = EnumProperty.create("type2", Type.class);

因为我们之前在沙发那期教程中,已经注册了一个EnumProperty类型的type,所以这里的注册id要改一下

然后进行方块属性的初始化

1
this.registerDefaultState(this.defaultBlockState().setValue(TYPE, Type.SINGLE));

那么默认就是SINGLE这个状态

还要将方块属性加入到方块中

1
2
3
4
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> pBuilder) {
pBuilder.add(TYPE);
}

重写 updateShape 方法

最后我们就要重写方法,改变方块的方块状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public BlockState updateShape(BlockState pState, Direction pDirection, BlockState pNeighborState, LevelAccessor pLevel, BlockPos pPos, BlockPos pNeighborPos) {
boolean top = pLevel.getBlockState(pPos.above()).is(this);
boolean bottom = pLevel.getBlockState(pPos.below()).is(this);
if (top && bottom) {
return pState.setValue(TYPE, Type.MIDDLE);
} else if (top) {
return pState.setValue(TYPE, Type.BOTTOM);
} else if (bottom) {
return pState.setValue(TYPE, Type.TOP);
} else {
return pState.setValue(TYPE, Type.SINGLE);
}
}

这里我们要重写updateShape方法

两个布尔值分别判断当前方块的上下是否为相同的方块,然后根据这两个布尔值来设置不同的方块状态

好了,这个方块类就这样写完了

其实方块状态能玩出很多花活来,你可以用方块状态来实现很多各种各样的方块,
即便你现在不是很熟练,写得多了,自然就会了

注册方块

注册

注册方块和之前一样,直接注册即可

1
2
3
public static final RegistryObject<Block> PILLAR = 
registerBlocks("pillar",
() -> new ModPillarBlock(BlockBehaviour.Properties.of().strength(2.0f, 2.0f).noOcclusion()));

实例化ModPillarBlock,同样,因为这个是Blockbench制作的非实心方块,所以这里要设置noOcclusion()

物品栏

同样的,我们也要注册物品栏

1
pOutput.accept(ModBlocks.PILLAR.get());

数据文件

战利品列表

1
dropSelf(ModBlocks.PILLAR.get());

常规的战利品列表

语言文件

1
add(ModBlocks.PILLAR.get(), "Pillar");

模型文件

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
pillar(ModBlocks.PILLAR, "pillar");

private <T extends Block> void pillar(RegistryObject<T> block, String name) {
ModelFile modelSingle = models().getExistingFile(modLoc("block/" + name));
ModelFile modelTop = models().getExistingFile(modLoc("block/" + name + "_top"));
ModelFile modelBottom = models().getExistingFile(modLoc("block/" + name + "_bottom"));
ModelFile modelMiddle = models().getExistingFile(modLoc("block/" + name + "_middle"));

getVariantBuilder(block.get()).forAllStates(state -> {
ModPillarBlock.Type type = state.getValue(ModPillarBlock.TYPE);

ModelFile t;
switch (type) {
case TOP -> t = modelTop;
case MIDDLE -> t = modelMiddle;
case BOTTOM -> t = modelBottom;
default -> t = modelSingle;
}

return ConfiguredModel.builder()
.modelFile(t)
.build();
});
simpleBlockItem(block.get(), modelSingle);
}

我们修改一下之前的sofa方法,去掉其中的FACING,然后就可以用了

生成的方块状态文件长这样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"variants": {
"type2=bottom": {
"model": "tutorial_mod:block/pillar_bottom"
},
"type2=middle": {
"model": "tutorial_mod:block/pillar_middle"
},
"type2=single": {
"model": "tutorial_mod:block/pillar"
},
"type2=top": {
"model": "tutorial_mod:block/pillar_top"
}
}
}

测试

那么最后我们就可以进入游戏进行测试了