10-gui · git:20260711.586c86f · 2026-07-11 · sha256 053017f6e4d89a16
10-gui git:20260711.586c86fA
Immutable. This exact content is served forever at /api/v1/blob/053017f6e4d89a16.
# 10 — GUI / Screen 开发
> 适用版本:Fabric 1.14.4
---
## 约束
### 核心原则
- GUI 代码**仅在客户端**执行(`ClientModInitializer`)
- Screen 类继承 `Screen` 或其子类
- Screen 通过 `ScreenRegistry` 注册
- **禁止**在服务端引用任何 GUI 类
---
## Decision Flow
### Decision: 选择 GUI 框架
```
IF 简单的输入界面(文本框)
→ Screen + TextFieldWidget
IF 容器型 GUI(如箱子、熔炉)
→ Screen + HandledContainerScreen + ScreenHandler
IF 使用 Fabric API Screen 模块
→ ScreenApi + HandledContainerScreen
```
---
## 基本 Screen
```java
public class MyScreen extends Screen {
private final Text title;
public MyScreen(Text title) {
super(title);
this.title = title;
}
@Override
protected void init() {
// 添加按钮
addButton(new ButtonWidget(width / 2 - 50, height / 2 + 20, 100, 20,
new TextComponent("Click Me"), btn -> {
// 按钮点击逻辑
this.closeScreen();
}));
// 添加文本
drawString(this.font, "Hello Fabric!", width / 2 - 50, height / 2 - 20, 0xFFFFFF);
}
@Override
public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) {
this.renderBackground(matrices);
super.render(matrices, mouseX, mouseY, delta);
}
}
```
## 注册 Screen
```java
// 在 ClientModInitializer 中注册
public class ExampleModClient implements ClientModInitializer {
@Override
public void onInitializeClient() {
ScreenRegistry.register(
ModScreenHandlers.MY_SCREEN_HANDLER,
MyScreen::new
);
}
}
```
## ScreenHandler(服务端数据)
```java
// ScreenHandler 在服务端创建
public class MyScreenHandler extends HandledContainerScreenHandler {
private final Inventory playerInventory;
private final Inventory blockInventory;
public MyScreenHandler(int syncId, PlayerInventory playerInventory, Inventory blockInventory) {
super(new SimpleNamedContainerProvider(
(id, inv, player) -> new MyScreenHandler(id, inv, blockInventory),
new TextComponent("My Block")
), syncId);
this.playerInventory = playerInventory;
this.blockInventory = blockInventory;
// ...
}
@Override
public boolean canUse(PlayerEntity player) {
return blockInventory.canPlayerUse(player);
}
}
// 注册 ScreenHandler Type
public static final RegistryObject<ContainerType<MyScreenHandler>> MY_SCREEN_HANDLER =
Registry.register(Registry.MENU,
new Identifier(MOD_ID, "my_screen"),
new ContainerType<>(MyScreenHandler::new)
);
```
## 打开 Screen
```java
// 在服务端触发打开(通过网络包)
public void openScreen(PlayerEntity player) {
player.openContainer(new SimpleNamedContainerProvider(
(id, inv, p) -> new MyScreenHandler(id, inv, blockInventory),
new TextComponent("My Block")
));
}
```
## Fabric Screen API(fabric-screen-api-v1)
```groovy
// build.gradle
modImplementation "net.fabric.sdk:fabric-screen-api-v1:1.1.2+build.8"
```
```java
// 使用 Screen API 的 Widget
public class MyWidgetScreen extends Screen {
private SimpleWidget titleWidget;
@Override
protected void init() {
titleWidget = new SimpleWidget(width / 2, height / 2, new TextComponent("My Title"));
addChild(titleWidget);
}
}
```
## 常见错误
- ❌ 在 `onInitialize()` 中注册 `ScreenRegistry` — 服务端崩溃,应在 `ClientModInitializer`
- ❌ 在 Screen 类中直接操作服务端数据 — 应通过 `ScreenHandler` 同步
- ❌ 在 `render()` 方法中创建新对象 — 性能问题,应在 `init()` 中创建
- ❌ `ScreenHandler` 中忘记调用 `putStackInSlot` — 玩家物品栏不显示
- ❌ 在 Screen 中忘记调用 `super.render()` — 背景和子元素不渲染
## 扩展点
| 配合 Skill | 协作说明 |
|------------|---------|
| `mc-registry` | ScreenHandler 通过 Registry.register() 注册 |
| `mc-item` | 物品可以触发打开 Screen |
| `mc-block` | 方块实体可以提供 ScreenHandler |
| `mc-networking` | 网络包用于打开/关闭 Screen |