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
# HudElementRegistry / GUI 描画クラスを IDE で開くか、Fabric API のソース 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の場合、F1でHUDが非表示の場合(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 を使用するか、RGBを ARGB.opaque(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。
ツールチップ
ミックスインなしで任意のアイテムのツールチップに行を追加:
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 やオプション画面はこれに依存しています)。 - キー押下やティックハンドラからクライアントスレッドで画面を開きます。ネットワークハンドラや別スレッドから開く場合は
client.execute(() -> ...)が必要です。 - カスタムウィジェットを書く前に、バニラのウィジェット(
Button、EditBox、CycleButton、AbstractSliderButton)を優先的に使用します。
パフォーマンス - このコードは毎フレーム実行される
HUD と Screen の描画メソッドは 60~500 回以上/秒実行されます。ホットループとして扱ってください:
- フレームごとのアロケーションを避ける: 描画パスでの
newオブジェクト、String.format、文字列連結、ストリーム/ラムダの作成、値が変わっていないのにComponent.literalを呼ぶことを避けてください。Componentをキャッシュし、基になる値が変更された場合(例:ティックイベント時、ブロック位置が変わった場合)のみ再構築します。 - レンダリング中にゲーム状態の処理をしない: ワールドスキャン、エンティティ検索、レイキャスト、ファイルI/O、ディスクからの設定読み込み、レジストリルックアップを行わないでください。
ClientTickEvents.END_CLIENT_TICK(20回/秒)で計算し、キャッシュした結果を描画します。 - レンダーパスからのログ出力は避けてください。
- 毎フレームアップロードされる使い捨てテクスチャは避け、テクスチャは一度だけ登録します。
- F3 のフレームタイムグラフやプロファイラ(Spark)で前後を測定します。
// 良い例: せいぜい1ティックに1回、変更があった場合のみ再計算
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を使用する、またはシンプルなオーバーレイのためにバニラのGuiクラスにミックスインする。 - 毎フレーム文字列やコンポーネントをアロケーションする、またはHUDコールバック内でワールドクエリを実行する。
mc.player == nullをチェックしない(タイトル画面でクラッシュ)、または F1 の HUD 非表示状態を無視する。Screenコンストラクタでウィジェットを構築する(init()で行うべき)。super.extractRenderState(...)/super.render(...)を呼び忘れ、背景とすべてのウィジェットが失われる。- 翻訳キーの代わりに英語の文字列をハードコードする。
- これらのコードを
src/mainに配置する。