本篇教程的视频

本篇教程的源代码

GitHub地址:

自定义方块类

告示牌是属于方块实体了,如果你挖过相关的源代码,你就会发现它在方块实体注册时,它的方块实体类型注册是硬编码的

不过,Forge这里提供了额外的方法,我们可以通过它提供的方法去重写方块实体的注册类型

当然,我们需要去创建自己的方块实体和方块

首先先写四个方块类,各自继承原版的四种不同类型的告示牌,并重写newBlockEntity方法,相应的方块实体我们后面再写

ModStandingSignBlock

1
2
3
4
5
6
7
8
9
10
public class ModStandingSignBlock extends StandingSignBlock {
public ModStandingSignBlock(Properties pProperties, WoodType pType) {
super(pProperties, pType);
}

@Override
public BlockEntity newBlockEntity(BlockPos pPos, BlockState pState) {
return new ModSignBlockEntity(pPos, pState);
}
}

ModHangingSignBlock

1
2
3
4
5
6
7
8
9
10
public class ModHangingSignBlock extends CeilingHangingSignBlock {
public ModHangingSignBlock(Properties pProperties, WoodType pType) {
super(pProperties, pType);
}

@Override
public BlockEntity newBlockEntity(BlockPos pPos, BlockState pState) {
return new ModHangingSignBlockEntity(pPos, pState);
}
}

ModWallSignBlock

1
2
3
4
5
6
7
8
9
10
public class ModWallSignBlock extends WallSignBlock {
public ModWallSignBlock(Properties pProperties, WoodType pType) {
super(pProperties, pType);
}

@Override
public BlockEntity newBlockEntity(BlockPos pPos, BlockState pState) {
return new ModSignBlockEntity(pPos, pState);
}
}

ModWallHangingSignBlock

1
2
3
4
5
6
7
8
9
10
public class ModWallHangingSignBlock extends WallHangingSignBlock {
public ModWallHangingSignBlock(Properties pProperties, WoodType pType) {
super(pProperties, pType);
}

@Override
public BlockEntity newBlockEntity(BlockPos pPos, BlockState pState) {
return new ModHangingSignBlockEntity(pPos, pState);
}
}

虽然说是4个方块类,实际上对应的是两个类型的方块,一个是普通告示牌,一个是悬挂告示牌,只不过它们各有两个状态

注册方块

注册方块之前我们先写一个WoodType,因为它关乎我们告示牌的材质

1
public static final WoodType ICE_ETHER_WOOD_TYPE = WoodType.register(new WoodType(TutorialMod.MOD_ID + ":ice_ether", BlockSetType.OAK));

并在主类的onClientSetup方法中注册这个WoodType

1
Sheets.addWoodType(ModBlocks.ICE_ETHER_WOOD_TYPE);

接下来,我们写四个方块

1
2
3
4
5
6
7
8
9
10
11
public static final RegistryObject<Block> ICE_ETHER_SIGN = BLOCKS.register("ice_ether_sign",
() -> new ModStandingSignBlock(BlockBehaviour.Properties.copy(Blocks.OAK_SIGN), ICE_ETHER_WOOD_TYPE));

public static final RegistryObject<Block> ICE_ETHER_WALL_SIGN = BLOCKS.register("ice_ether_wall_sign",
() -> new ModWallSignBlock(BlockBehaviour.Properties.copy(Blocks.OAK_WALL_SIGN), ICE_ETHER_WOOD_TYPE));

public static final RegistryObject<Block> ICE_ETHER_HANGING_SIGN = BLOCKS.register("ice_ether_hanging_sign",
() -> new ModHangingSignBlock(BlockBehaviour.Properties.copy(Blocks.OAK_HANGING_SIGN), ICE_ETHER_WOOD_TYPE));

public static final RegistryObject<Block> ICE_ETHER_WALL_HANGING_SIGN = BLOCKS.register("ice_ether_wall_hanging_sign",
() -> new ModWallHangingSignBlock(BlockBehaviour.Properties.copy(Blocks.OAK_WALL_HANGING_SIGN), ICE_ETHER_WOOD_TYPE));

这里我们先将方块注册了,当然注意,这里是直接调用BLOCKS.register方法的,物品的我们额外写

注册物品

1
2
3
4
5
6
public static final RegistryObject<Item> ICE_ETHER_SIGN = ITEMS.register("ice_ether_sign",
() -> new SignItem(new Item.Properties().stacksTo(16), ModBlocks.ICE_ETHER_SIGN.get(), ModBlocks.ICE_ETHER_WALL_SIGN.get()));

public static final RegistryObject<Item> ICE_ETHER_HANGING_SIGN = ITEMS.register("ice_ether_hanging_sign",
() -> new HangingSignItem(ModBlocks.ICE_ETHER_HANGING_SIGN.get(), ModBlocks.ICE_ETHER_WALL_HANGING_SIGN.get(),
new Item.Properties().stacksTo(16)));

像原版一样,我们可以将它的最大堆叠上限设置为16

不要忘记将其添加到物品栏

1
2
pOutput.accept(ModItems.ICE_ETHER_SIGN.get());
pOutput.accept(ModItems.ICE_ETHER_HANGING_SIGN.get());

自定义方块实体类

ModSignBlockEntity

1
2
3
4
5
6
7
8
9
10
public class ModSignBlockEntity extends SignBlockEntity {
public ModSignBlockEntity(BlockPos pPos, BlockState pBlockState) {
super(pPos, pBlockState);
}

@Override
public BlockEntityType<?> getType() {

}
}

重写getType()方法,先空着

ModHangingSignBlockEntity

1
2
3
4
5
6
7
8
9
10
public class ModHangingSignBlockEntity extends SignBlockEntity {
public ModHangingSignBlockEntity(BlockPos pPos, BlockState pBlockState) {
super(pPos, pBlockState);
}

@Override
public BlockEntityType<?> getType() {

}
}

同样重写getType()方法,先空着

ModBlockEntities

这个类用于方块实体的注册,不过,方块实体的教程还在后面,这里我们只是简单接触一下

1
2
3
4
5
6
7
8
public class ModBlockEntities {
public static final DeferredRegister<BlockEntityType<?>> BLOCK_ENTITIES =
DeferredRegister.create(ForgeRegistries.BLOCK_ENTITY_TYPES, TutorialMod.MOD_ID);

public static void register(IEventBus bus) {
BLOCK_ENTITIES.register(bus);
}
}

先把两个常规的东西写上,并在主类调用register方法

1
ModBlockEntities.register(modEventBus);

然后是方块实体的注册

1
2
3
4
5
6
7
8
9
public static final RegistryObject<BlockEntityType<ModSignBlockEntity>> MOD_SIGN =
BLOCK_ENTITIES.register("mod_sign", () ->
BlockEntityType.Builder.of(ModSignBlockEntity::new,
ModBlocks.ICE_ETHER_SIGN.get(), ModBlocks.ICE_ETHER_WALL_SIGN.get()).build(null));

public static final RegistryObject<BlockEntityType<ModHangingSignBlockEntity>> MOD_HANGING_SIGN =
BLOCK_ENTITIES.register("mod_hanging_sign", () ->
BlockEntityType.Builder.of(ModHangingSignBlockEntity::new,
ModBlocks.ICE_ETHER_HANGING_SIGN.get(), ModBlocks.ICE_ETHER_WALL_HANGING_SIGN.get()).build(null));

这里的注册调用的是BlockEntityType.Builder.of,未来讲到方块实体我们会经常用到

然后我们可以将前面两个方块实体的方法给补全了,顺便再改一下构造函数

ModSignBlockEntity

1
2
3
4
5
6
7
8
9
10
public class ModSignBlockEntity extends SignBlockEntity {
public ModSignBlockEntity(BlockPos pPos, BlockState pBlockState) {
super(ModBlockEntities.MOD_SIGN.get(), pPos, pBlockState);
}

@Override
public BlockEntityType<?> getType() {
return ModBlockEntities.MOD_SIGN.get();
}
}

ModHangingSignBlockEntity

1
2
3
4
5
6
7
8
9
10
public class ModHangingSignBlockEntity extends SignBlockEntity {
public ModHangingSignBlockEntity(BlockPos pPos, BlockState pBlockState) {
super(ModBlockEntities.MOD_HANGING_SIGN.get(), pPos, pBlockState);
}

@Override
public BlockEntityType<?> getType() {
return ModBlockEntities.MOD_HANGING_SIGN.get();
}
}

方块实体渲染器注册

ClientModEvents中的registerRenderers方法中添加以下内容

1
2
event.registerBlockEntityRenderer(ModBlockEntities.MOD_SIGN.get(), SignRenderer::new);
event.registerBlockEntityRenderer(ModBlockEntities.MOD_HANGING_SIGN.get(), HangingSignRenderer::new);

我们还需要针对方块实体的渲染器进行注册,不然游戏不知道如何渲染

数据文件

战利品列表

1
2
3
4
5
6
7
8
add(ModBlocks.ICE_ETHER_SIGN.get(), 
createSingleItemTable(ModItems.ICE_ETHER_SIGN.get()));
add(ModBlocks.ICE_ETHER_WALL_SIGN.get(),
createSingleItemTable(ModItems.ICE_ETHER_SIGN.get()));
add(ModBlocks.ICE_ETHER_HANGING_SIGN.get(),
createSingleItemTable(ModItems.ICE_ETHER_HANGING_SIGN.get()));
add(ModBlocks.ICE_ETHER_WALL_HANGING_SIGN.get(),
createSingleItemTable(ModItems.ICE_ETHER_HANGING_SIGN.get()));

语言文件

1
2
add(ModItems.ICE_ETHER_SIGN.get(), "Ice Ether Sign");
add(ModItems.ICE_ETHER_HANGING_SIGN.get(), "Ice Ether Hanging Sign");

模型文件

物品的模型文件只要写悬挂告示牌的模型文件即可

1
2
3
4
5
6
7
8
9
10
11
signBlock(((StandingSignBlock) ModBlocks.ICE_ETHER_SIGN.get()), ((WallSignBlock) ModBlocks.ICE_ETHER_WALL_SIGN.get()),
blockTexture(ModBlocks.ICE_ETHER_PLANKS.get()));

hangingSignBlock(ModBlocks.ICE_ETHER_HANGING_SIGN.get(), ModBlocks.ICE_ETHER_WALL_HANGING_SIGN.get(),
blockTexture(ModBlocks.ICE_ETHER_PLANKS.get()));

public void hangingSignBlock(Block signBlock, Block wallSignBlock, ResourceLocation texture) {
ModelFile sign = models().sign(ForgeRegistries.BLOCKS.getKey(signBlock).getPath(), texture);
simpleBlock(signBlock, sign);
simpleBlock(wallSignBlock, sign);
}

悬挂告示牌的生成方法我们是自定义的

接下来还有物品模型

1
2
basicItem(ModItems.ICE_ETHER_SIGN.get());
basicItem(ModItems.ICE_ETHER_HANGING_SIGN.get());

材质

我们需要准备两个方块的展开图材质,两个物品材质

其中一般告示牌的展开图需要放置在textures/entity/signs下,悬挂告示牌的展开图需要放置在textures/entity/signs/hanging

测试

跑好数据生成,我们就可以启动游戏进行测试了