Renderização cliente Fabric
Todo o código desta skill é apenas para o cliente: deve ficar em src/client/java e ser registrado a partir do seu ClientModInitializer. Referenciá-lo no código comum (shared) causará crash em servidores dedicados.
Passo 0 – Qual API de desenho esta versão usa?
A classe de desenho da GUI e a API de HUD foram renomeadas várias vezes. Verifique antes de escrever código:
grep -E "minecraft_version|yarn_mappings" gradle.properties
grep -n "mappings" build.gradle
| Versão / mapeamentos | Classe de desenho GUI | Método de desenho da tela | Método de texto | API de HUD |
|---|---|---|---|---|
| 26.1+ (Mojang, não ofuscado) | GuiGraphicsExtractor | extractRenderState(graphics, mouseX, mouseY, delta) | graphics.text(font, ...) | HudElementRegistry |
| 1.21.6 – 1.21.11, mapeamentos 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 | como acima para seus mapeamentos | render(...) | como acima | HudLayerRegistrationCallback (HudRenderCallback obsoleto) |
| 1.20 – 1.21.1 | como acima para seus mapeamentos | render(...) | como acima | HudRenderCallback |
Em seguida, confirme na dependência real, porque os nomes continuam mudando (26.2 moveu minecraft.setScreen para minecraft.gui.setScreen, por exemplo):
./gradlew genSources
# abra HudElementRegistry / sua classe de desenho GUI na IDE, ou leia o jar de fontes da Fabric API
Guias compatíveis com versões e com código funcional: https://docs.fabricmc.net/develop/rendering/hud (use o seletor de versão no topo da página).
Os exemplos abaixo usam os nomes Mojang 26.1+. Traduza conforme a tabela para versões mais antigas; a estrutura é idêntica.
Elementos de 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() no 26.3; mc.options.hideGui em versões antigas.
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 um id de VanillaHudElements como HOTBAR, CHAT, CROSSHAIR, BOSS_BAR, SCOREBOARD), removeElement e replaceElement (para ocultar ou encapsular um elemento vanilla). A API gerencia camadas; você não precisa gerenciar eixos Z.
Regras para código de HUD:
- Retorne cedo quando
mc.player == null, quando o HUD estiver oculto com F1 (mc.gui.hud.isHidden()no 26.3,mc.options.hideGuiem versões antigas – a Fabric não oculta seu elemento automaticamente) e quando sua funcionalidade estiver desativada. Considere também ocultar enquanto a tela de depuração F3 estiver aberta se seu elemento sobrepujá-la (consulte o accessor de debug-overlay nas fontes da sua versão). - Posicione relativo a
graphics.guiWidth()/graphics.guiHeight()(tamanho escalado), nunca suposições de pixel codificadas sobre a janela. - Use
DeltaTracker#getGameTimeDeltaPartialTick(false)para animação interpolada por tick eUtil.getMillis()para animação baseada em tempo real (wall-clock).
Callbacks de HUD antigos (pré-1.21.6)
// 1.20 – 1.21.1 (nomes Mojang mostrados; Yarn: DrawContext, RenderTickCounter)
HudRenderCallback.EVENT.register((graphics, deltaTracker) -> { /* desenhar */ });
HudLayerRegistrationCallback (1.21.2 – 1.21.5) registra camadas relativas a ids de IdentifiedLayer, semelhante em espírito a HudElementRegistry. Consulte a página de documentação para essa versão exata antes de usá-la.
Primitivas de desenho
graphics.fill(x1, y1, x2, y2, 0x80000000); // ARGB! 0x80 = 50% de alpha
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; as chamadas devem ser equilibradas
// ... desenhar ...
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 do atlas da GUI
graphics.centeredText(font, component, centerX, y, 0xFFFFFFFF);
graphics.setTooltipForNextFrame(font, component, mouseX, mouseY);
graphics.item(stack, x, y); // ícone de item (nome 26.x; antigo: renderItem / drawItem)
Cores são ARGB desde 1.21.6. 0xFFFFFF é branco completamente transparente e não renderiza nada. Use 0xFFFFFFFF, ou envolva RGB com ARGB.opaque(rgb). Esse é o erro mais comum de "meu texto não aparece" ao portar código antigo.
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
- Coloque strings visíveis ao usuário em
assets/<modid>/lang/en_us.jsone useComponent.translatable. Strings em inglês codificadas emComponent.literalnão podem ser traduzidas. - Equivalentes Yarn:
Text.translatable,Text.literal,Formatting,textRenderer.
Tooltips
Adicione linhas ao tooltip de qualquer item sem um mixin:
ItemTooltipCallback.EVENT.register((stack, tooltipContext, tooltipFlag, lines) -> {
if (stack.is(Items.COMPASS)) {
lines.add(Component.translatable("tooltip.examplemod.compass").withStyle(ChatFormatting.GRAY));
}
});
ItemTooltipCallback está em net.fabricmc.fabric.api.client.item.v1. Para seus próprios itens, sobrescreva o método de tooltip do item ou use um componente de dados. Use tooltipFlag.isAdvanced() para mostrar informações de depuração atrás de F3+H.
Telas 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() { // chamado ao abrir E em todo redimensionamento – reconstrua widgets aqui
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); // fundo + widgets
graphics.centeredText(this.font, this.title, this.width / 2, 15, 0xFFFFFFFF);
}
@Override
public void onClose() {
this.minecraft.gui.setScreen(this.parent); // 26.2+; anterior: this.minecraft.setScreen(...)
}
}
- Crie widgets em
init(), não no construtor;init()é reexecutado no redimensionamento e a lista de widgets é limpa a cada vez. - Sempre mantenha um
parente retorne a ele (Mod Menu e telas de opções dependem disso). - Abra uma tela a partir de um pressionamento de tecla ou manipulador de tick na thread do cliente. Abri-la a partir de um manipulador de rede ou outra thread requer
client.execute(() -> ...). - Prefira widgets vanilla (
Button,EditBox,CycleButton,AbstractSliderButton) antes de criar os próprios.
Desempenho – este código roda a cada quadro
Os métodos de desenho de HUD e tela rodam 60–500+ vezes por segundo. Trate-os como um loop quente:
- Sem alocações por quadro sempre que possível: sem objetos
new, semString.format, sem concatenação de strings no caminho de desenho, sem streams/lambdas criados por chamada, semComponent.literalde um valor que não mudou. Armazene em cache oComponente reconstrua-o apenas quando o valor subjacente mudar (por exemplo, em um evento de tick ou quando a posição do bloco diferir). - Sem trabalho de estado do jogo no renderizador: sem varreduras do mundo, buscas por entidades, raycasts, E/S de arquivo, leitura de configuração do disco ou consultas a registries. Calcule em
ClientTickEvents.END_CLIENT_TICK(20 vezes/s) e desenhe o resultado em cache. - Não faça log a partir do caminho de renderização.
- Evite texturas únicas enviadas a cada quadro; registre texturas uma vez.
- Meça com o gráfico de tempo por quadro do F3 ou um profiler (Spark) antes e depois.
// Bom: recalcula no máximo uma vez por tick, apenas se mudou
public final class CoordsText {
private static Component cached = Component.empty();
private static BlockPos last = null;
public static void onTick(Minecraft mc) { // a partir de 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; }
}
Renderização do mundo
Desenhar no mundo 3D (contornos, caixas, linhas) usa LevelRenderEvents (26.1+; antes chamado WorldRenderEvents) e pipelines de renderização, e muda quase toda versão conforme a Mojang reescreve o renderizador. Leia https://docs.fabricmc.net/develop/rendering/world para sua versão exata e copie sua estrutura; não adapte um trecho de renderização de mundo de outra versão.
Erros comuns
- Usar
DrawContext/MatrixStack/TextRenderer(Yarn) em um projeto com nomes Mojang, ouGuiGraphicsem um projeto 26.1+ onde éGuiGraphicsExtractor. - Sobrescrever
render(...)em umaScreen26.1+ – o método éextractRenderState; o nome antigo compila apenas se você também esquecer@Override, e então nunca executa. Sempre use@Override. - Cores RGB (
0xFFFFFF) após 1.21.6 → textos e preenchimentos invisíveis. - Usar
HudRenderCallbackobsoleto no 1.21.6+, ou um mixin na classe vanillaGuipara uma sobreposição simples. - Alocar strings/componentes a cada quadro, ou fazer consultas ao mundo dentro do callback de HUD.
- Não verificar
mc.player == null(crash na tela de título) ou ignorar o estado de ocultação do HUD (F1). - Construir widgets no construtor da
Screenem vez deinit(). - Esquecer de chamar
super.extractRenderState(...)/super.render(...)e perder o fundo e todos os widgets. - Strings em inglês codificadas em vez de chaves de tradução.
- Colocar qualquer um destes em
src/main.