Fabric 用戶端渲染
此技能中的所有程式碼皆為僅限用戶端:應置於 src/client/java,並透過 ClientModInitializer 註冊。在共用程式碼中引用這些類別會導致專用伺服器當機。
步驟 0 — 此版本使用哪個繪圖 API?
GUI 繪圖類別與 HUD API 已多次重新命名。撰寫程式碼前請先確認:
grep -E "minecraft_version|yarn_mappings" gradle.properties
grep -n "mappings" build.gradle
| 版本 / 對映 | GUI 繪圖類別 | Screen 繪圖方法 | 文字方法 | HUD API |
|---|---|---|---|---|
| 26.1+ (Mojang, 未混淆) | GuiGraphicsExtractor | extractRenderState(graphics, mouseX, mouseY, delta) | graphics.text(font, ...) | HudElementRegistry |
| 1.21.6 - 1.21.11, Mojang 對映 | GuiGraphics | render(graphics, mouseX, mouseY, delta) | graphics.drawString(font, ...) | HudElementRegistry |
| 1.21.6 - 1.21.11, Yarn | DrawContext | render(context, mouseX, mouseY, delta) | context.drawText(textRenderer, ...) | HudElementRegistry |
| 1.21.2 - 1.21.5 | 依對映方式同上 | render(...) | 同上 | HudLayerRegistrationCallback (HudRenderCallback 已棄用) |
| 1.20 - 1.21.1 | 依對映方式同上 | render(...) | 同上 | HudRenderCallback |
然後在實際的依賴中再次確認,因為名稱不斷變動(例如 26.2 將 minecraft.setScreen 移至 minecraft.gui.setScreen):
./gradlew genSources
# 在 IDE 中開啟 HudElementRegistry / 你的 GUI 繪圖類別,或閱讀 Fabric API 的 sources jar
附有可運作程式碼的版本對應指南:https://docs.fabricmc.net/develop/rendering/hud (使用頁面頂端的版本選擇器)。
以下範例使用 26.1+ Mojang 名稱。對於較舊版本,請透過上表進行轉換;結構完全相同。
HUD 元素 (HudElementRegistry)
package com.example.examplemod.client;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.rendering.v1.hud.HudElementRegistry;
import net.fabricmc.fabric.api.client.rendering.v1.hud.VanillaHudElements;
import net.minecraft.client.DeltaTracker;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphicsExtractor;
import com.example.examplemod.ExampleMod;
public class ExampleModClient implements ClientModInitializer {
@Override
public void onInitializeClient() {
HudElementRegistry.attachElementAfter(
VanillaHudElements.HOTBAR, ExampleMod.id("coords"), ExampleModClient::extractCoords);
}
private static void extractCoords(GuiGraphicsExtractor graphics, DeltaTracker delta) {
Minecraft mc = Minecraft.getInstance();
// F1 "隱藏 HUD":在 26.3 為 mc.gui.hud.isHidden();較舊版本為 mc.options.hideGui。
if (mc.player == null || mc.gui.hud.isHidden() || !ExampleConfig.get().showCoords) {
return;
}
graphics.text(mc.font, CoordsText.current(mc.player), 4, 4, 0xFFFFFFFF, true);
}
}
HudElementRegistry 方法:addFirst、addLast、attachElementBefore、attachElementAfter(相對於 VanillaHudElements 的 ID,例如 HOTBAR、CHAT、CROSSHAIR、BOSS_BAR、SCOREBOARD)、removeElement 與 replaceElement(用於隱藏或包裹原始元素)。API 會處理圖層順序,無需手動管理 Z 偏移。
HUD 程式碼規則:
- 當
mc.player == null、HUD 被 F1 隱藏時(在 26.3 為mc.gui.hud.isHidden(),較舊版本為mc.options.hideGui— Fabric 不會自動為你隱藏元素),以及你的功能被停用時,請及早返回。此外,如果你的元素可能與 F3 除錯畫面重疊,也應考慮在除錯畫面開啟時隱藏(請查閱你所使用版本的原始碼中的除錯疊層存取器)。 - 相對於
graphics.guiWidth()/graphics.guiHeight()(縮放後的尺寸)進行定位,切勿對視窗大小使用硬編碼的像素假設。 - 使用
DeltaTracker#getGameTimeDeltaPartialTick(false)進行以 tick 插值的動畫,使用Util.getMillis()進行即時時鐘動畫。
舊版 HUD 回呼 (1.21.6 之前)
// 1.20 - 1.21.1 (此處顯示 Mojang 名稱;Yarn:DrawContext, RenderTickCounter)
HudRenderCallback.EVENT.register((graphics, deltaTracker) -> { /* 繪製 */ });
HudLayerRegistrationCallback (1.21.2 - 1.21.5) 會相對於 IdentifiedLayer ID 註冊圖層,其精神與 HudElementRegistry 類似。使用前請先查閱該確切版本的說明文件。
繪製基本圖形
graphics.fill(x1, y1, x2, y2, 0x80000000); // ARGB!0x80 = 50% 透明度
graphics.outline(x, y, width, height, 0xFFFF0000);
graphics.fillGradient(x1, y1, x2, y2, 0xFFFF0000, 0xFF0000FF);
graphics.horizontalLine(x1, x2, y, color);
graphics.verticalLine(x, y1, y2, color);
graphics.enableScissor(x1, y1, x2, y2); // 裁剪;呼叫必須成對
// ... 繪製 ...
graphics.disableScissor();
graphics.blit(RenderPipelines.GUI_TEXTURED, ExampleMod.id("textures/gui/icon.png"),
x, y, 0, 0, 16, 16, 16, 16); // 管線、紋理、x、y、u、v、w、h、texW、texH
graphics.blitSprite(RenderPipelines.GUI_TEXTURED, spriteId, x, y, w, h); // GUI 圖集精靈
graphics.centeredText(font, component, centerX, y, 0xFFFFFFFF);
graphics.setTooltipForNextFrame(font, component, mouseX, mouseY);
graphics.item(stack, x, y); // 物品圖示 (26.x 名稱;舊版:renderItem / drawItem)
自 1.21.6 起顏色為 ARGB。 0xFFFFFF 是完全透明的白色,不會渲染任何內容。請使用 0xFFFFFFFF,或使用 ARGB.opaque(rgb) 包裹 RGB。這是在移植舊版程式碼時最常見的「文字未顯示」錯誤原因。
文字
Component title = Component.translatable("hud.examplemod.title"); // 本地化
Component value = Component.literal(String.valueOf(count)).withStyle(ChatFormatting.GREEN);
int width = mc.font.width(title);
graphics.text(mc.font, title, x, y, 0xFFFFFFFF, true); // 最後一個參數:投影
- 將使用者可見的字串放入
assets/<modid>/lang/en_us.json,並使用Component.translatable。在Component.literal中硬編碼的英文無法被翻譯。 - Yarn 對應:
Text.translatable、Text.literal、Formatting、textRenderer。
提示框
無需使用 Mixin 即可為任何物品的提示框附加行:
ItemTooltipCallback.EVENT.register((stack, tooltipContext, tooltipFlag, lines) -> {
if (stack.is(Items.COMPASS)) {
lines.add(Component.translatable("tooltip.examplemod.compass").withStyle(ChatFormatting.GRAY));
}
});
ItemTooltipCallback 位於 net.fabricmc.fabric.api.client.item.v1。對於你自己的物品,請覆寫物品的提示框方法,或改用資料元件。使用 tooltipFlag.isAdvanced() 來將除錯資訊限制在 F3+H 啟用時顯示。
自訂螢幕
public class ExampleScreen extends Screen {
private final Screen parent;
public ExampleScreen(Screen parent) {
super(Component.translatable("screen.examplemod.title"));
this.parent = parent;
}
@Override
protected void init() { // 在開啟時和每次調整大小時呼叫 — 在此重建小部件
this.addRenderableWidget(Button.builder(Component.translatable("gui.done"), b -> this.onClose())
.bounds(this.width / 2 - 100, this.height - 28, 200, 20)
.build());
}
@Override
public void extractRenderState(GuiGraphicsExtractor graphics, int mouseX, int mouseY, float delta) {
super.extractRenderState(graphics, mouseX, mouseY, delta); // 背景 + 小部件
graphics.centeredText(this.font, this.title, this.width / 2, 15, 0xFFFFFFFF);
}
@Override
public void onClose() {
this.minecraft.gui.setScreen(this.parent); // 26.2+;較早版本:this.minecraft.setScreen(...)
}
}
- 在
init()中建立小部件,而不是在建構子中;init()會在調整大小時重新執行,且小部件清單每次都會被清空。 - 務必保留
parent並返回該螢幕(Mod Menu 和選項畫面依賴於此)。 - 從按鍵按下或用戶端 tick 處理器在用戶端執行緒上開啟螢幕。如果從網路處理器或其他執行緒開啟,則需要
client.execute(() -> ...)。 - 在編寫自訂小部件之前,優先使用原始小部件(
Button、EditBox、CycleButton、AbstractSliderButton)。
效能 — 此程式碼每影格執行
HUD 和螢幕繪製方法每秒執行 60-500+ 次。請將其視為熱循環:
- 盡量避免每影格配置記憶體:不要在繪製路徑中建立
new物件、使用String.format、字串串接、建立串流/ lambda、或對未變更的值呼叫Component.literal。快取Component,僅在底層值發生變化時(例如在 tick 事件中,或區塊位置不同時)才重建。 - 不在渲染中進行遊戲狀態工作:不要在世界中掃描、搜尋實體、進行光線投射、檔案 I/O、從磁碟讀取設定或查詢註冊表。應在
ClientTickEvents.END_CLIENT_TICK(20 次/秒)中計算並繪製快取的結果。 - 不要在渲染路徑中記錄日誌。
- 避免每影格上傳一次性紋理;應僅註冊紋理一次。
- 使用 F3 影格時間圖表或效能分析器 (Spark) 在修改前後進行測量。
// 良好實踐:最多每 tick 重新計算一次,且僅在變更時計算
public final class CoordsText {
private static Component cached = Component.empty();
private static BlockPos last = null;
public static void onTick(Minecraft mc) { // 來自 END_CLIENT_TICK
if (mc.player == null) return;
BlockPos pos = mc.player.blockPosition();
if (!pos.equals(last)) {
last = pos;
cached = Component.translatable("hud.examplemod.coords", pos.getX(), pos.getY(), pos.getZ());
}
}
public static Component current(LocalPlayer player) { return cached; }
}
世界渲染
在 3D 世界中繪製(輪廓、方塊、線條)使用 LevelRenderEvents(26.1+;先前命名為 WorldRenderEvents)和渲染管線,且幾乎每個版本都會因 Mojang 重寫渲染器而改變。請閱讀 https://docs.fabricmc.net/develop/rendering/world 以取得你的確切版本,並複製其結構;請勿從其他版本改寫世界渲染的程式碼片段。
常見錯誤
- 在 Mojang 對映的專案中使用
DrawContext/MatrixStack/TextRenderer(Yarn),或在 26.1+ 專案中使用GuiGraphics(該版本應為GuiGraphicsExtractor)。 - 在 26.1+ 的
Screen中覆寫render(...)— 該方法名為extractRenderState;舊名稱僅在你同時忘記@Override時才會編譯通過,然後永遠不會被執行。務必使用@Override。 - 在 1.21.6 之後使用 RGB 顏色 (
0xFFFFFF) → 文字和填充變為不可見。 - 在 1.21.6+ 中使用已棄用的
HudRenderCallback,或為了簡單的疊層而使用 Mixin 混入原始Gui類別。 - 每影格配置字串/元件,或在 HUD 回呼內部執行世界查詢。
- 未檢查
mc.player == null(在標題畫面會當機)或忽略 F1 隱藏 HUD 的狀態。 - 在
Screen建構子中建立小部件,而非init()。 - 忘記呼叫
super.extractRenderState(...)/super.render(...),導致背景和所有小部件遺失。 - 使用硬編碼的英文字串而非翻譯鍵。
- 將上述任何程式碼放置在
src/main中。