====== 添加方块实体 ======
===== 介绍 =====
**方块实体**主要用于在方块内存储数据。创建之前,您需要一个[[blocks|方块]]。本教程将介绍 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 register(String path, T blockEntityType) {
return Registry.register(Registries.BLOCK_ENTITY_TYPE, Identifier.of("tutorial", path), blockEntityType);
}
public static final BlockEntityType 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'' 的构造方法的参数是否正确。
> **注意:**和其他方块一样,这个方块也需要方块模型和物品模型,可能也需要战利品表,关于如何创建请参见 [[blocks]]。对于战利品表,有[[blockentity_sync_itemstac|后续教程]]会提到如何改进战利品表表以包含方块实体数据。
==== 将方块实体连接到方块 ====
一旦创建并注册了 ''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 BlockEntityTicker getTicker(World world, BlockState state, BlockEntityType 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'' 的新实例。
你也学习了如何为它添加课刻。下一步,可以尝试对方块实体进行一些复杂的操作,例如:
* [[blockentity_modify_data|修改方块实体数据]]
* [[inventory|作为物品栏在方块实体存储物品]]
* [[blockentityrenderers|使用方块实体渲染器动态渲染]]
* [[screenhandler|创建容器方块]]