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