Communitygithub.com

vvs1off/fabric-client-rendering

Fabricクライアントでの描画に関するスキル - HUD要素(HudElementRegistryや旧HUDコールバック)、GUI描画(GuiGraphicsExtractor / GuiGraphics / DrawContext)、テキストとコンポーネント、アイテムツールチップ、カスタムスクリーンとウィジェット、レンダリングパフォーマンスをカバーします。HUDオーバーレイ、スクリーン、ツールチップ、またはフレームごとのクライアント描画コードの追加・修正時に使用します。

fabric-client-rendering とは?

このスキルは、Fabric Mod 開発におけるクライアントサイドの描画タスクを包括的にサポートします。HUD要素の追加(HudElementRegistry および旧HudRenderCallback)、カスタムスクリーンやウィジェットの作成、アイテムツールチップの拡張、そしてフレームごとの描画コードのパフォーマンス最適化について、具体的なコード例とともに解説します。特に、Minecraftのバージョンによって変化する描画API(GuiGraphicsExtractor, GuiGraphics, DrawContext)の違いを明確なテーブルで示し、バージョン間の移植を容易にします。 本スキルは、Claude Code、Cursor、Codex などのコード生成・編集エージェントが、ユーザーの「HUDに座標を表示したい」「カスタム設定画面を作りたい」といった要求に対して、バージョン互換性を考慮した正しいコードを生成するために設計されています。描画コードは毎フレーム実行されるホットパスであることを強調し、アロケーション削減やゲーム状態の更新分離など、パフォーマンスに関する重要なプラクティスも網羅しています。 Fabric API が提供する公式の描画APIに基づいており、Mixinによるバニラクラスの書き換えを推奨しない、モダンな開発手法を取り入れています。また、コードはすべて `src/client/java` に配置し、ClientModInitializer から登録する原則も徹底しています。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/vvs1off/fabric-skills/tree/main/skills/fabric-client-rendering

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

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, 難読化解除)GuiGraphicsExtractorextractRenderState(graphics, mouseX, mouseY, delta)graphics.text(font, ...)HudElementRegistry
1.21.6 - 1.21.11, Mojang マッピングGuiGraphicsrender(graphics, mouseX, mouseY, delta)graphics.drawString(font, ...)HudElementRegistry
1.21.6 - 1.21.11, YarnDrawContextrender(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 に配置する。

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

関連スキル