User Tools

Site Tools


zh_cn:tutorial:blockentity

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
Last revisionBoth sides next revision
zh_cn:tutorial:blockentity [2022/02/07 07:37] – [序列化数据] solidblockzh_cn:tutorial:blockentity [2022/12/16 02:14] – [注册你的方块实体] solidblock
Line 1: Line 1:
-====== 添加一个方块实体 ======+====== 添加方块实体 ======
  
 ===== 介绍 ===== ===== 介绍 =====
- +方块实体主要用于在方块内存储数据。创建之前,您需要一个[[zh_cn:tutorial:blocks|方块]]。本教程将介绍BlockEntity类的创建及其注册。
-BlockEntity主要用于在方块内存储数据。创建之前,您需要一个[[zh_cn:tutorial:blocks|方块]]。本教程将介绍BlockEntity类的创建及其注册。+
  
 ===== 创建一个方块实体 ===== ===== 创建一个方块实体 =====
  
-最简单的方块实体仅继承''BlockEntity'',并使用默认构造函数。 这是完全有效的,但不会给予方块任何特殊功能。+最简单的方块实体仅继承 ''BlockEntity'',并使用默认构造函数。 这是完全有效的,但不会给予方块任何特殊功能。
  
 <code java> <code java>
Line 17: Line 16:
 </code> </code>
  
-面将向您展示如何创建''ExampleMod.DEMO_BLOCK_ENTITY''字段。 +请确保这个构造方法只接收这两个参数,否则我们后写的方法引用 ''DemoBlockEntity::new'' 会无效。这个 ''ExampleMod.DEMO_BLOCK_ENTITY'' 字段稍后再写。 
- +  
-您可以简单地向此准系统类添加变量,或实现诸如''Tickable''和''Inventory''之类的接口以添加更多功能。''Tickable''提供了一个单独的''tick()''方法,该方法将会在世界加载的每一刻调用一次。而''Inventory''则允许您的BlockEntity与自动进行交互,例如漏斗——稍后可能会有专门针对此接口的单独教程。+您可以简单地向此准系统类添加变量,或实现诸如 ''Tickable'' 和 ''Inventory'' 之类的接口以添加更多功能。''Tickable'' 提供了一个单独的 ''tick()'' 方法,该方法将会在世界加载的每一刻调用一次。而 ''Inventory'' 则允许您的方块实体自动进行交互,例如漏斗——稍后可能会有专门针对此接口的单独教程。
  
 ===== 注册你的方块实体 ===== ===== 注册你的方块实体 =====
  
-一旦创建了''BlockEntity''类,您将需要对其进行注册以使其起作用。 此过程的第一步是创建一个''BlockEntityType'',它将''Block''和''BlockEntity''链接在一起。 假设您的''Block''已创建并保存到本地变量''DEMO_BLOCK'',则将在下面的行中创建匹配的''BlockEntityType''。在本教程中,方块实体的ID是''tutorial:demo_block_entity''+一旦创建了 ''BlockEntity'' 类,您将需要对其进行注册以使其起作用。 此过程的第一步是创建一个 ''BlockEntityType'',它将 ''Block'' 和 ''BlockEntity'' 链接在一起。假设您的 ''Block'' 已创建并保存到了静态常字段 ''DEMO_BLOCK'' ,则将在下面的行中创建匹配的 ''BlockEntityType''。在本教程中,方块实体的ID是 ''tutorial:demo_block_entity''
  
-''BlockEntityType''应在的''onInitialize''方法中注册,以确保它在正确的时候注册。+''BlockEntityType'' 应在初始化或 ''onInitialize'' 方法中注册,以确保它在正确的时候注册。
  
 <code java> <code java>
-public static BlockEntityType<DemoBlockEntity> DEMO_BLOCK_ENTITY; +    public static final BlockEntityType<DemoBlockEntity> DEMO_BLOCK_ENTITY = Registry.register( 
-  +        Registries.BLOCK_ENTITY_TYPE, 
-@Override +        new Identifier("tutorial", "demo_block_entity"), 
-public void onInitialize() { +        FabricBlockEntityTypeBuilder.create(DemoBlockEntity::new, DEMO_BLOCK).build() 
-    DEMO_BLOCK_ENTITY = Registry.register(Registry.BLOCK_ENTITY_TYPE, "tutorial:demo_block_entity", FabricBlockEntityTypeBuilder.create(DemoBlockEntity::new, DEMO_BLOCK).build(null)); +    );
-}+
 </code> </code>
 +
 +这个方块实体类型定义了只有 ''DEMO_BLOCK'' 可以拥有这个方块实体类型。如果你想要让方块实体类型支持更多方块,只需要将其添加到 ''FabricBlockEntityTypeBuilder.create'' 的参数中即可。如果方法引用 ''DemoBlockEntity::new'' 无法解析,检查 ''DemoBlockEntity'' 的构造方法的参数是否正确。
  
 ==== 将方块实体连接到方块 ==== ==== 将方块实体连接到方块 ====
  
-一旦创建并注册了''BlockEntityType'',就可以在''Block''类中简单地实现''BlockEntityProvider''。每次放置方块,就会产生对应的方块实体。+一旦创建并注册了 ''BlockEntityType'',就可以在 ''Block'' 类中简单地实现 ''BlockEntityProvider''。每次放置方块,就会产生对应的方块实体。
  
 <code java> <code java>
Line 56: Line 56:
 如果要将任何数据存储在 ''BlockEntity'' 中,则需要保存并加载,或者仅在加载 ''BlockEntity'' 时保留,并且只要您返回到它。幸运的是,保存和加载非常简单——只需要覆盖 ''writeNbt()'' 和 ''readNbt()'' 即可。 如果要将任何数据存储在 ''BlockEntity'' 中,则需要保存并加载,或者仅在加载 ''BlockEntity'' 时保留,并且只要您返回到它。幸运的是,保存和加载非常简单——只需要覆盖 ''writeNbt()'' 和 ''readNbt()'' 即可。
  
-''writeNbt()'' 返回一个 ''NBTCompound''其中应包含 ''BlockEntity'' 中的所有数据。如果您需要将 ''BlockEntity'' 数据与客户端同步,则此数据将保存到磁盘并通过数据包发送。调用 ''writeNbt'' 的默认实现非常重要,因为它将“识数据(Identifying data)''(位置和ID)''保存到标签中。否则,您尝试保存的所有其他数据都将丢失,因为它与位置和 ''BlockEntityType'' 不相关。知道了这一点,下面的示例演示了如何将 ''BlockEntity'' 中的整数保存到标签中。在此示例中,整数保存在键 ''number'' 下——您可以将其替换为任何字符串,但是标签中的每个键只能有一个条目(entry),并且需要记住该键以便以后检索数据。+''writeNbt()'' 将会修改其参数 ''nbt'' 的内容这个 ''nbt'' 包含了方块实体中的所有数据。该方法通常不会修改方块实体本身。方块实体数据将会存储在磁盘中,并且如果您需要将 ''BlockEntity'' 数据与客户端同步,则通过包发送。调用 ''super.writeNbt()'' 非常重要,因为方块实体的坐及其方块实体类型 id 保存到 nbt 中。否则,您尝试保存的所有其他数据都将丢失,因为它与位置和 ''BlockEntityType'' 不相关。 
 + 
 +知道了这一点,下面的示例演示了如何将 ''BlockEntity'' 中的整数保存到标签中。在此示例中,整数保存在键 ''number'' 下——您可以将其替换为任何字符串,但是标签中的每个键只能有一个,并且需要记住该键以便以后读取数据。
  
 <code java> <code java>
Line 70: Line 72:
     // 序列化方块实体     // 序列化方块实体
     @Override     @Override
-    public voidwriteNbt(NbtCompound tag) { +    public void writeNbt(NbtCompound nbt) {
-        writeNbt(tag); +
         // Save the current value of the number to the tag         // Save the current value of the number to the tag
-        tag.putInt("number", number);+        nbt.putInt("number", number); 
 +         
 +        super.writeNbt(nbt)
     }     }
 } }
 </code> </code>
  
-为了以后检索数据,您还需要覆盖 ''readNBT''。 此方法与 ''writeNBT'' 相反——不会将数据保存到 ''NBTCompound'',而是您已经有了之前保存的标签,使您可以检索所需的任何数据。与 ''writeNbt'' 一样,必须调用 ''super.readNbt'',并且您将需要使用相同的键来检索保存的数据。要检索我们之前保存的数字,请参见下面的示例。+为了以后读取数据,您还需要覆盖 ''readNBT''。此方法与 ''writeNBT'' 相反——不会将数据保存到 ''NBTCompound'',而是您已经有了之前保存的 nbt 数据,使您可以检索所需的任何数据。该方法会修改方块实体本身,不会修改这个 ''nbt'' 参数。与 ''writeNbt'' 一样,必须调用 ''super.readNbt'',并且您将需要使用相同的键来检索保存的数据。要检索我们之前保存的数字,请参见下面的示例。
  
 <code java> <code java>
