zh_cn:tutorial:screenhandler
Differences
This shows you the differences between two versions of the page.
| Next revision | Previous revision | ||
| zh_cn:tutorial:screenhandler [2021/11/08 15:45] – created breakice | zh_cn:tutorial:screenhandler [2024/08/27 04:50] (current) – update translations solidblock | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | ====== 创建一个容器方块 ====== | + | ====== 创建容器方块 ====== |
| - | 这篇教程会创建一个类似分解器的方块,用于解释如何使用FabricAPI中的 | + | 这篇教程会创建一个类似发射器的简单存储方块,并解释如何使用 |
| - | 让我们先做一些词汇练习: | + | 先解释一些词汇: |
| - | **Screenhandler:** | + | **Screenhandler:** |
| - | '' | + | '' |
| - | 我们的子类会有以下两个构造器: 一个将在服务器端使用,并将储存真正的 '' | + | |
| - | **Screen:** | + | **Screen:** |
| - | '' | + | '' |
| ===== 方块和方块实体类 ===== | ===== 方块和方块实体类 ===== | ||
| - | 首先我们需要创建 '' | + | 首先我们需要创建 '' |
| <code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
| Line 19: | Line 18: | ||
| protected BoxBlock(Settings settings) { | protected BoxBlock(Settings settings) { | ||
| super(settings); | super(settings); | ||
| + | } | ||
| + | | ||
| + | // 从 1.20.5 开始需要有这个方法。 | ||
| + | @Override | ||
| + | protected MapCodec<? | ||
| + | return createCodec(BoxBlock:: | ||
| } | } | ||
| Line 28: | Line 33: | ||
| @Override | @Override | ||
| public BlockRenderType getRenderType(BlockState state) { | public BlockRenderType getRenderType(BlockState state) { | ||
| - | // 通过从 BlockWithEntity 继承而来的值是默认为 INVISIBLE | + | // 从 BlockWithEntity 继承的默认值为 INVISIBLE,所以这里需要进行改变! |
| return BlockRenderType.MODEL; | return BlockRenderType.MODEL; | ||
| } | } | ||
| Line 35: | Line 40: | ||
| public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { | public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { | ||
| if (!world.isClient) { | if (!world.isClient) { | ||
| - | // 这里会调用 BlockWithEntity 的 createScreenHandlerFactory 方法, 他会转换为 | + | // 这里会调用 BlockWithEntity 的 createScreenHandlerFactory 方法,会将返回的方块实体强转为 |
| - | // 一个 namedScreenHandlerFactory。 如果你的方块没有继承 BlockWithEntity,那就需要单独实现 createScreenHandlerFactory. | + | // 一个 namedScreenHandlerFactory。如果你的方块没有继承 BlockWithEntity,那就需要单独实现 createScreenHandlerFactory。 |
| NamedScreenHandlerFactory screenHandlerFactory = state.createScreenHandlerFactory(world, | NamedScreenHandlerFactory screenHandlerFactory = state.createScreenHandlerFactory(world, | ||
| if (screenHandlerFactory != null) { | if (screenHandlerFactory != null) { | ||
| - | //With this call the server will request the client to open the appropriate | + | // 这个调用会让服务器请求客户端开启合适的 |
| player.openHandledScreen(screenHandlerFactory); | player.openHandledScreen(screenHandlerFactory); | ||
| } | } | ||
| Line 48: | Line 53: | ||
| | | ||
| | | ||
| - | // 这个方法能让你储存在他里面的物品在被破坏时洒落一地! | + | // 这个方法能让方块破坏时物品全部掉落 |
| @Override | @Override | ||
| public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) { | public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) { | ||
| Line 74: | Line 79: | ||
| </ | </ | ||
| - | 我们接下里要创建 '' | + | 我们接下来要创建 '' |
| <code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
| Line 93: | Line 98: | ||
| } | } | ||
| - | //These Methods are from the NamedScreenHandlerFactory | + | // 这些方法来自 |
| - | // | + | // createMenu |
| - | // | + | // getDisplayName |
| @Override | @Override | ||
| public ScreenHandler createMenu(int syncId, PlayerInventory playerInventory, | public ScreenHandler createMenu(int syncId, PlayerInventory playerInventory, | ||
| - | //We provide | + | // 因为我们的类实现 Inventory,所以将*这个*提供给 ScreenHandler |
| - | //Only the Server has the Inventory at the start, this will be synced to the client in the ScreenHandler | + | // 一开始只有服务器拥有物品栏,然后在 |
| return new BoxScreenHandler(syncId, | return new BoxScreenHandler(syncId, | ||
| } | } | ||
| Line 106: | Line 111: | ||
| @Override | @Override | ||
| public Text getDisplayName() { | public Text getDisplayName() { | ||
| - | return new TranslatableText(getCachedState().getBlock().getTranslationKey()); | + | |
| + | // 对于 1.19 之前的版本,请使用: | ||
| + | // return new TranslatableText(getCachedState().getBlock().getTranslationKey()); | ||
| } | } | ||
| | | ||
| + | // 以下两个方法,旧版本请移除参数 `registryLookup`。 | ||
| @Override | @Override | ||
| - | public void readNbt(NbtCompound nbt) { | + | public void readNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registryLookup) { |
| - | super.readNbt(nbt); | + | super.readNbt(nbt, registryLookup); |
| - | Inventories.readNbt(nbt, | + | Inventories.readNbt(nbt, |
| } | } | ||
| @Override | @Override | ||
| - | public NbtCompound writeNbt(NbtCompound nbt) { | + | public NbtCompound writeNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registryLookup) { |
| - | super.writeNbt(nbt); | + | super.writeNbt(nbt, registryLookup); |
| - | Inventories.writeNbt(nbt, | + | Inventories.writeNbt(nbt, |
| return nbt; | return nbt; | ||
| } | } | ||
| Line 125: | Line 133: | ||
| </ | </ | ||
| - | ===== Registering Block, BlockItem and BlockEntity | + | ===== 注册方块、物品和方块实体 |
| - | + | 在这个例子中,方块、方块物品。方块实体直接注册在 '' | |
| <code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
| public class ExampleMod implements ModInitializer { | public class ExampleMod implements ModInitializer { | ||
| - | | + | // 对于 1.21 之前的版本,将 `Identifier.of` 替换为 `new Identifier`。 |
| - | public static final BlockItem BOX_BLOCK_ITEM; | + | |
| - | public static final BlockEntityType< | + | |
| - | + | | |
| - | public static final String MOD_ID = " | + | public static final BlockItem |
| - | | + | |
| - | public static final Identifier BOX = new Identifier(MOD_ID, " | + | |
| - | + | | |
| - | static | + | |
| - | | + | // 在 1.17 使用 |
| - | BOX_BLOCK_ITEM = Registry.register(Registry.ITEM, | + | // public static final BlockEntityType< |
| - | + | // FabricBlockEntityTypeBuilder.create(BoxBlockEntity:: | |
| - | | + | |
| - | // pre-1.17 | + | |
| - | | + | |
| - | // In 1.17 use FabricBlockEntityTypeBuilder | + | |
| - | BOX_BLOCK_ENTITY = Registry.register(Registry.BLOCK_ENTITY_TYPE, | + | |
| - | } | + | |
| @Override | @Override | ||
| public void onInitialize() { | public void onInitialize() { | ||
| - | |||
| } | } | ||
| } | } | ||
| - | |||
| - | |||
| </ | </ | ||
| - | ===== ScreenHandler | + | ===== ScreenHandler |
| - | As explained earlier, we need both a '' | + | 正如前面解释的,我们同时需要 |
| <code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
| Line 168: | Line 166: | ||
| private final Inventory inventory; | private final Inventory inventory; | ||
| - | //This constructor gets called on the client when the server wants it to open the screenHandler, | + | // 服务器想要客户端开启 |
| - | //The client will call the other constructor with an empty Inventory and the screenHandler | + | // 如有空的物品栏,客户端会调用其他构造器,screenHandler |
| - | //sync this empty inventory with the inventory on the server. | + | // 在客户端将空白物品栏同步给物品栏。 |
| public BoxScreenHandler(int syncId, PlayerInventory playerInventory) { | public BoxScreenHandler(int syncId, PlayerInventory playerInventory) { | ||
| this(syncId, | this(syncId, | ||
| } | } | ||
| - | //This constructor gets called from the BlockEntity | + | // 这个构造器是在服务器的 |
| - | //and can therefore directly provide it as an argument. This inventory will then be synced to the client. | + | // 并直接将其作为参数传入。然后物品栏在客户端完成同步。 |
| public BoxScreenHandler(int syncId, PlayerInventory playerInventory, | public BoxScreenHandler(int syncId, PlayerInventory playerInventory, | ||
| super(ExampleMod.BOX_SCREEN_HANDLER, | super(ExampleMod.BOX_SCREEN_HANDLER, | ||
| checkSize(inventory, | checkSize(inventory, | ||
| this.inventory = inventory; | this.inventory = inventory; | ||
| - | //some inventories do custom logic when a player opens it. | + | // 玩家开启时,一些物品栏有自定义的逻辑。 |
| inventory.onOpen(playerInventory.player); | inventory.onOpen(playerInventory.player); | ||
| - | //This will place the slot in the correct locations for a 3x3 Grid. The slots exist on both server and client! | + | // 这会将槽位放置在 3×3 网格的正确位置中。这些槽位在客户端和服务器中都存在! |
| - | //This will not render the background of the slots however, this is the Screens | + | // 但是这不会渲染槽位的背景,这是 |
| int m; | int m; | ||
| int l; | int l; | ||
| Line 194: | Line 192: | ||
| } | } | ||
| } | } | ||
| - | //The player inventory | + | // 玩家物品栏 |
| for (m = 0; m < 3; ++m) { | for (m = 0; m < 3; ++m) { | ||
| for (l = 0; l < 9; ++l) { | for (l = 0; l < 9; ++l) { | ||
| Line 200: | Line 198: | ||
| } | } | ||
| } | } | ||
| - | //The player Hotbar | + | // 玩家快捷栏 |
| for (m = 0; m < 9; ++m) { | for (m = 0; m < 9; ++m) { | ||
| this.addSlot(new Slot(playerInventory, | this.addSlot(new Slot(playerInventory, | ||
| Line 212: | Line 210: | ||
| } | } | ||
| - | // Shift + Player Inv Slot | + | // Shift + 玩家物品栏槽位 |
| @Override | @Override | ||
| public ItemStack transferSlot(PlayerEntity player, int invSlot) { | public ItemStack transferSlot(PlayerEntity player, int invSlot) { | ||
| Line 243: | Line 241: | ||
| <code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
| public class BoxScreen extends HandledScreen< | public class BoxScreen extends HandledScreen< | ||
| - | //A path to the gui texture. In this example we use the texture from the dispenser | + | // GUI 纹理的路径,本例中使用发射器中的纹理 |
| - | private static final Identifier TEXTURE = new Identifier(" | + | private static final Identifier TEXTURE = Identifier.ofVanilla(" |
| public BoxScreen(ScreenHandler handler, PlayerInventory inventory, Text title) { | public BoxScreen(ScreenHandler handler, PlayerInventory inventory, Text title) { | ||
| Line 251: | Line 249: | ||
| @Override | @Override | ||
| - | protected void drawBackground(MatrixStack matrices, float delta, int mouseX, int mouseY) { | + | protected void drawBackground(DrawContext context, float delta, int mouseX, int mouseY) { |
| - | RenderSystem.setShader(GameRenderer:: | + | RenderSystem.setShader(GameRenderer:: |
| RenderSystem.setShaderColor(1.0F, | RenderSystem.setShaderColor(1.0F, | ||
| RenderSystem.setShaderTexture(0, | RenderSystem.setShaderTexture(0, | ||
| int x = (width - backgroundWidth) / 2; | int x = (width - backgroundWidth) / 2; | ||
| int y = (height - backgroundHeight) / 2; | int y = (height - backgroundHeight) / 2; | ||
| - | drawTexture(matrices, x, y, 0, 0, backgroundWidth, | + | |
| } | } | ||
| @Override | @Override | ||
| - | public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { | + | public void render(DrawContext context, int mouseX, int mouseY, float delta) { |
| - | renderBackground(matrices); | + | renderBackground(context, mouseX, mouseY, delta); |
| - | super.render(matrices, mouseX, mouseY, delta); | + | super.render(context, mouseX, mouseY, delta); |
| - | drawMouseoverTooltip(matrices, mouseX, mouseY); | + | drawMouseoverTooltip(context, mouseX, mouseY); |
| } | } | ||
| Line 270: | Line 268: | ||
| protected void init() { | protected void init() { | ||
| super.init(); | super.init(); | ||
| - | // Center the title | + | // 将标题居中 |
| titleX = (backgroundWidth - textRenderer.getWidth(title)) / 2; | titleX = (backgroundWidth - textRenderer.getWidth(title)) / 2; | ||
| } | } | ||
| Line 276: | Line 274: | ||
| </ | </ | ||
| - | ===== Registering our Screen | + | ===== 注册 |
| - | + | 所有的屏幕都只是仅存于客户端的概念,因此只能在客户端进行注册。 | |
| - | As screens are a client-only concept, we can only register them on the client. | + | |
| <code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
| - | |||
| @Environment(EnvType.CLIENT) | @Environment(EnvType.CLIENT) | ||
| public class ExampleClientMod implements ClientModInitializer { | public class ExampleClientMod implements ClientModInitializer { | ||
| @Override | @Override | ||
| public void onInitializeClient() { | public void onInitializeClient() { | ||
| - | | + | |
| } | } | ||
| } | } | ||
| Line 293: | Line 289: | ||
| </ | </ | ||
| - | Don't forget to register this entrypoint in '' | + | 别忘了在 |
| - | <code json> | + | < |
| - | /* ... */ | + | { |
| + | /* ... */ | ||
| " | " | ||
| /* ... */ | /* ... */ | ||
| " | " | ||
| - | "tutorial.path.to.ExampleModClient" | + | "net.fabricmc.example.ExampleModClient" |
| ] | ] | ||
| }, | }, | ||
| + | /* ... */ | ||
| + | } | ||
| </ | </ | ||
| - | '' | + | '' |
| <code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
| public class ExampleMod implements ModInitializer { | public class ExampleMod implements ModInitializer { | ||
| [...] | [...] | ||
| - | public static final ScreenHandlerType< | + | public static final ScreenHandlerType< |
| - | [...] | + | |
| - | static { | + | |
| - | [...] | + | |
| - | //We use registerSimple here because our Entity is not an ExtendedScreenHandlerFactory | + | |
| - | //but a NamedScreenHandlerFactory. | + | |
| - | //In a later Tutorial you will see what ExtendedScreenHandlerFactory can do! | + | |
| - | BOX_SCREEN_HANDLER = ScreenHandlerRegistry.registerSimple(BOX, BoxScreenHandler:: | + | |
| - | | + | |
| @Override | @Override | ||
| public void onInitialize() { | public void onInitialize() { | ||
| + | [...] | ||
| } | } | ||
| } | } | ||
| </ | </ | ||
| - | ===== Result | + | ===== 结果 |
| - | You have now created your own container Block, you could easily change it to contain a smaller or bigger Inventory. Maybe even apply a texture :-P | + | 您现在应该创建了容器方块,可以轻易地改变它以包含更小或者更大的物品栏。也许还需要应用一个纹理!qwq |
| {{: | {{: | ||
| - | ===== Further Reading | + | ===== 延伸阅读 |
| - | - [[tutorial:extendedscreenhandler|Syncing Custom Data with Extended ScreenHandlers | + | |
| - | + | | |
| - | - [[tutorial:propertydelegates|Syncing Integers continuously with PropertyDelegates]] | + | |
| - | - An example mod using the '' | + | 使用 |
zh_cn/tutorial/screenhandler.1636386309.txt.gz · Last modified: 2021/11/08 15:45 by breakice