Table of Contents

添加方块实体

介绍

方块实体主要用于在方块内存储数据。创建之前,您需要一个方块。本教程将介绍 BlockEntity 类的创建及其注册。

创建方块实体

最简单的方块实体仅继承 BlockEntity,并使用默认构造函数。这是完全有效的,但不会给予方块任何特殊功能。

DemoBlockEntity.java
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 对象,将 BlockBlockEntity 连接在一起。创建 Block 并保存到 TutorialBlocks 类的静态常量字段 DEMO_BLOCK 中。在本教程中,方块实体的 ID 是 tutorial:demo_block

BlockEntityType 应在类的初始化或 onInitialize 方法中注册,以确保在正确的时候注册。在这个例子中,我们在单独的类中注册(见 blocks

TutorialBlocks.java
public final class TutorialBlocks {
    [...]
 
    public static final DemoBlock DEMO_BLOCK = register("demo_block", new DemoBlock(AbstractBlock.Settings.create()));
 
    [...]
}
TutorialBlockEntityTypes.java
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 方法:

ExampleMod.java
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。对于战利品表,有后续教程会提到如何改进战利品表表以包含方块实体数据。

将方块实体连接到方块

一旦创建并注册了 BlockEntityType,就需要与之关联的方块。你可以继承 BlockWithEntity,或者简单地实现 BlockEntityProvider 并覆盖 createBlockEntity。每次放置方块,就会产生对应的方块实体。

DemoBlock.java
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 类中:

DemoBlock.java
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 类中:

DemoBlockEntity.java
public class DemoBlockEntity extends BlockEntity {
    [...]
 
    public static void tick(World world, BlockPos pos, BlockState state, DemoBlockEntity be) {
        [...]
    }
}

下一步

现在,您应该拥有自己的 BlockEntity,可以以各种方式扩展以适应您的需求。注册了 BlockEntityType,并用它来将 BlockBlockEntity 类连接在一起。然后,继承了 BlockWithEntity 并使用了接口 BlockEntityProvider 以提供 BlockEntity 的新实例。

你也学习了如何为它添加课刻。下一步,可以尝试对方块实体进行一些复杂的操作,例如: