User Tools

Site Tools


zh_cn:tutorial:screenhandler

Differences

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

Link to this comparison view

Next revision
Previous revision
zh_cn:tutorial:screenhandler [2021/11/08 15:45] – created breakicezh_cn:tutorial:screenhandler [2023/09/14 01:42] (current) – [ScreenHandler 和 Screen] wjz_p
Line 1: Line 1:
 ====== 创建一个容器方块 ====== ====== 创建一个容器方块 ======
-这篇教程会创建一个类似分解器的方块,用于解释如何使用FabricAPI中的 ''ScreenHandler'' 构建用户交互和解释原版Minecraft是如何运作的+这篇教程会创建一个类似发射器的简单存储方块,解释如何使用 Fabric 和原版 Minecraft 中的 ScreenHandler API 构建用户界面。
  
-让我们一些词汇练习+解释一些词汇:
  
 **Screenhandler:** **Screenhandler:**
-''ScreenHandler'' 是负责在客户端和服务器之间同步库存内容的类。它也可以同步额外的数值,比如熔炉烧炼进度,这将在下一个教程中展示。 +''ScreenHandler'' 是负责在客户端和服务器之间同步物品栏内容的类。它也可以同步额外的数值,比如熔炉烧炼进度,这将在下一个教程中展示。我们的子类会有以下两个构造器:一个将在服务器端使用,并将储存真正的 ''Inventory'',另外一个将会在客户端运行,用于储存 ''ItemStack'' 并且让他们能和服务端同步。
-我们的子类会有以下两个构造器: 一个将在服务器端使用,并将储存真正的 ''Inventory'',另外一个将会在客户端运行,用于储存 ''ItemStack'' 并且让他们能和服务端同步。+
  
 **Screen:** **Screen:**
-''Screen'' 类仅存在于客户端将为的 ''ScreenHandler'' 呈现背景和其他装饰。+''Screen'' 类仅存在于客户端,将为的 ''ScreenHandler'' 呈现背景和其他装饰。
  
 ===== 方块和方块实体类 ===== ===== 方块和方块实体类 =====
  
-首先我们需要创建 ''Block''对应的 ''BlockEntity''+首先我们需要创建 ''Block'' 和对应的 ''BlockEntity''
  
 <code java [enable_line_numbers="true"] BoxBlock.java> <code java [enable_line_numbers="true"] BoxBlock.java>
Line 28: Line 27:
     @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 34:
     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, pos);             NamedScreenHandlerFactory screenHandlerFactory = state.createScreenHandlerFactory(world, pos);
  
             if (screenHandlerFactory != null) {             if (screenHandlerFactory != null) {
-                //With this call the server will request the client to open the appropriate Screenhandler+                // 这个调用会让服务器请求客户端开启合适的 Screenhandler
                 player.openHandledScreen(screenHandlerFactory);                 player.openHandledScreen(screenHandlerFactory);
             }             }
Line 48: Line 47:
          
          
-    // 这个方法能让你储存在他里面的物品在被破坏时一地!+    // 这个方法能让方块破坏时物品全部掉
     @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 73:
 </code> </code>
  
-我们接下要创建 ''BlockEntity'', 他会使用 [[tutorial:inventory|Inventory Tutorial]] 中提到的''ImplementedInventory''接口.+我们接下要创建 ''BlockEntity'',并使用[[inventory|物品栏教程]]中提到的 ''ImplementedInventory'' 接口
  
 <code java [enable_line_numbers="true"] BoxBlockEntity.java> <code java [enable_line_numbers="true"] BoxBlockEntity.java>
Line 93: Line 92:
     }     }
  
-    //These Methods are from the NamedScreenHandlerFactory Interface +    // 这些方法来自 NamedScreenHandlerFactory 接口 
-    //createMenu creates the ScreenHandler itself +    // createMenu 会创建 ScreenHandler 自身 
-    //getDisplayName will Provide its name which is normally shown at the top+    // getDisplayName 会提供名称,名称通常显示在顶部
  
     @Override     @Override
     public ScreenHandler createMenu(int syncId, PlayerInventory playerInventory, PlayerEntity player) {     public ScreenHandler createMenu(int syncId, PlayerInventory playerInventory, PlayerEntity player) {
-        //We provide *thisto the screenHandler as our class Implements Inventory +        // 因为我们的类实现 Inventory,所以将*这个*提供给 ScreenHandler 
-        //Only the Server has the Inventory at the start, this will be synced to the client in the ScreenHandler+        // 一开始只有服务器拥有物品栏,然后在 ScreenHandler 中同步给客户端
         return new BoxScreenHandler(syncId, playerInventory, this);         return new BoxScreenHandler(syncId, playerInventory, this);
     }     }
Line 106: Line 105:
     @Override     @Override
     public Text getDisplayName() {     public Text getDisplayName() {
-        return new TranslatableText(getCachedState().getBlock().getTranslationKey());+        return Text.translatable(getCachedState().getBlock().getTranslationKey()); 
 +        // 对于1.19之前的版本,请使用: 
 +        // return new TranslatableText(getCachedState().getBlock().getTranslationKey());
     }     }
          
Line 125: Line 126:
 </code> </code>
  
-===== Registering Block, BlockItem and BlockEntity ===== +===== 注册方块、物品和方块实体 =====
- +
  
 <code java [enable_line_numbers="true"] ExampleMod.java> <code java [enable_line_numbers="true"] ExampleMod.java>
Line 137: Line 136:
  
     public static final String MOD_ID = "testmod";     public static final String MOD_ID = "testmod";
-    // a public identifier for multiple parts of our bigger chest+    // 我们的大型箱子中不同部分的公共id
     public static final Identifier BOX = new Identifier(MOD_ID, "box_block");     public static final Identifier BOX = new Identifier(MOD_ID, "box_block");
  
     static {     static {
-        BOX_BLOCK = Registry.register(Registry.BLOCK, BOX, new BoxBlock(FabricBlockSettings.copyOf(Blocks.CHEST))); +        BOX_BLOCK = Registry.register(Registries.BLOCK, BOX, new BoxBlock(FabricBlockSettings.copyOf(Blocks.CHEST))); 
-        BOX_BLOCK_ITEM = Registry.register(Registry.ITEM, BOX, new BlockItem(BOX_BLOCK, new Item.Settings().group(ItemGroup.MISC)));+        BOX_BLOCK_ITEM = Registry.register(Registries.ITEM, BOX, new BlockItem(BOX_BLOCK, new Item.Settings().group(ItemGroup.MISC)));
  
         //The parameter of build at the very end is always null, do not worry about it         //The parameter of build at the very end is always null, do not worry about it
-        // pre-1.17 +        // 1.17 之前 
-        BOX_BLOCK_ENTITY = Registry.register(Registry.BLOCK_ENTITY_TYPE, BOX, BlockEntityType.Builder.create(BoxBlockEntity::new, BOX_BLOCK).build(null)); +        BOX_BLOCK_ENTITY = Registry.register(Registries.BLOCK_ENTITY_TYPE, BOX, BlockEntityType.Builder.create(BoxBlockEntity::new, BOX_BLOCK).build(null)); 
-        // In 1.17 use FabricBlockEntityTypeBuilder instead of BlockEntityType.Builder +        // 在 1.17 使用 FabricBlockEntityTypeBuilder 而不是 BlockEntityType.Builder 
-        BOX_BLOCK_ENTITY = Registry.register(Registry.BLOCK_ENTITY_TYPE, BOX, FabricBlockEntityTypeBuilder.create(BoxBlockEntity::new, BOX_BLOCK).build(null));+        BOX_BLOCK_ENTITY = Registry.register(Registries.BLOCK_ENTITY_TYPE, BOX, FabricBlockEntityTypeBuilder.create(BoxBlockEntity::new, BOX_BLOCK).build(null));
     }     }
  
