方块实体主要用于在方块内存储数据。创建之前,您需要一个方块。本教程将介绍 BlockEntity 类的创建及其注册。
最简单的方块实体仅继承 BlockEntity
,并使用默认构造函数。这是完全有效的,但不会给予方块任何特殊功能。
public class DemoBlockEntity extends BlockEntity { public DemoBlockEntity(BlockPos pos, BlockState state) { super(TutorialBlockEntityTypes.DEMO_BLOCK, pos, state); } }
请确保这个构造方法只接收这两个参数,否则我们后面写的方法引用 DemoBlockEntity::new
将会无效。这个 TutorialBlockEntityTypes.DEMO_BLOCK
字段稍后再写。
方块实体支持一系列方法以支持一些功能,例如与 NBT 之间的序列化和反序列化、提供物品栏等。本教程提供方块实体功能的最常见一些实现。
一旦创建了 BlockEntity
类,就需要注册它才起作用。这个过程的第一步是在我们的 TutorialBlockEntities
类中创建 BlockEntityType
对象,将 Block
和 BlockEntity
连接在一起。创建 Block
并保存到 TutorialBlocks
类的静态常量字段 DEMO_BLOCK
中。在本教程中,方块实体的 ID 是 tutorial:demo_block
。
BlockEntityType
应在类的初始化或 onInitialize
方法中注册,以确保在正确的时候注册。在这个例子中,我们在单独的类中注册(见 blocks)
public final class TutorialBlocks { [...] public static final DemoBlock DEMO_BLOCK = register("demo_block", new DemoBlock(AbstractBlock.Settings.create())); [...] }
public class TutorialBlockEntityTypes { public static <T extends BlockEntityType<?>> T register(String path, T blockEntityType) { return Registry.register(Registries.BLOCK_ENTITY_TYPE, Identifier.of("tutorial", path), blockEntityType); } public static final BlockEntityType<DemoBlockEntity> DEMO_BLOCK = register( "demo_block", // 对于 1.21.2 及以上的版本, // 请将 `BlockEntityType.Builder` 替换为 `FabricBlockEntityTypeBuilder`。 BlockEntityType.Builder.create(DemoBlockEntity::new, TutorialBlocks.DEMO_BLOCK).build() ); public static void initialize() { } }
记得要在 ModInitializer
里面引用 initialize
方法:
public class ExampleMod implements ModInitializer { [...] @Override public void onInitialize() { [...] TutorialBlockEntityTypes.initialize(); } }
对于旧版本,如果无法访问 BlockEntityType.Builder.create
,尝试 FabricBlockEntityTypeBuilder.create
。
这个方块实体类型定义了只有 TutorialBlocks.DEMO_BLOCK
可以拥有这个方块实体类型。如果你想要让方块实体类型支持更多方块,只需要将其添加到 BlockEntityType.Builder.create
的参数中即可。如果方法引用 DemoBlockEntity::new
无法解析,检查 DemoBlockEntity
的构造方法的参数是否正确。
一旦创建并注册了 BlockEntityType
,就需要与之关联的方块。你可以继承 BlockWithEntity
,或者简单地实现 BlockEntityProvider
并覆盖 createBlockEntity
。每次放置方块,就会产生对应的方块实体。
public class DemoBlock extends BlockWithEntity { public DemoBlock(Settings settings) { super(settings); } @Override protected MapCodec<? extends DemoBlock> getCodec() { return createCodec(DemoBlock::new); } @Override public BlockEntity createBlockEntity(BlockPos pos, BlockState state) { return new DemoBlockEntity(pos, state); } @Override protected BlockRenderType getRenderType(BlockState state) { return BlockRenderType.MODEL; } }
覆盖getRenderType
为因为 BlockWithEntity
会默认让它不可见。
刻是指方块在每一刻(每 1/20 秒)运行些什么。要让方块有刻,通常需要使用 Block
中的 getTicker
,链接回到 BlockEntity
中的静态方法 tick
。参考下面关于刻的常见的实现。
在你的 Block
类中:
public class DemoBlock extends BlockWithEntity { [...] @Override public <T extends BlockEntity> BlockEntityTicker<T> getTicker(World world, BlockState state, BlockEntityType<T> type) { // 如果只需要在服务器上有刻,确保检查 world.isClient return validateTicker(type, ExampleMod.DEMO_BLOCK_ENTITY, DemoBlockEntity::tick); }
在你的 BlockEntity
类中:
public class DemoBlockEntity extends BlockEntity { [...] public static void tick(World world, BlockPos pos, BlockState state, DemoBlockEntity be) { [...] } }
现在,您应该拥有自己的 BlockEntity
,可以以各种方式扩展以适应您的需求。注册了 BlockEntityType
,并用它来将 Block
和 BlockEntity
类连接在一起。然后,继承了 BlockWithEntity
并使用了接口 BlockEntityProvider
以提供 BlockEntity
的新实例。
你也学习了如何为它添加课刻。下一步,可以尝试对方块实体进行一些复杂的操作,例如: