Communitygithub.com

vvs1off/fabric-client-rendering

Dibuja elementos en el cliente de Minecraft con Fabric: HUD (HudElementRegistry y callbacks antiguos), GUI (GuiGraphicsExtractor / GuiGraphics / DrawContext según la versión), texto y componentes, tooltips de objetos, pantallas y widgets personalizados, y rendimiento de renderizado. Úsalo para añadir o corregir superposiciones HUD, pantallas, tooltips o cualquier código de dibujo en el cliente por fotograma.

¿Qué es fabric-client-rendering?

Este skill proporciona una guía completa para implementar renderizado en el cliente de Minecraft utilizando Fabric API. Cubre todas las formas modernas de dibujar en pantalla: elementos HUD con HudElementRegistry (y los callbacks antiguos HudRenderCallback / HudLayerRegistrationCallback para versiones anteriores), dibujo de primitivas como rectángulos, líneas y texturas, texto traducible con Component, tooltips de objetos, y pantallas personalizadas (Screens). También incluye una sección crucial de rendimiento, explicando que el código se ejecuta 60-500 veces por segundo y cómo evitar asignaciones por fotograma, no hacer trabajo pesado en el render, y cachear resultados. Está orientado a desarrolladores que trabajan con Fabric, soportando múltiples versiones de Minecraft (1.20 hasta 26.1+) y diferentes mapeos (Mojang y Yarn), con una tabla de referencia para traducir nombres de API según la versión. El skill advierte sobre errores comunes como usar colores RGB en versiones recientes (deben ser ARGB), no verificar null en el jugador, o construir widgets en el constructor de Screen en lugar de init().

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

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Renderizado en el cliente de Fabric

Todo el código en este skill es solo para el cliente: va en src/client/java y se registra desde tu ClientModInitializer. Referenciarlo desde código común crashea servidores dedicados.

Paso 0 - ¿Qué API de dibujo tiene esta versión?

La clase de dibujo de la GUI y la API del HUD se renombraron varias veces. Verifica antes de escribir código:

grep -E "minecraft_version|yarn_mappings" gradle.properties
grep -n "mappings" build.gradle
Versión / mapeosClase de dibujo GUIMétodo de dibujo ScreenMétodo de textoAPI de HUD
26.1+ (Mojang, sin ofuscar)GuiGraphicsExtractorextractRenderState(graphics, mouseX, mouseY, delta)graphics.text(font, ...)HudElementRegistry
1.21.6 - 1.21.11, mapeos MojangGuiGraphicsrender(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.5como arriba para tus mapeosrender(...)como arribaHudLayerRegistrationCallback (HudRenderCallback obsoleto)
1.20 - 1.21.1como arriba para tus mapeosrender(...)como arribaHudRenderCallback

Luego confirma en la dependencia real, porque los nombres siguen moviéndose (26.2 movió minecraft.setScreen a minecraft.gui.setScreen, por ejemplo):

./gradlew genSources
# abre HudElementRegistry / tu clase de dibujo GUI en el IDE, o lee el jar de fuentes de Fabric API

Guías adaptadas por versión con código funcional: https://docs.fabricmc.net/develop/rendering/hud (usa el selector de versión en la parte superior de la página).

Los ejemplos a continuación usan los nombres de Mojang 26.1+. Traduce a través de la tabla para versiones más antiguas; la estructura es idéntica.

Elementos 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 "ocultar HUD": mc.gui.hud.isHidden() en 26.3; mc.options.hideGui en versiones antiguas.
        if (mc.player == null || mc.gui.hud.isHidden() || !ExampleConfig.get().showCoords) {
            return;
        }
        graphics.text(mc.font, CoordsText.current(mc.player), 4, 4, 0xFFFFFFFF, true);
    }
}