Line 160: Line 159:
 </code> </code>
  
-===== ScreenHandler and Screen =====+===== ScreenHandler 和 Screen =====
  
-As explained earlier, we need both a ''ScreenHandler'' and a ''HandledScreen'' to display and sync the GUI''ScreenHandler'' classes are used to synchronize GUI state between the server and the client. ''HandledScreen'' classes are fully client-sided and are responsible for drawing GUI elements.+正如前面解释的,我们同时需要 ''ScreenHandler'' 和 ''HandledScreen'' 来显示并同步 GUI''ScreenHandler'' 类用来在服务器和客户端之间同步 GUI 状态。''HandledScreen'' 类是完全客户端的,负责绘制 GUI 元素。
  
 <code java [enable_line_numbers="true"] BoxScreenHandler.java> <code java [enable_line_numbers="true"] BoxScreenHandler.java>
Line 168: Line 167:
     private final Inventory inventory;     private final Inventory inventory;
  
-    //This constructor gets called on the client when the server wants it to open the screenHandler, +    // 服务器想要客户端开启 screenHandler 时,客户端调用这个构造器。 
-    //The client will call the other constructor with an empty Inventory and the screenHandler will automatically +    // 如有空的物品栏,客户端会调用其他构造器,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, playerInventory, new SimpleInventory(9));         this(syncId, playerInventory, new SimpleInventory(9));
     }     }
  
-    //This constructor gets called from the BlockEntity on the server without calling the other constructor first, the server knows the inventory of the container +    // 这个构造器是在服务器的 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, Inventory inventory) {     public BoxScreenHandler(int syncId, PlayerInventory playerInventory, Inventory inventory) {
         super(ExampleMod.BOX_SCREEN_HANDLER, syncId);         super(ExampleMod.BOX_SCREEN_HANDLER, syncId);
         checkSize(inventory, 9);         checkSize(inventory, 9);
         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 job+        // 但是这不会渲染槽位的背景,这是 Screens 类的工作
         int m;         int m;
         int l;         int l;
Line 194: Line 193:
             }             }
         }         }
-        //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 199:
             }             }
         }         }
-        //The player Hotbar+        // 玩家快捷栏
         for (m = 0; m < 9; ++m) {         for (m = 0; m < 9; ++m) {
             this.addSlot(new Slot(playerInventory, m, 8 + m * 18, 142));             this.addSlot(new Slot(playerInventory, m, 8 + m * 18, 142));
Line 212: Line 211:
     }     }
  