-// 取消序列化方块实体+// 序列化方块实体
 @Override @Override
-public void readNbt(NbtCompound tag) { +public void readNbt(NbtCompound nbt) { 
-    super.readNbt(tag); +    super.readNbt(nbt); 
-    number = tag.getInt("number");+     
 +    number = nbt.getInt("number");
 } }
 </code> </code>
  
-一旦实现了 ''writeNbt'' 和 ''readNbt'' 方法,您只需要确保在合适的时候调用即可。每当您的 ''BlockEntity'' 数据发生更改并需要保存时,请调用 ''markDirty()''。这会将方块所在的区块标记为dirty,在世界下次保存时强制调用 ''writeNbt'' 方法。原则上,只要改了 ''BlockEntity'' 类中的任何一个自定义变量,就只需要简单调用 ''markDirty''+一旦实现了 ''writeNbt'' 和 ''readNbt'' 方法,您只需要确保在合适的时候调用即可。每当您的 ''BlockEntity'' 数据发生更改并需要保存时,请调用 ''markDirty()''。这会将方块所在的区块标记为dirty,在世界下次保存时强制调用 ''writeNbt'' 方法。原则上,只要改了 ''BlockEntity'' 类中的任何一个自定义变量,就只需要简单调用 ''markDirty'',否则当你退出并重进世界后,这个方块实体依然会是没有修改过的
  
 ===== 将服务器数据同步至客户端 ===== ===== 将服务器数据同步至客户端 =====
-数据通常是在服务器世界读取的。有时候你需要将所有或者部分数据同步到客户端,比如用于渲染。+数据通常是在服务器世界读取的。大多数数据都是客户端不需要知道的,例如客户端并不需要知道箱子和熔炉里面什么,除非打开它。但对于某些方块实体,例如告示牌和旗帜,你需要将所有或者部分数据告知客户端,比如用于渲染。
  
-对于 1.17.1 及以下版本,请实现 Fabric API 中的''BlockEntityClientSerializable''。此接口提供了 ''fromClientTag'' 和 ''toClientTag'' 方法,其作用与前面讨论的 ''readNbt'' 和 ''writeNbt'' 方法基本相同,只是专门用于发送和接收客户端上的数据。你可以简单地在 ''fromClientTag'' 和 ''toClientTag'' 两个方法中调用 ''readNbt'' 和 ''writeNbt''+对于 1.17.1 及以下版本,请实现 Fabric API 中的 ''BlockEntityClientSerializable''。此接口提供了 ''fromClientTag'' 和 ''toClientTag'' 方法,其作用与前面讨论的 ''readNbt'' 和 ''writeNbt'' 方法基本相同,只是专门用于发送和接收客户端上的数据。你可以简单地在 ''fromClientTag'' 和 ''toClientTag'' 两个方法中调用 ''readNbt'' 和 ''writeNbt''
  
 对于 1.18 及以上版本,请覆盖 ''toUpdatePacket'' 和 ''toInitialChunkDataNbt'' 对于 1.18 及以上版本,请覆盖 ''toUpdatePacket'' 和 ''toInitialChunkDataNbt''
Line 110: Line 113:
   }   }
 </code> </code>
-===== 方块实体Ticking ===== +**警告**: 需要调用 ''world.updateListeners(pos, state, state, Block.NOTIFY_LISTENERS);'' 来触发数据的同步,否则客户端不会知道方块实体已经改变。 
-1.17添加了静态的ticking, where before you'd implement the ''Tickable'' interface. 对于需要tick的方块,你只需要使用''Block''中的''getTicker'',链接回到''Block Entity''。参考下面关于ticking的一个常见的实现。+===== 方块实体刻 ===== 
 +1.17 添加了静态的刻,然后你就可以实现 ''Tickable'' 接口。对于需要计划刻的方块,你只需要使用 ''Block'' 中的 ''getTicker'',链接回到 ''Block Entity''。参考下面关于的一个常见的实现。
  
-在你的''Block''类中:+在你的 ''Block'' 类中:
 <code java> <code java>
 public class DemoBlock extends BlockWithEntity { public class DemoBlock extends BlockWithEntity {
Line 127: Line 131:
     }     }
 </code> </code>
-在你的''Block Entity''中:+在你的 ''BlockEntity'' 中:
 <code java> <code java>
 public class DemoBlockEntity extends BlockEntity { public class DemoBlockEntity extends BlockEntity {
zh_cn/tutorial/blockentity.txt · Last modified: 2023/09/03 01:37 by wjz_p