Métodos de HudElementRegistry: addFirst, addLast, attachElementBefore, attachElementAfter (relativo a un id de VanillaHudElements como HOTBAR, CHAT, CROSSHAIR, BOSS_BAR, SCOREBOARD), removeElement y replaceElement (para ocultar o envolver un elemento vanilla). La API maneja las capas; no gestionas desplazamientos z.

Reglas para el código HUD:

  • Retorna temprano cuando mc.player == null, cuando el HUD está oculto con F1 (mc.gui.hud.isHidden() en 26.3, mc.options.hideGui en versiones antiguas - Fabric no oculta tu elemento por ti), y cuando tu función está deshabilitada. También considera ocultar mientras la pantalla de depuración F3 está abierta si tu elemento se superpondría (busca el accessor de superposición de depuración en las fuentes de tu versión).
  • Posición relativa a graphics.guiWidth() / graphics.guiHeight() (tamaño escalado), nunca suposiciones fijas de píxeles sobre la ventana.
  • Usa DeltaTracker#getGameTimeDeltaPartialTick(false) para animación interpolada por tick y Util.getMillis() para animación en tiempo real.

Callbacks HUD antiguos (pre-1.21.6)

// 1.20 - 1.21.1 (nombres Mojang mostrados; Yarn: DrawContext, RenderTickCounter)
HudRenderCallback.EVENT.register((graphics, deltaTracker) -> { /* dibujar */ });

HudLayerRegistrationCallback (1.21.2 - 1.21.5) registra capas relativas a ids de IdentifiedLayer, similar en espíritu a HudElementRegistry. Consulta la página de documentación para esa versión exacta antes de usarlo.

Primitivas de dibujo

graphics.fill(x1, y1, x2, y2, 0x80000000);                     // ¡ARGB! 0x80 = 50% alfa
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);   // recortar; las llamadas deben estar equilibradas
// ... dibujar ...
graphics.disableScissor();

graphics.blit(RenderPipelines.GUI_TEXTURED, ExampleMod.id("textures/gui/icon.png"),
              x, y, 0, 0, 16, 16, 16, 16);   // pipeline, textura, x, y, u, v, w, h, texW, texH
graphics.blitSprite(RenderPipelines.GUI_TEXTURED, spriteId, x, y, w, h);   // sprite del atlas de la GUI
graphics.centeredText(font, component, centerX, y, 0xFFFFFFFF);
graphics.setTooltipForNextFrame(font, component, mouseX, mouseY);
graphics.item(stack, x, y);               // icono de objeto (nombre 26.x; más antiguo: renderItem / drawItem)

Los colores son ARGB desde 1.21.6. 0xFFFFFF es blanco completamente transparente y no renderiza nada. Usa 0xFFFFFFFF, o envuelve RGB con ARGB.opaque(rgb). Este es el error más común de "mi texto no se muestra" al portar código antiguo.

Texto

Component title = Component.translatable("hud.examplemod.title");        // localizado
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);   // último argumento: sombra
  • Pon las cadenas visibles para el usuario en assets/<modid>/lang/en_us.json y usa Component.translatable. Las cadenas en inglés hardcodeadas en Component.literal no pueden traducirse.
  • Equivalentes en Yarn: Text.translatable, Text.literal, Formatting, textRenderer.

Tooltips

Añade líneas al tooltip de cualquier objeto sin un mixin:

ItemTooltipCallback.EVENT.register((stack, tooltipContext, tooltipFlag, lines) -> {
    if (stack.is(Items.COMPASS)) {
        lines.add(Component.translatable("tooltip.examplemod.compass").withStyle(ChatFormatting.GRAY));
    }
});

ItemTooltipCallback vive en net.fabricmc.fabric.api.client.item.v1. Para tus propios objetos, sobrescribe el método tooltip del objeto o usa un componente de datos. Usa tooltipFlag.isAdvanced() para mostrar información de depuración detrás de F3+H.

Pantallas personalizadas

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() {   // se llama al abrir Y en cada redimensionamiento - reconstruye widgets aquí
        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);   // fondo + widgets
        graphics.centeredText(this.font, this.title, this.width / 2, 15, 0xFFFFFFFF);
    }

    @Override
    public void onClose() {
        this.minecraft.gui.setScreen(this.parent);   // 26.2+; más antiguo: this.minecraft.setScreen(...)
    }
}
  • Crea widgets en init(), no en el constructor; init() se vuelve a ejecutar al redimensionar y la lista de widgets se limpia cada vez.
  • Siempre guarda un parent y vuelve a él (Mod Menu y las pantallas de opciones dependen de esto).
  • Abre una pantalla desde un manejador de teclas o tick en el hilo del cliente. Abrirla desde un manejador de red u otro hilo necesita client.execute(() -> ...).
  • Prefiere widgets vanilla (Button, EditBox, CycleButton, AbstractSliderButton) antes de escribir personalizados.

Rendimiento - este código se ejecuta cada fotograma

Los métodos de dibujo de HUD y pantalla se ejecutan 60-500+ veces por segundo. Trátalos como un bucle caliente:

  • Sin asignaciones por fotograma donde sea evitable: sin objetos new, sin String.format, sin concatenación de cadenas en la ruta de dibujo, sin streams/lambdas creados por llamada, sin Component.literal de un valor que no ha cambiado. Almacena en caché el Component y reconstrúyelo solo cuando el valor subyacente cambie (por ejemplo, en un evento de tick, o cuando la posición del bloque difiera).
  • Sin trabajo de estado del juego en el render: sin escaneos del mundo, búsquedas de entidades, raycasts, E/S de archivos, lecturas de configuración desde el disco, o búsquedas en registros. Calcula en ClientTickEvents.END_CLIENT_TICK (20 veces/s) y dibuja el resultado almacenado en caché.
  • No hagas logs desde la ruta de renderizado.
  • Evita texturas únicas subidas cada fotograma; registra las texturas una vez.
  • Mide con el gráfico de tiempo por fotograma de F3 o un perfilador (Spark) antes y después.
// Bueno: recalcular como máximo una vez por tick, solo si cambió
public final class CoordsText {
    private static Component cached = Component.empty();
    private static BlockPos last = null;

    public static void onTick(Minecraft mc) {             // desde 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; }
}

Renderizado del mundo

Dibujar en el mundo 3D (contornos, cajas, líneas) usa LevelRenderEvents (26.1+; antes llamado WorldRenderEvents) y pipelines de renderizado, y cambia casi cada versión mientras Mojang reescribe el motor de renderizado. Lee https://docs.fabricmc.net/develop/rendering/world para tu versión exacta y copia su estructura; no adaptes un fragmento de renderizado del mundo de otra versión.

Errores comunes

  • Usar DrawContext/MatrixStack/TextRenderer (Yarn) en un proyecto con nombres Mojang, o GuiGraphics en un proyecto 26.1+ donde es GuiGraphicsExtractor.
  • Sobrescribir render(...) en un Screen 26.1+ - el método es extractRenderState; el nombre antiguo compila solo si también olvidas @Override, y entonces nunca se ejecuta. Siempre usa @Override.
  • Colores RGB (0xFFFFFF) después de 1.21.6 -> texto y rellenos invisibles.
  • Usar HudRenderCallback obsoleto en 1.21.6+, o un mixin en la clase Gui vanilla para una superposición simple.
  • Asignar cadenas/componentes cada fotograma, o hacer consultas al mundo dentro del callback del HUD.
  • No verificar mc.player == null (crash en la pantalla de título) o ignorar el estado de ocultación del HUD con F1.
  • Construir widgets en el constructor de Screen en lugar de init().
  • Olvidar llamar a super.extractRenderState(...) / super.render(...) y perder el fondo y todos los widgets.
  • Cadenas en inglés hardcodeadas en lugar de claves de traducción.
  • Poner cualquiera de esto en src/main.

Individual skills in this repo

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

Skills relacionados