-    // Shift + Player Inv Slot+    // Shift + 玩家物品栏槽位
     @Override     @Override
     public ItemStack transferSlot(PlayerEntity player, int invSlot) {     public ItemStack transferSlot(PlayerEntity player, int invSlot) {
Line 243: Line 242:
 <code java [enable_line_numbers="true"] BoxScreen.java> <code java [enable_line_numbers="true"] BoxScreen.java>
 public class BoxScreen extends HandledScreen<ScreenHandler> { public class BoxScreen extends HandledScreen<ScreenHandler> {
-    //A path to the gui texture. In this example we use the texture from the dispenser+    // GUI 纹理的路径,本例中使用发射器中的纹理
     private static final Identifier TEXTURE = new Identifier("minecraft", "textures/gui/container/dispenser.png");     private static final Identifier TEXTURE = new Identifier("minecraft", "textures/gui/container/dispenser.png");
  
Line 252: Line 251:
     @Override     @Override
     protected void drawBackground(MatrixStack matrices, float delta, int mouseX, int mouseY) {     protected void drawBackground(MatrixStack matrices, float delta, int mouseX, int mouseY) {
-        RenderSystem.setShader(GameRenderer::getPositionTexShader);+        RenderSystem.setShader(GameRenderer::getPositionTexProgram);
         RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);         RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
         RenderSystem.setShaderTexture(0, TEXTURE);         RenderSystem.setShaderTexture(0, TEXTURE);
Line 258: Line 257:
         int y = (height - backgroundHeight) / 2;         int y = (height - backgroundHeight) / 2;
         drawTexture(matrices, x, y, 0, 0, backgroundWidth, backgroundHeight);         drawTexture(matrices, x, y, 0, 0, backgroundWidth, backgroundHeight);
 +        //1.20或者以上的版本,这个方法在DrawContext类里面。
     }     }
  
Line 270: Line 270:
     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 276:
 </code> </code>
  
-===== Registering our Screen and ScreenHandler ===== +===== 注册 Screen 和 ScreenHandler =====
  
-As screens are a client-only concept, we can only register them on the client.+所有的屏幕都只是仅存于客户端的概念,因此只能在客户端进行注册。
  
 <code java [enable_line_numbers="true"] ExampleModClient.java> <code java [enable_line_numbers="true"] ExampleModClient.java>
Line 293: Line 292:
 </code> </code>
  
-Don't forget to register this entrypoint in ''fabric.mod.json'' if you haven't done it yet:+别忘了在 ''fabric.mod.json'' 中注册这个入口点,如果还没有完成的话:
 <code json> <code json>
 /* ... */ /* ... */
Line 299: Line 298:
     /* ... */     /* ... */
     "client": [     "client": [
-      "tutorial.path.to.ExampleModClient"+      "(到这个 ExampleModClient 的路径)"
     ]     ]
   },   },
 </code> </code>
  
-''ScreenHandler''s exist both on the client and on the server and therefore have to be registered on both.+''ScreenHandler'' 同时在客户端和服务器存在,因此在两者都需要注册。
  
 <code java [enable_line_numbers="true"] ExampleMod.java> <code java [enable_line_numbers="true"] ExampleMod.java>
Line 313: Line 312:
     static {     static {
         [...]         [...]
-        //We use registerSimple here because our Entity is not an ExtendedScreenHandlerFactory +        // 我们在这里使用 registerSimple 因为实体不是 ExtendedScreenHandlerFactory 
-        //but a NamedScreenHandlerFactory. +        // 而是 NamedScreenHandlerFactory. 
-        //In a later Tutorial you will see what ExtendedScreenHandlerFactory can do!+        // 后面的教程中,你将会看到 ExtendedScreenHandlerFactory 能做什么!
         BOX_SCREEN_HANDLER = ScreenHandlerRegistry.registerSimple(BOX, BoxScreenHandler::new);         BOX_SCREEN_HANDLER = ScreenHandlerRegistry.registerSimple(BOX, BoxScreenHandler::new);
     }     }
Line 326: Line 325:
 </code> </code>
  
-===== 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
  
 {{:tutorial:bildschirmfoto_vom_2020-08-14_18-32-07.png?nolink&400|}} {{:tutorial:bildschirmfoto_vom_2020-08-14_18-32-07.png?nolink&400|}}
  
-===== Further Reading ===== +===== 延伸阅读 ===== 
-- [[tutorial:extendedscreenhandler|Syncing Custom Data with Extended ScreenHandlers when screen is opened]]+- [[extendedscreenhandler|屏幕开启时,使用 Extended ScreenHandlers 同步自定义的数据]]
  
-- [[tutorial:propertydelegates|Syncing Integers continuously with PropertyDelegates]]+- [[propertydelegates|使用 PropertyDelegates 持续同步整数]]
  
-An example mod using the ''ScreenHandler'' API: [[https://github.com/FabricMC/fabric/tree/1.16/fabric-screen-handler-api-v1/src/testmod|ExampleMod on Github]].+使用 ''ScreenHandler'' 的示例模组:[[https://github.com/FabricMC/fabric/tree/1.16/fabric-screen-handler-api-v1/src/testmod|Github 上的示例模组]]
  
zh_cn/tutorial/screenhandler.1636386309.txt.gz · Last modified: 2021/11/08 15:45 by breakice