Rendu client Fabric
Tout le code de cette compétence est uniquement côté client : il appartient à src/client/java et est enregistré
à partir de votre ClientModInitializer. Y faire référence depuis du code commun plantera les serveurs dédiés.
Étape 0 – Quelle API de dessin cette version utilise-t-elle ?
La classe de dessin GUI et l'API HUD ont été renommées plusieurs fois. Vérifiez avant d'écrire du code :
grep -E "minecraft_version|yarn_mappings" gradle.properties
grep -n "mappings" build.gradle
| Version / mappings | Classe de dessin GUI | Méthode draw de Screen | Méthode Text | API HUD |
|---|---|---|---|---|
| 26.1+ (Mojang, non obscurci) | GuiGraphicsExtractor | extractRenderState(graphics, mouseX, mouseY, delta) | graphics.text(font, ...) | HudElementRegistry |
| 1.21.6 - 1.21.11, mappings 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 | comme ci-dessus pour vos mappings | render(...) | comme ci-dessus | HudLayerRegistrationCallback (HudRenderCallback déprécié) |
| 1.20 - 1.21.1 | comme ci-dessus pour vos mappings | render(...) | comme ci-dessus | HudRenderCallback |
Confirmez ensuite dans la dépendance réelle, car les noms continuent de bouger (26.2 a déplacé
minecraft.setScreen vers minecraft.gui.setScreen, par exemple) :
./gradlew genSources
# ouvrez HudElementRegistry / votre classe de dessin GUI dans l'IDE, ou lisez le jar des sources de Fabric API
Guides adaptés à la version avec code fonctionnel : https://docs.fabricmc.net/develop/rendering/hud (utilisez le sélecteur de version en haut de la page).
Les exemples ci-dessous utilisent les noms Mojang 26.1+. Traduisez via le tableau pour les versions plus anciennes ; la structure est identique.
Éléments 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 "hide HUD" : mc.gui.hud.isHidden() sur 26.3 ; mc.options.hideGui sur les versions plus anciennes.
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éthodes de HudElementRegistry : addFirst, addLast, attachElementBefore,
attachElementAfter (par rapport à un identifiant VanillaHudElements tel que HOTBAR, CHAT,
CROSSHAIR, BOSS_BAR, SCOREBOARD), removeElement et replaceElement (pour masquer ou
encapsuler un élément vanilla). L'API gère le calque ; vous ne gérez pas les décalages Z.
Règles pour le code HUD :
- Retournez tôt quand
mc.player == null, quand le HUD est masqué avec F1 (mc.gui.hud.isHidden()sur 26.3,mc.options.hideGuisur les versions plus anciennes – Fabric ne masque pas votre élément pour vous), et quand votre fonctionnalité est désactivée. Envisagez également de masquer l'élément pendant que l'écran de débug F3 est ouvert si votre élément le chevaucherait (recherchez l'accesseur du debug-overlay dans les sources de votre version). - Positionnez par rapport à
graphics.guiWidth()/graphics.guiHeight()(taille mise à l'échelle), jamais d'hypothèses de pixels codées en dur sur la fenêtre. - Utilisez
DeltaTracker#getGameTimeDeltaPartialTick(false)pour l'animation interpolée par tick etUtil.getMillis()pour l'animation en temps réel.
Anciens callbacks HUD (pre-1.21.6)
// 1.20 - 1.21.1 (noms Mojang affichés ; Yarn : DrawContext, RenderTickCounter)
HudRenderCallback.EVENT.register((graphics, deltaTracker) -> { /* dessiner */ });
HudLayerRegistrationCallback (1.21.2 - 1.21.5) enregistre les couches par rapport aux
identifiants IdentifiedLayer, dans le même esprit que HudElementRegistry. Consultez la page de documentation pour cette
version précise avant de l'utiliser.
Primitives de dessin
graphics.fill(x1, y1, x2, y2, 0x80000000); // ARGB ! 0x80 = 50 % d'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); // clip ; les appels doivent être équilibrés
// ... dessiner ...
graphics.disableScissor();
graphics.blit(RenderPipelines.GUI_TEXTURED, ExampleMod.id("textures/gui/icon.png"),
x, y, 0, 0, 16, 16, 16, 16); // pipeline, texture, x, y, u, v, l, h, texL, texH
graphics.blitSprite(RenderPipelines.GUI_TEXTURED, spriteId, x, y, l, h); // sprite de l'atlas GUI
graphics.centeredText(font, component, centerX, y, 0xFFFFFFFF);
graphics.setTooltipForNextFrame(font, component, mouseX, mouseY);
graphics.item(stack, x, y); // icône d'objet (nom 26.x ; plus ancien : renderItem / drawItem)
Les couleurs sont en ARGB depuis 1.21.6. 0xFFFFFF est un blanc totalement transparent et n'affiche rien.
Utilisez 0xFFFFFFFF, ou enveloppez le RGB avec ARGB.opaque(rgb). C'est le bug "mon texte ne s'affiche pas" le plus courant
lors du portage de code plus ancien.
Texte
Component title = Component.translatable("hud.examplemod.title"); // localisé
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); // dernier argument : ombre portée
- Placez les chaînes visibles par l'utilisateur dans
assets/<modid>/lang/en_us.jsonet utilisezComponent.translatable. Les chaînes anglaises codées en dur dansComponent.literalne peuvent pas être traduites. - Équivalents Yarn :
Text.translatable,Text.literal,Formatting,textRenderer.
Infobulles
Ajoutez des lignes à l'infobulle de n'importe quel objet sans mixin :
ItemTooltipCallback.EVENT.register((stack, tooltipContext, tooltipFlag, lines) -> {
if (stack.is(Items.COMPASS)) {
lines.add(Component.translatable("tooltip.examplemod.compass").withStyle(ChatFormatting.GRAY));
}
});
ItemTooltipCallback se trouve dans net.fabricmc.fabric.api.client.item.v1. Pour vos propres
objets, remplacez la méthode d'infobulle de l'objet ou utilisez un composant de données à la place. Utilisez
tooltipFlag.isAdvanced() pour limiter les informations de débogage à F3+H.
Écrans personnalisés
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() { // appelé à l'ouverture ET à chaque redimensionnement – reconstruisez les widgets ici
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); // arrière-plan + widgets
graphics.centeredText(this.font, this.title, this.width / 2, 15, 0xFFFFFFFF);
}
@Override
public void onClose() {
this.minecraft.gui.setScreen(this.parent); // 26.2+ ; avant : this.minecraft.setScreen(...)
}
}
- Créez les widgets dans
init(), pas dans le constructeur ;init()est relancé lors du redimensionnement et la liste des widgets est vidée à chaque fois. - Gardez toujours un
parentet revenez-y (Mod Menu et les écrans d'options en dépendent). - Ouvrez un écran depuis un gestionnaire de touches ou un gestionnaire de ticks sur le thread client. L'ouvrir depuis un
gestionnaire réseau ou un autre thread nécessite
client.execute(() -> ...). - Préférez les widgets vanilla (
Button,EditBox,CycleButton,AbstractSliderButton) avant d'en écrire des personnalisés.
Performances – ce code s'exécute à chaque frame
Les méthodes de dessin HUD et d'écran s'exécutent 60 à 500+ fois par seconde. Traitez-les comme une boucle chaude :
- Pas d'allocations par frame quand c'est évitable : pas de
newobjets, pas deString.format, pas de concaténation de chaînes dans le chemin de dessin, pas de streams/lambdas créés par appel, pas deComponent.literald'une valeur qui n'a pas changé. Mettez en cache leComponentet ne le reconstruisez que lorsque la valeur sous-jacente change (par exemple lors d'un événement de tick, ou lorsque la position du bloc diffère). - Pas de travail d'état du jeu dans le rendu : pas de scans du monde, de recherches d'entités, de raycasts, d'E/S de fichiers,
de lectures de configuration depuis le disque, ou de recherches dans les registres. Calculez dans
ClientTickEvents.END_CLIENT_TICK(20 fois/s) et dessinez le résultat mis en cache. - Ne faites pas de logging depuis le chemin de rendu.
- Évitez les textures uniques téléchargées à chaque frame ; enregistrez les textures une fois.
- Mesurez avec le graphique de temps par frame de F3 ou un profileur (Spark) avant et après.
// Bien : ne recalculez qu'au plus une fois par tick, seulement si modifié
public final class CoordsText {
private static Component cached = Component.empty();
private static BlockPos last = null;
public static void onTick(Minecraft mc) { // depuis 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; }
}
Rendu du monde
Le dessin dans le monde 3D (contours, boîtes, lignes) utilise LevelRenderEvents (26.1+ ; nommé
WorldRenderEvents avant) et des pipelines de rendu, et change presque à chaque version car Mojang
réécrit le moteur de rendu. Lisez https://docs.fabricmc.net/develop/rendering/world pour votre version
précise et copiez sa structure ; n'adaptez pas un extrait de rendu du monde depuis une autre version.
Erreurs courantes
- Utiliser
DrawContext/MatrixStack/TextRenderer(Yarn) dans un projet nommé Mojang, ouGuiGraphicsdans un projet 26.1+ où il s'agit deGuiGraphicsExtractor. - Surcharger
render(...)dans unScreen26.1+ – la méthode estextractRenderState; l'ancien nom compile seulement si vous oubliez aussi@Override, puis ne s'exécute jamais. Utilisez toujours@Override. - Couleurs RGB (
0xFFFFFF) après 1.21.6 -> textes et remplissages invisibles. - Utiliser
HudRenderCallbackdéprécié sur 1.21.6+, ou un mixin dans la classe vanillaGuipour un simple overlay. - Allouer des chaînes/composants à chaque frame, ou faire des requêtes monde dans le callback HUD.
- Ne pas vérifier
mc.player == null(crash sur l'écran titre) ou ignorer l'état de masquage HUD F1. - Construire des widgets dans le constructeur de
Screenau lieu deinit(). - Oublier d'appeler
super.extractRenderState(...)/super.render(...)et perdre l'arrière-plan et tous les widgets. - Chaînes anglaises codées en dur au lieu de clés de traduction.
- Placer quoi que ce soit de ceci dans
src/main.