Overlay HUD
Affiche des informations en permanence sur le HUD du joueur — barres de vie, stats de faction, effets visuels — sans bloquer le jeu.
Qu'est-ce qu'un overlay ?
Un overlay est un element graphique affiche en permanence sur l'ecran du joueur,
par-dessus le jeu et le HUD vanilla de Minecraft. Contrairement a un GUI (EriGuiScreen)
qui prend le controle de l'ecran, un overlay reste affiché en permanence pendant que le joueur joue.
Cas d'usage typiques :
- Barres de ressources — mana, endurance, energie, chaleur
- HUD de faction — nom de faction, grade, points de territoire
- Indicateurs de statut — effets actifs, buffs/debuffs
- Minimap ou boussole personnalisee
- Effets decoratifs — particules, aurora, fumee ambiante
Overlay vs GUI — quelle difference ?
| Caracteristique | EriGuiScreen (GUI) | EriOverlay (HUD) |
|---|---|---|
| Ouverture | Sur action du joueur (touche, commande) | Permanent (enregistre une fois) |
| Bloque le jeu | Oui — le joueur ne peut pas bouger | Non — le joueur joue normalement |
| Interactions | Clavier, souris, clics | Aucune (rendu uniquement) |
| Coordonnees | 1920x1080 design pixels | 1920x1080 design pixels (identique) |
| Composants | Tous les composants EriAPI | Composants visuels seulement (pas d'input) |
| Cycle de vie | Ouvert / ferme | Register / unregister + show / hide |
Les overlays utilisent exactement les memes composants, le meme systeme de coordonnees
1920x1080 et les memes animations qu'un GUI classique. Si tu sais creer un EriGuiScreen,
tu sais deja creer un overlay.
EriOverlay
EriOverlay est la classe de base abstraite pour tous les overlays HUD.
Tu crees une sous-classe, tu definis les composants dans buildOverlay(),
et tu l'enregistres dans OverlayManager.
Creation d'un overlay
import fr.eri.eriapi.overlay.*;
import fr.eri.eriapi.gui.components.*;
public class HealthOverlay extends EriOverlay {
public HealthOverlay() {
// id unique, largeur et hauteur en design pixels
super("health_hud", 200, 60);
setAnchor(Anchor.BOTTOM_LEFT); // coin inferieur gauche
setOffset(10, -10); // decalage depuis l'ancrage
}
@Override
protected void buildOverlay() {
getRoot().add(
new Rectangle(0, 0, 200, 60)
.fillColor(0x80000000)
.cornerRadius(8),
new Label(10, 20, 180, 20, "HP : 20")
.color(0xFFFF5555)
);
}
}
// Enregistrement (une fois, dans ClientProxy.init() par exemple) :
OverlayManager.getInstance().register(new HealthOverlay());
Cycle de vie
La methode buildOverlay() est appelee une seule fois au premier rendu
(lazy initialization). Les appels suivants a render() reutilisent les composants deja
construits. Si tu veux forcer une reconstruction (donnees qui changent), appelle rebuild().
-
ConstructeurEriOverlay(String id, int designWidth, int designHeight)Constructeur.
idest l'identifiant unique utilise parOverlayManager.designWidthetdesignHeightdefinissent la zone de l'overlay en pixels de design (1920x1080). -
A implementervoid buildOverlay()Methode abstraite a implementer. Appelle
getRoot().add(...)pour ajouter des composants. Appelee une seule fois au premier rendu. -
GetterContainerComponent getRoot()Retourne le conteneur racine. A utiliser depuis
buildOverlay()pour ajouter des composants. -
Methodevoid rebuild()Force la reconstruction de l'overlay au prochain rendu. Utile pour rafraichir l'affichage apres un changement de donnees important.
-
FluentEriOverlay setAnchor(Anchor anchor)Definit le point d'ancrage de l'overlay sur l'ecran. Voir la section Anchor. Defaut :
TOP_LEFT. -
FluentEriOverlay setOffset(int offsetX, int offsetY)Decalage en pixels de design depuis l'ancrage. Positif = bas/droite, negatif = haut/gauche.
-
FluentEriOverlay setLayer(OverlayLayer layer)Definit le layer de rendu (moment dans le pipeline HUD). Defaut :
POST_ALL. -
FluentEriOverlay hideInGui(boolean hide)Si
true(defaut), l'overlay disparait quand un GUI est ouvert. Mettre afalsepour toujours afficher l'overlay meme dans les menus. -
FluentEriOverlay hideOnF1(boolean hide)Si
true(defaut), l'overlay disparait quand le joueur appuie sur F1 (masquer le HUD). Mettre afalsepour ignorer F1. -
Methodevoid show() / hide() / toggle()Controle direct de la visibilite de cet overlay. Equivalent a
OverlayManager.getInstance().show("id").
OverlayManager
OverlayManager est le singleton qui centralise tous les overlays enregistres.
Il gere automatiquement le rendu et les ticks via les events Forge — tu n'as rien a configurer
au-dela de l'enregistrement initial.
import fr.eri.eriapi.overlay.OverlayManager;
OverlayManager om = OverlayManager.getInstance();
// Enregistrer des overlays (une fois, en init)
om.register(new HealthOverlay());
om.register(new ManaOverlay());
om.register(new FactionHudOverlay());
// Controle de la visibilite par ID
om.show("health_hud");
om.hide("health_hud");
om.toggle("health_hud");
// Controle global
om.hideAll();
om.showAll();
// Recuperer un overlay pour le modifier
EriOverlay overlay = om.get("health_hud");
if (overlay != null) {
overlay.rebuild(); // Forcer une reconstruction
}
// Desenregistrer un overlay
om.unregister("health_hud");
-
Statiquestatic OverlayManager getInstance()Retourne l'instance unique du gestionnaire.
-
Methodevoid register(EriOverlay overlay)Enregistre un overlay. Si un overlay avec le meme ID existe deja, il est remplace. L'ordre d'enregistrement determine l'ordre de rendu.
-
Methodevoid unregister(String id)Supprime un overlay enregistre. Sans effet si l'ID est inconnu.
-
GetterEriOverlay get(String id)Retourne l'overlay correspondant a l'ID, ou
nulls'il n'existe pas. -
Methodevoid show(String id) / hide(String id) / toggle(String id)Controle la visibilite d'un overlay par son ID. Sans effet si l'ID est inconnu.
-
Methodevoid showAll() / hideAll()Affiche ou masque tous les overlays enregistres.
Enregistre tes overlays dans ClientProxy.init() (ou postInit() si
tu as besoin que les items/blocs soient charges en premier). Ne les enregistre pas dans un
constructeur de GUI ou dans un event qui se declenche souvent — le register est fait une seule fois.
Anchor
L'enum Anchor definit les neuf points d'ancrage possibles pour un overlay.
L'ancrage est le coin ou le bord de l'ecran depuis lequel l'overlay est positionne.
Combine avec setOffset(x, y) pour affiner le positionnement.
| Valeur | Position sur l'ecran | Cas d'usage typique |
|---|---|---|
TOP_LEFT |
Coin superieur gauche (0, 0) | Minimap, logo de mod |
TOP_CENTER |
Centre du bord superieur | Titre de zone, boss bar |
TOP_RIGHT |
Coin superieur droit | Horloge, coordonnees, ping |
CENTER_LEFT |
Centre du bord gauche | Barre de statut laterale |
CENTER |
Centre absolu de l'ecran | Reticule custom, alerte centrale |
CENTER_RIGHT |
Centre du bord droit | Liste de buffs/debuffs |
BOTTOM_LEFT |
Coin inferieur gauche | Barre de mana/endurance |
BOTTOM_CENTER |
Centre du bord inferieur | Barre d'action, raccourcis |
BOTTOM_RIGHT |
Coin inferieur droit | Stats de faction, nom de faction |
// Overlay en bas a droite avec 15px de marge
new MonOverlay()
.setAnchor(Anchor.BOTTOM_RIGHT)
.setOffset(-15, -15); // negatif = vers le haut/gauche
// Overlay centre en haut avec 20px de marge du bord
new BossBarOverlay()
.setAnchor(Anchor.TOP_CENTER)
.setOffset(0, 20); // positif = vers le bas
Les offsets sont en pixels de design (espace 1920x1080), comme pour les composants GUI.
EriAPI les convertit automatiquement vers la resolution reelle du joueur. Un offset de
(-15, -15) representera la meme marge visuelle sur un ecran 1080p ou 4K.
OverlayLayer
OverlayLayer definit a quel moment dans le pipeline de rendu HUD
de Minecraft ton overlay est dessine. Cela determine ce qui est affiche par-dessus ou par-dessous.
| Valeur | Moment du rendu | Consequence |
|---|---|---|
PRE_ALL |
Avant tous les elements HUD vanilla | Ton overlay est affiche sous la barre de vie, l'inventaire rapide, etc. |
POST_HOTBAR |
Apres la barre d'items (hotbar) | S'affiche par-dessus la hotbar mais sous la barre de vie |
POST_EXPERIENCE |
Apres la barre d'experience | S'affiche apres l'XP bar, avant les autres elements |
POST_ALL |
Apres tous les elements HUD vanilla (defaut) | Ton overlay est affiche par-dessus tout le HUD vanilla |
// Afficher l'overlay par-dessus tout le HUD (comportement par defaut)
overlay.setLayer(OverlayLayer.POST_ALL);
// Afficher juste apres la hotbar
overlay.setLayer(OverlayLayer.POST_HOTBAR);
// Fond ambiant sous tout le HUD
overlay.setLayer(OverlayLayer.PRE_ALL);
Composants utilisables dans les overlays
Les overlays reutilisent exactement les memes composants que les GUIs. La seule difference : les composants interactifs (TextField, ScrollList, TabView, etc.) n'ont aucune utilite dans un overlay car il n'y a pas d'interaction clavier/souris avec le HUD.
Formes et fonds
Rectangle uni avec option coins arrondis et bordure. Parfait pour les fonds de panneau HUD.
new Rectangle(0, 0, 200, 60)
.fillColor(0x80000000) // fond semi-transparent
.borderColor(0x40FFFFFF) // bordure subtile
.cornerRadius(8);
Rectangle avec degrade lineaire (vertical ou horizontal). Ideal pour les barres d'ambiance ou les fonds degrades.
// Degrade vertical violet → bleu
new GradientRectangle()
.originalPos(0, 0).originalSize(200, 60)
.vertical(0xFF6B2FA0, 0xFF00E5FF)
.cornerRadius(8);
// Degrade horizontal
new GradientRectangle()
.originalPos(0, 0).originalSize(300, 8)
.horizontal(0xFF00E5FF, 0x00000000); // fondu vers transparent
Cercle plein ou triangle (indicateur directionnel). Utiles pour des icones ou fleches de navigation.
Texte
Affichage de texte avec scale, alignement et ombre. Le composant texte de base pour tous les HUD.
new Label(10, 10, 180, 20, "Faction : Erinien")
.color(0xFFFFD700)
.scale(1.2f)
.shadow(true);
Controles de progression
Barre de progression avec fill anime, couleurs et coins arrondis. Le composant ideal pour afficher des ressources (mana, vie, exp).
ProgressBar manaBar = new ProgressBar(0, 0, 180, 14)
.progress(0.75f) // 75% de mana
.fillColor(0xFF6B2FA0) // couleur du remplissage
.backgroundColor(0x40000000) // fond
.cornerRadius(7)
.showText(false); // pas de texte en overlay
Conteneurs
Conteneur pour imbriquer des composants. Permet de grouper des elements et d'appliquer
une opacity ou des animations collectives. La methode getRoot() de EriOverlay
retourne deja un ContainerComponent racine — utilise des sous-conteneurs pour
organiser ton HUD en sections logiques.
Effets visuels decoratifs
Ces composants sont particulierement adaptes aux overlays : ils s'animent en continu,
consomment peu de ressources et apportent une ambiance visuelle sans necessiter de
configuration de l'AnimationManager.
Bandes d'aurore boreale ondulantes avec degrade vertical. Jusqu'a 10 bandes superposables,
chacune avec sa propre couleur, frequence, amplitude et vitesse. Anime en continu via
System.currentTimeMillis() — pas besoin d'animation EriAPI.
new Aurora()
.originalPos(0, 0).originalSize(1920, 400)
.addBand(0x406B2FA0, 0.3f, 1.2f, 80, 50) // violet
.addBand(0x3000E5FF, 0.5f, 0.8f, 60, 120) // cyan
.addBand(0x20FF6B6B, 0.7f, 0.6f, 50, 200) // rose subtil
.speed(0.02f);
// Parametres addBand : couleur, frequence, amplitude, hauteur, offsetY
Champ d'etoiles avec scintillement sinusoidal et etoiles filantes aleatoires. Jusqu'a 3 etoiles filantes simultanees.
new Starfield()
.originalPos(0, 0).originalSize(600, 400)
.starCount(80)
.starColor(0xFFF0F2FF)
.shootingStarChance(0.003f); // 0 = jamais, 0.01 = frequent
Systeme de particules soft (cercles avec gradient radial) qui naissent, derivent et s'estompent. Blending additif pour un effet lumineux. Parfait pour une ambiance magique.
new ParticleSystem()
.originalPos(0, 0).originalSize(300, 100)
.maxParticles(20)
.spawnRate(0.3f)
.particleLife(2000, 4000)
.particleSize(20, 60)
.particleColor(0x306B2FA0)
.drift(0, -0.008f) // monte doucement
.spread(0.01f)
.fadeIn(0.2f).fadeOut(0.4f);
Brouillard organique base sur du bruit Simplex fractal (FBM). Anime en continu, le brouillard coule et se deforme naturellement. Tres subtil a faible intensite.
new SmokeFog()
.originalPos(0, 0).originalSize(400, 200)
.color(0x6B2FA0) // couleur RGB
.intensity(0.08f) // alpha max (tres subtil)
.scale(0.006f) // taille des volutes
.speed(0.0003f)
.octaves(3)
.cellSize(12);
Animations et scope "overlay"
Toutes les animations EriAPI fonctionnent dans les overlays. Cependant, il y a une subtilite
importante : quand un GUI est ferme, EriGuiScreen.onGuiClosed() appelle
AnimationManager.getInstance().stopGuiAnimations() — cela arrete toutes les
animations qui n'ont pas de scope.
Pour proteger les animations d'overlay contre cet arret, utilise .scope("overlay") :
public class ManaOverlay extends EriOverlay {
private Label manaLabel;
private ProgressBar manaBar;
public ManaOverlay() {
super("mana_hud", 200, 40);
setAnchor(Anchor.BOTTOM_LEFT);
setOffset(10, -70); // au-dessus de la barre de vie vanilla
}
@Override
protected void buildOverlay() {
manaLabel = new Label(10, 5, 100, 15, "Mana")
.color(0xFF9B59B6);
manaBar = new ProgressBar(10, 22, 180, 10)
.progress(1f)
.fillColor(0xFF6B2FA0)
.backgroundColor(0x40000000)
.cornerRadius(5);
getRoot().add(manaLabel, manaBar);
// Animation respirante — scope "overlay" pour ne pas etre arretee
// quand le joueur ouvre/ferme un GUI
Animation anim = Animation.create(0.6f, 1f, 30)
.pingPong()
.scope("overlay")
.easing(Easing.EASE_IN_OUT)
.onUpdate(v -> manaLabel.setOpacity(v));
AnimationManager.getInstance().play(anim);
}
// Methode publique pour mettre a jour la valeur de mana
public void setMana(float ratio) {
if (manaBar != null) {
manaBar.progress(ratio);
}
}
}
Sans ce scope, tes animations pingPong() ou loop() seront
interrompues chaque fois que le joueur ferme un GUI (inventaire, etc.). Avec
.scope("overlay"), elles continuent de tourner independamment.
Helpers d'animation sur les composants
Les methodes pré-faites de Component (fadeIn, breathe, pulse, etc.)
fonctionnent aussi dans les overlays, mais elles ne definissent pas de scope automatiquement.
Pour les animations en boucle dans un overlay, cree l'animation manuellement avec .scope("overlay").
// ❌ Sera arrete quand le joueur ferme un GUI
label.breathe(40); // helper interne, pas de scope
// ✅ Continue meme quand un GUI est ouvert puis ferme
Animation.create(0.4f, 1f, 20)
.pingPong()
.scope("overlay")
.easing(Easing.EASE_IN_OUT)
.onUpdate(v -> label.setOpacity(v));
AnimationManager.getInstance().play(breatheAnim);
Exemple 1 — Barre de mana
Un overlay simple avec une barre de progression et un label, ancre en bas a gauche. La barre peut etre mise a jour depuis n'importe ou dans le code.
import fr.eri.eriapi.overlay.*;
import fr.eri.eriapi.gui.components.*;
public class ManaOverlay extends EriOverlay {
private ProgressBar manaBar;
private Label manaLabel;
public ManaOverlay() {
super("mana_overlay", 210, 50);
setAnchor(Anchor.BOTTOM_LEFT);
setOffset(10, -80); // juste au-dessus de la barre de vie
hideInGui(true); // masquer si GUI ouvert
hideOnF1(true); // masquer si F1
}
@Override
protected void buildOverlay() {
// Fond
getRoot().add(new Rectangle(0, 0, 210, 50)
.fillColor(0x90000000)
.cornerRadius(6));
// Label "Mana"
manaLabel = new Label(8, 6, 60, 12, "MANA")
.color(0xFFBB86FC)
.scale(0.8f)
.shadow(true);
// Barre de mana
manaBar = new ProgressBar(8, 24, 194, 12)
.progress(1f)
.fillColor(0xFF6B2FA0)
.backgroundColor(0x50000000)
.cornerRadius(6);
// Label valeur
Label valueLabel = new Label(8, 38, 194, 10, "100 / 100")
.color(0x80FFFFFF)
.scale(0.75f)
.align(Label.Align.CENTER);
getRoot().add(manaLabel, manaBar, valueLabel);
}
/** Appele depuis ton systeme de mana pour mettre a jour la barre. */
public void setMana(int current, int max) {
if (manaBar != null) {
manaBar.progress((float) current / max);
}
}
}
// Dans ClientProxy.init() :
ManaOverlay manaOverlay = new ManaOverlay();
OverlayManager.getInstance().register(manaOverlay);
// Depuis n'importe ou dans ton mod (cote client) :
ManaOverlay overlay = (ManaOverlay) OverlayManager.getInstance().get("mana_overlay");
if (overlay != null) overlay.setMana(75, 100);
Exemple 2 — HUD de faction
Un HUD de faction dans le coin inferieur droit avec le nom de la faction, le grade du joueur et le nombre de membres en ligne.
import fr.eri.eriapi.overlay.*;
import fr.eri.eriapi.gui.components.*;
public class FactionHudOverlay extends EriOverlay {
private Label factionNameLabel;
private Label rankLabel;
private Label membersLabel;
public FactionHudOverlay() {
super("faction_hud", 220, 70);
setAnchor(Anchor.BOTTOM_RIGHT);
setOffset(-10, -10);
}
@Override
protected void buildOverlay() {
// Fond avec degrade
getRoot().add(new GradientRectangle()
.originalPos(0, 0).originalSize(220, 70)
.vertical(0x80150A2A, 0x801A0B35)
.cornerRadius(8));
// Bordure superieure
getRoot().add(new GradientRectangle()
.originalPos(0, 0).originalSize(220, 2)
.horizontal(0x00000000, 0xFF6B2FA0)
.cornerRadius(0));
// Icone / prefixe
getRoot().add(new Label(8, 10, 20, 16, "\u2726")
.color(0xFFFFD700)
.scale(1.0f));
// Nom de la faction
factionNameLabel = new Label(28, 8, 180, 18, "Aucune faction")
.color(0xFFE0E0E0)
.scale(1.0f)
.shadow(true);
// Grade
rankLabel = new Label(8, 30, 204, 14, "Grade : ---")
.color(0xFF9B8BCC)
.scale(0.85f);
// Membres en ligne
membersLabel = new Label(8, 48, 204, 14, "Membres en ligne : 0")
.color(0xFF6BCB77)
.scale(0.85f);
getRoot().add(factionNameLabel, rankLabel, membersLabel);
}
public void update(String faction, String rank, int onlineMembers) {
if (factionNameLabel != null) factionNameLabel.text(faction);
if (rankLabel != null) rankLabel.text("Grade : " + rank);
if (membersLabel != null) membersLabel.text("Membres en ligne : " + onlineMembers);
}
}
Exemple 3 — Overlay avec effets visuels animes
Un overlay ambiante avec un champ d'etoiles en fond et une aurora, affiche en haut de l'ecran. Ce type d'overlay est utilise pour des effets decoratifs permanents.
import fr.eri.eriapi.overlay.*;
import fr.eri.eriapi.gui.components.*;
import fr.eri.eriapi.gui.anim.*;
public class AmbientOverlay extends EriOverlay {
public AmbientOverlay() {
super("ambient_overlay", 1920, 300);
setAnchor(Anchor.TOP_LEFT);
setLayer(OverlayLayer.PRE_ALL); // sous le HUD vanilla
hideInGui(false); // reste visible meme dans les menus
hideOnF1(true);
}
@Override
protected void buildOverlay() {
// Champ d'etoiles en fond
getRoot().add(new Starfield()
.originalPos(0, 0).originalSize(1920, 300)
.starCount(60)
.starColor(0xFFF0F2FF)
.shootingStarChance(0.002f));
// Aurora au-dessus
getRoot().add(new Aurora()
.originalPos(0, 0).originalSize(1920, 300)
.addBand(0x306B2FA0, 0.4f, 1.0f, 100, 80)
.addBand(0x2000E5FF, 0.6f, 0.7f, 80, 160)
.speed(0.015f));
}
}
Notes techniques
Scope des animations
Quand un GUI est ferme (EriGuiScreen.onGuiClosed()), le framework appelle
AnimationManager.getInstance().stopGuiAnimations(). Cette methode arrete
toutes les animations sans scope (scope == null).
Les animations avec .scope("overlay") (ou tout autre scope non nul) sont preservees.
Regle a retenir : dans un overlay, toute animation en boucle (loop(),
pingPong()) doit avoir .scope("overlay").
Les animations a usage unique (fondu d'apparition, slide) n'ont pas besoin de scope.
hideInGui
Par defaut, hideInGui(true) masque automatiquement l'overlay quand
Minecraft.getMinecraft().currentScreen != null (un GUI est ouvert).
Mettre a false si ton overlay doit rester visible dans les menus
(effet decoratif, information persistante).
hideOnF1
Par defaut, hideOnF1(true) masque l'overlay quand
mc.gameSettings.hideGUI == true (joueur appuie sur F1 pour cacher le HUD).
Mettre a false uniquement pour les overlays qui ont une raison forte de
toujours s'afficher (debug info, etc.).
Thread safety
Les overlays sont entierement rendus sur le thread de rendu client. Ne modifie pas
les composants depuis un thread async (EriScheduler.async ou autre). Utilise
EriScheduler.delay(0, () -> overlay.setMana(...)) pour rappliquer sur
le thread principal si necessaire.
Performance
Les overlays sont rendus a chaque frame (60 FPS+). Evite les calculs lourds dans
buildOverlay() ou dans les lambdas onUpdate. Les composants
decoratifs (SmokeFog, ParticleSystem) ont des limites integrees
(cellSize, maxParticles) pour controler le cout GPU.
Stocke les references aux composants comme champs prives de ta classe overlay.
Expose des methodes publiques (setMana(), update()) pour
mettre a jour les valeurs. Ne cree pas de nouveaux composants a chaque tick —
modifie seulement les proprietes des composants existants (.text(),
.progress(), etc.).
OverlayMod — Systeme Modulaire
OverlayMod est le nouveau systeme d'overlay modulaire d'EriAPI. Il remplace
l'approche manuelle avec EriOverlay pour les overlays complexes qui ont besoin
de configuration utilisateur, de positionnement par drag & drop et de persistance
automatique entre les sessions de jeu.
Chaque OverlayMod est un module autonome qui embarque :
- Position propre — stockee en pixels de design (espace 1920x1080), convertie a l'affichage par le ScaleManager
- Facteur de zoom propre — chaque mod a son propre scale configurable
- Parametres configurables — via
ModSettings, exposes dans l'editeur - Positionnement drag & drop — l'editeur integre permet a l'utilisateur de deplacer chaque mod librement
- Persistance JSON automatique — position, scale et parametres sont sauvegardes et restaures
- Rendu par frame avec precision nanotime — le callback
onFrame()recoit le delta en nanosecondes
Creer un OverlayMod
import fr.eri.eriapi.overlay.OverlayMod;
import fr.eri.eriapi.gui.components.*;
import net.minecraft.client.Minecraft;
import net.minecraft.entity.player.EntityPlayer;
public class HealthOverlayMod extends OverlayMod {
private Label healthLabel;
private Rectangle background;
public HealthOverlayMod() {
super("my_health", "Health Display", 200, 40);
setCategory("Combat");
setDescription("Affiche les PV du joueur.");
setDefaultPosition(0.01f, 0.9f); // bas-gauche (x et y en fraction de l'ecran)
setMinScale(0.5f);
setMaxScale(2.0f);
}
@Override
protected void buildOverlay() {
background = new Rectangle();
background.originalPos(0, 0).originalSize(200, 40);
background.fillColor(0xAA000000).cornerRadius(6);
getRoot().add(background);
healthLabel = new Label("HP: 20/20");
healthLabel.originalPos(8, 8).originalSize(184, 24);
healthLabel.color(0xFFFF5555).scale(1.2f);
getRoot().add(healthLabel);
}
@Override
protected void onFrame(float partialTicks, long deltaNanos) {
EntityPlayer player = Minecraft.getMinecraft().player;
if (player == null) return;
int hp = (int) player.getHealth();
int maxHp = (int) player.getMaxHealth();
healthLabel.text("HP: " + hp + "/" + maxHp);
}
}
Enregistrement
Enregistre tes mods dans ClientProxy.init() via OverlayModManager.
Comme pour OverlayManager, l'enregistrement se fait une seule fois.
import fr.eri.eriapi.overlay.OverlayModManager;
// Dans ClientProxy.init() :
OverlayModManager.getInstance().register(new HealthOverlayMod());
OverlayModManager.getInstance().register(new DebugOverlayMod());
OverlayModManager.getInstance().register(new FactionInfoMod());
-
ConstructeurOverlayMod(String id, String name, int designWidth, int designHeight)Constructeur.
idest l'identifiant unique (utilise pour la persistance JSON).nameest le nom affiche dans l'editeur.designWidthetdesignHeightsont les dimensions de la zone de rendu en pixels de design (espace 1920x1080). -
Configurationvoid setCategory(String category)Definit la categorie affichee dans le gestionnaire (ex : "Combat", "Informations", "Debug"). Permet de regrouper les mods par theme dans l'interface de gestion.
-
Configurationvoid setDescription(String description)Description courte affichee dans le gestionnaire d'overlays. Doit expliquer en une phrase ce que le mod affiche.
-
Configurationvoid setDefaultPosition(float x, float y)Position par defaut en fraction de l'ecran (0.0 a 1.0 pour x et y). Compatible avec l'ancien format — convertit en interne en pixels de design (
x * 1920,y * 1080). Exemple :(0.01f, 0.9f)= environ coin bas-gauche. -
Configurationvoid setDefaultPositionDesign(float designX, float designY)Position par defaut directement en pixels de design (espace 1920x1080). Preferable a
setDefaultPosition()pour un positionnement precis. Exemple :(20, 950)= 20 design-pixels depuis le bord gauche, 950 depuis le haut. -
Configurationvoid setMinScale(float min) / setMaxScale(float max)Definit les limites du zoom utilisateur dans l'editeur. Valeurs typiques : min
0.5f, max2.0f. Le scale par defaut est1.0f. -
A implementervoid buildOverlay()Methode abstraite a implementer. Construis ici tous les composants visuels avec
getRoot().add(...). Appelee une seule fois au premier rendu (et a chaque appel derebuild()). -
Optionnelvoid buildSettings(ModSettings settings)Override optionnel. Declare ici les parametres configurables du mod via le builder
ModSettings. Voir la section ModSettings. -
Optionnelvoid onFrame(float partialTicks, long deltaNanos)Override optionnel. Appele a chaque frame de rendu (60-144+ fois par seconde).
partialTicksest la fraction de tick ecoulee depuis le dernier tick (0.0 a 1.0).deltaNanosest le temps en nanosecondes depuis le dernier appel — utile pour les timers internes. C'est ici que tu mets a jour les labels, barres, etc. -
Optionnelvoid onTick()Override optionnel. Appele 20 fois par seconde (chaque tick serveur). Utilise pour les mises a jour de donnees lourdes ou les synchronisations serveur. Voir onFrame vs onTick.
-
Optionnelvoid onSettingChanged(String key, Object value)Override optionnel. Appele automatiquement quand l'utilisateur modifie un parametre dans l'editeur.
keycorrespond a la cle declaree dansbuildSettings().valueest la nouvelle valeur (Integer, Float, Boolean ou String selon le type de parametre). -
Getter<T> T getSetting(String key, T defaultValue)Lit la valeur courante d'un parametre. Si le parametre n'a pas encore ete charge (premiere session), retourne
defaultValue. Le typeTest infere depuisdefaultValue— passe le meme type que celui declare dansbuildSettings(). -
Methodevoid rebuild()Force la reconstruction complete des composants visuels au prochain rendu. Appelle
buildOverlay()a nouveau. Utile quand un parametre change le layout (nombre de lignes, mode compact, etc.). -
GetterContainerComponent getRoot()Retourne le conteneur racine dans lequel ajouter les composants. Disponible depuis
buildOverlay().
Utilise EriOverlay pour des overlays simples et statiques (barre de mana,
HUD de faction) ou tu geres toi-meme la mise a jour via des methodes publiques.
Utilise OverlayMod quand tu veux que l'utilisateur puisse configurer,
deplacer et activer/desactiver l'overlay depuis un editeur integre, avec
persistance automatique. Les deux systemes coexistent et peuvent etre utilises ensemble.
ModSettings — Parametres configurables
Chaque OverlayMod peut declarer des parametres configurables en implementant
buildSettings(ModSettings settings). Ces parametres sont automatiquement
exposes dans l'editeur GuiOverlayHub et leurs valeurs sont persistees en JSON.
Le builder ModSettings utilise une syntaxe fluente — chaque appel retourne
le meme builder pour enchaîner les declarations.
@Override
protected void buildSettings(ModSettings settings) {
settings
.separator("Sections visibles")
.toggle("show_fps", "FPS", true)
.toggle("show_coords", "Coordonnees", true)
.separator("Apparence")
.colorPicker("text_color", "Couleur du texte", 0xFFCCCCCC)
.slider("bg_opacity", "Opacite du fond", 0f, 1f, 0.67f)
.toggle("compact", "Mode compact", false);
}
@Override
protected void onSettingChanged(String key, Object value) {
if ("text_color".equals(key)) {
int color = (Integer) value;
myLabel.color(color);
} else if ("compact".equals(key)) {
rebuild(); // Le layout change completement en mode compact
}
}
// Lire une valeur dans onFrame() ou onTick() :
boolean showFps = getSetting("show_fps", true);
float opacity = getSetting("bg_opacity", 0.67f);
Types de parametres disponibles
| Methode | Type retourne | Description |
|---|---|---|
toggle(key, label, default) |
Boolean | Interrupteur on/off. Affiche un toggle switch dans l'editeur. |
slider(key, label, min, max, default) |
Float | Curseur a valeur flottante entre min et max. Ideal pour les opacites, tailles, vitesses. |
sliderInt(key, label, min, max, default) |
Integer | Curseur a valeur entiere. Pour les compteurs, limites de lignes, etc. |
colorPicker(key, label, defaultColor) |
Integer (ARGB) | Selecteur de couleur complet (roue, luminosite, alpha). La valeur est un entier ARGB 0xAARRGGBB. |
dropdown(key, label, default, options...) |
String | Menu deroulant avec une liste de choix. Passe les options sous forme de varargs String. |
textField(key, label, default) |
String | Champ texte libre. Utile pour des prefixes, suffixes ou noms personnalises. |
keybind(key, label, defaultKey) |
Integer (LWJGL) | Selecteur de touche. La valeur est un code LWJGL (Keyboard.KEY_*). |
separator(title) |
— | Separateur visuel avec titre. Ne cree pas de parametre, sert uniquement a organiser l'interface. |
info(text) |
— | Texte informatif non interactif. Utile pour des notes ou avertissements dans l'editeur. |
La methode getSetting(key, defaultValue) retourne toujours une valeur
valide : soit la valeur sauvegardee, soit la valeur par defaut si le parametre n'a
jamais ete modifie. Utilise la meme valeur par defaut dans buildSettings()
et dans getSetting() pour garantir la coherence.
GuiOverlayHub — L'editeur integre
GuiOverlayHub est l'interface graphique centrale du systeme modulaire.
Elle permet aux utilisateurs de positionner, configurer et activer/desactiver leurs
overlays sans quitter le jeu.
Fonctionnalites de l'editeur
La couche de base de l'editeur affiche tous les overlays actifs en superposition sur l'ecran de jeu. Chaque overlay peut etre :
- Deplace — clic + glisser pour repositionner librement
- Zoom — molette de souris pour changer le scale
- Selectionne — clic droit pour ouvrir le menu contextuel (parametres, reset, toggle)
Un toggle Snap to grid permet d'activer l'alignement magnetique sur une grille. Le bouton Reset All remet tous les overlays a leur position par defaut.
Ouvre un popup listant tous les OverlayMod enregistres. Pour chaque mod :
- Toggle d'activation/desactivation rapide
- Categorie et description
- Barre de recherche par nom
- Filtrage par categorie
Disponible sur chaque mod selectionne (via le bouton dans l'editeur ou le menu
contextuel au clic droit). Affiche tous les parametres declares dans
buildSettings() avec les composants correspondants
(toggle, slider, colorPicker, dropdown...). Les changements sont appliques
en temps reel via onSettingChanged().
Ouvrir l'editeur
L'editeur s'ouvre par defaut avec la touche ] (configurable via les keybindings EriAPI). Il peut aussi etre ouvert programmatiquement :
// Ouvrir l'editeur depuis du code (ex. : depuis un keybinding ou une commande)
OverlayModManager.getInstance().openEditor();
Sauvegarder
Le bouton Sauvegarder et fermer ecrit la configuration en JSON (voir Persistance JSON) et ferme l'editeur. Tant que l'utilisateur n'a pas sauvegarde, les changements sont appliques visuellement en temps reel mais pas encore persistes sur disque.
onFrame vs onTick
OverlayMod propose deux callbacks de mise a jour avec des frequences
tres differentes. Bien choisir lequel utiliser impacte directement la fluidite et
les performances de ton overlay.
onFrame() pour tout ce qui est visuel et doit etre fluide. onTick() pour les calculs lourds, les acces au monde ou les donnees synchronisees depuis le serveur.
| Callback | Frequence | Quand l'utiliser |
|---|---|---|
onFrame(partialTicks, deltaNanos) |
60 a 144+ fois/s (chaque frame de rendu) | Mise a jour de labels, coordonnees, FPS, animations fluides, interpolation de valeurs |
onTick() |
20 fois/s (toutes les 50ms) | Calculs lourds, acces au biome, requetes au monde, donnees synchronisees serveur |
Le parametre deltaNanos de onFrame() donne le temps exact
ecoule depuis le dernier appel en nanosecondes. Il permet de creer des timers internes
pour executer du code semi-lourd a intervalles precis sans passer par onTick().
private long timer = 0L;
private static final long INTERVAL = 500_000_000L; // 500ms en nanosecondes
@Override
protected void onFrame(float partialTicks, long deltaNanos) {
timer += deltaNanos;
// Mise a jour legere chaque frame (fluide)
fpsLabel.text("FPS: " + Minecraft.getDebugFPS());
// Mise a jour lourde toutes les 500ms seulement
if (timer >= INTERVAL) {
timer = 0L;
biomeLabel.text("Biome: " + getBiomeName()); // acces au monde, plus couteux
}
}
Les coordonnees du joueur changent entre les ticks (le mouvement est interpole
avec partialTicks). Si tu mets a jour les coordonnees dans
onTick(), le texte saute par increments discrets. Avec
onFrame(), le texte suit le mouvement reel du joueur de facon
completement fluide.
Positionnement en design-pixels
Depuis la mise a jour du systeme modulaire, les positions des OverlayMod sont stockees
en pixels de design (espace 1920x1080), identiquement a tous les composants EriAPI.
C'est le ScaleManager qui convertit ces coordonnees en pixels ecran reels au moment
du rendu.
Pourquoi ce changement ?
L'ancien systeme stockait les positions en fractions de l'ecran (0.0 a 1.0). En apparence independant de la resolution, ce systeme causait un probleme subtil : l'ecart visuel entre deux overlays adjacents changeait selon la resolution et le guiScale du joueur, car les positions et les tailles n'etaient pas dans le meme espace de coordonnees.
| Critere | Ancien systeme (fractions) | Nouveau systeme (design-pixels) |
|---|---|---|
| Unite de position | Fraction ecran (0.0 – 1.0) | Pixels de design (0 – 1920 / 0 – 1080) |
| Unite des composants | Pixels de design (ScaleManager) | Pixels de design (ScaleManager) — identique |
| Ecart entre overlays | Varie selon la resolution | Constant quelque soit la resolution |
| Conversion au rendu | fraction * displayWidth / guiScale |
ScaleManager.scaleXf(posXDesign) |
Compatibilite ascendante
L'API publique reste 100% compatible. setDefaultPosition(float x, float y) accepte
toujours des fractions et les convertit en interne :
// Methode historique — fractions (0.0 a 1.0)
// Toujours valide, convert en interne : x * 1920, y * 1080
setDefaultPosition(0.01f, 0.9f); // ≈ (19, 972) en design-pixels
// Nouvelle methode — pixels de design directs (recommandee)
// Plus precis et coherent avec le reste du systeme EriAPI
setDefaultPositionDesign(20f, 960f); // exactement 20px a gauche, 960px en bas
Exemple — deux overlays adjacents
// Barre de mana en bas a gauche
public class ManaBarMod extends OverlayMod {
public ManaBarMod() {
super("mana_bar", "Mana", 240, 20);
setDefaultPositionDesign(20f, 1010f); // 20px depuis la gauche, 1010px depuis le haut
}
}
// Barre d'endurance juste en dessous, ecart de 6 design-pixels
public class StaminaMod extends OverlayMod {
public StaminaMod() {
super("stamina_bar", "Endurance", 240, 20);
// 1010 (mana) + 20 (hauteur mana) + 6 (ecart) = 1036
setDefaultPositionDesign(20f, 1036f);
}
}
// L'ecart de 6 design-pixels est identique a toutes les resolutions.
getPosXDesign() et getPosYDesign() retournent la position en pixels
de design. Les anciens getters getPosXPercent() et getPosYPercent()
sont toujours disponibles pour la compatibilite (ils convertissent en divisant par 1920/1080).
Persistance JSON
Le systeme modulaire sauvegarde automatiquement la configuration de chaque
OverlayMod dans un fichier JSON. Tu n'as rien a implementer :
la persistance est entierement geree par OverlayModManager.
Emplacement du fichier
.minecraft/config/eriapi/overlay_mods.json
Donnees persistees par mod
| Donnee | Type JSON | Description |
|---|---|---|
| enabled | boolean | Si le mod est actif ou desactive dans le gestionnaire |
| posX / posY | float | Position en pixels de design (0..1920 / 0..1080), convertie au chargement depuis les fractions JSON existantes |
| scale | float | Facteur de zoom applique par l'utilisateur dans l'editeur |
| settings.* | mixed | Valeurs de tous les parametres declares dans buildSettings() (boolean, float, int, String) |
Cycle de sauvegarde et de chargement
- Chargement — automatique a la premiere frame de rendu, apres que tous les mods ont ete enregistres dans
ClientProxy.init() - Sauvegarde — declenchee quand l'utilisateur clique sur "Sauvegarder et fermer" dans
GuiOverlayHub
Le fichier overlay_mods.json est genere et lu par EriAPI.
Modifier sa structure manuellement peut provoquer des erreurs de chargement.
Si le fichier est corrompu, supprime-le — EriAPI recreera les valeurs par defaut
au prochain lancement du jeu.
Exemple complet — DebugOverlayMod
Voici un exemple reel et complet : le DebugOverlayMod du mod EriniumFaction.
Il affiche FPS, coordonnees, chunk, biome, direction, lumiere et memoire en temps reel.
C'est un bon modele pour comprendre comment combiner onFrame(), un timer
nanotime pour les donnees lourdes, et buildSettings().
Architecture du mod
- Dimensions : 380 x 160 px (design 1920x1080)
- Position par defaut : coin superieur gauche (0.5%, 0.5%)
- Donnees legeres (FPS, coords, chunk, direction) — mises a jour dans
onFrame()a chaque frame - Donnees lourdes (biome, lumiere, memoire) — mises a jour toutes les 500ms via un timer nanotime dans
onFrame() - 7 parametres configurables : 7 toggles de visibilite, un colorPicker, un slider et un toggle compact
- FPS colore : vert ≥60, jaune ≥30, rouge <30
package fr.eriniumgroup.eriniumfaction.overlay;
import fr.eri.eriapi.gui.components.Label;
import fr.eri.eriapi.gui.components.Rectangle;
import fr.eri.eriapi.overlay.OverlayMod;
import fr.eri.eriapi.overlay.settings.ModSettings;
import net.minecraft.client.Minecraft;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.chunk.Chunk;
public class DebugOverlayMod extends OverlayMod {
// Labels mis a jour chaque frame
private Label fpsLabel;
private Label coordsLabel;
private Label chunkLabel;
private Label biomeLabel;
private Label directionLabel;
private Label lightLabel;
private Label memoryLabel;
// Fond
private Rectangle background;
// Timer nanotime pour les mises a jour lourdes (500ms)
private long heavyUpdateTimer = 0L;
private static final long HEAVY_UPDATE_INTERVAL = 500_000_000L;
// Cache pour les valeurs lourdes
private String cachedBiome = "";
private String cachedLight = "";
private String cachedMemory = "";
public DebugOverlayMod() {
super("erinium_debug", "Debug Info", 380, 160);
setCategory("Informations");
setDescription("Affiche FPS, coordonnees, biome, direction et memoire.");
setDefaultPosition(0.005f, 0.005f); // coin superieur gauche
setMinScale(0.5f);
setMaxScale(2.0f);
}
@Override
protected void buildOverlay() {
int w = 380;
// Fond semi-transparent avec coins arrondis
background = new Rectangle();
background.originalPos(0, 0).originalSize(w, 160);
background.fillColor(0xAA1A1A2E).cornerRadius(6);
getRoot().add(background);
// Creation des labels sur chaque ligne (espacement de 20px)
int x = 8, y = 6, lineH = 20;
fpsLabel = createLine(x, y, w - 16); y += lineH;
coordsLabel = createLine(x, y, w - 16); y += lineH;
chunkLabel = createLine(x, y, w - 16); y += lineH;
biomeLabel = createLine(x, y, w - 16); y += lineH;
directionLabel = createLine(x, y, w - 16); y += lineH;
lightLabel = createLine(x, y, w - 16); y += lineH;
memoryLabel = createLine(x, y, w - 16);
getRoot().add(fpsLabel, coordsLabel, chunkLabel, biomeLabel,
directionLabel, lightLabel, memoryLabel);
}
private Label createLine(int x, int y, int w) {
Label label = new Label("...");
label.originalPos(x, y).originalSize(w, 18);
label.color(0xFFCCCCCC).scale(0.9f);
return label;
}
@Override
protected void buildSettings(ModSettings settings) {
settings
.separator("Sections visibles")
.toggle("show_fps", "FPS", true)
.toggle("show_coords", "Coordonnees", true)
.toggle("show_chunk", "Chunk", true)
.toggle("show_biome", "Biome", true)
.toggle("show_direction", "Direction", true)
.toggle("show_light", "Lumiere", true)
.toggle("show_memory", "Memoire", true)
.separator("Apparence")
.colorPicker("text_color", "Couleur du texte", 0xFFCCCCCC)
.slider("bg_opacity", "Opacite du fond", 0f, 1f, 0.67f)
.toggle("compact", "Mode compact", false);
}
@Override
protected void onSettingChanged(String key, Object value) {
if ("text_color".equals(key)) {
applyTextColor((Integer) value);
} else if ("bg_opacity".equals(key)) {
float opacity = (Float) value;
int alpha = (int) (opacity * 255);
background.fillColor((alpha << 24) | 0x1A1A2E);
} else if ("compact".equals(key) || key.startsWith("show_")) {
rebuild(); // Le layout change (lignes cachees en mode compact)
}
}
private void applyTextColor(int color) {
if (fpsLabel != null) fpsLabel.color(color);
if (coordsLabel != null) coordsLabel.color(color);
if (chunkLabel != null) chunkLabel.color(color);
if (biomeLabel != null) biomeLabel.color(color);
if (directionLabel != null) directionLabel.color(color);
if (lightLabel != null) lightLabel.color(color);
if (memoryLabel != null) memoryLabel.color(color);
}
@Override
protected void onFrame(float partialTicks, long deltaNanos) {
Minecraft mc = Minecraft.getMinecraft();
EntityPlayer player = mc.player;
if (player == null || mc.world == null) return;
heavyUpdateTimer += deltaNanos;
// FPS — chaque frame, avec couleur adaptative
if (fpsLabel != null && getSetting("show_fps", true)) {
int fps = Minecraft.getDebugFPS();
fpsLabel.text("FPS: " + fps);
fpsLabel.color(fps >= 60 ? 0xFF55FF55 : fps >= 30 ? 0xFFFFFF55 : 0xFFFF5555);
}
// Coordonnees — chaque frame pour une fluidite maximale
if (coordsLabel != null && getSetting("show_coords", true)) {
boolean compact = getSetting("compact", false);
coordsLabel.text(compact
? String.format("XYZ: %.0f / %.0f / %.0f", player.posX, player.posY, player.posZ)
: String.format("X: %.1f Y: %.1f Z: %.1f", player.posX, player.posY, player.posZ));
}
// Chunk — chaque frame
if (chunkLabel != null && getSetting("show_chunk", true)) {
int cx = MathHelper.floor(player.posX) >> 4;
int cz = MathHelper.floor(player.posZ) >> 4;
chunkLabel.text("Chunk: " + cx + ", " + cz);
}
// Direction — chaque frame (rotation interpolee)
if (directionLabel != null && getSetting("show_direction", true)) {
float yaw = MathHelper.wrapDegrees(player.rotationYaw);
directionLabel.text("Direction: " + getCardinalDirection(yaw)
+ " (" + String.format("%.0f", yaw) + ")");
}
// Infos lourdes — toutes les 500ms via timer nanotime
if (heavyUpdateTimer >= HEAVY_UPDATE_INTERVAL) {
heavyUpdateTimer = 0L;
BlockPos pos = player.getPosition();
if (getSetting("show_biome", true)) {
cachedBiome = mc.world.getBiome(pos).getBiomeName();
}
if (getSetting("show_light", true)) {
Chunk chunk = mc.world.getChunk(pos);
cachedLight = "Block: "
+ chunk.getLightFor(net.minecraft.world.EnumSkyBlock.BLOCK, pos)
+ " Sky: "
+ chunk.getLightFor(net.minecraft.world.EnumSkyBlock.SKY, pos);
}
if (getSetting("show_memory", true)) {
Runtime rt = Runtime.getRuntime();
long used = (rt.totalMemory() - rt.freeMemory()) / 1048576L;
long max = rt.maxMemory() / 1048576L;
cachedMemory = used + "MB / " + max + "MB (" + (used * 100 / max) + "%)";
}
}
// Appliquer les valeurs cachees des infos lourdes
if (biomeLabel != null) biomeLabel.text("Biome: " + cachedBiome);
if (lightLabel != null) lightLabel.text("Light " + cachedLight);
if (memoryLabel != null) memoryLabel.text("Mem: " + cachedMemory);
// Visibilite par setting
if (fpsLabel != null) fpsLabel.visible(getSetting("show_fps", true));
if (coordsLabel != null) coordsLabel.visible(getSetting("show_coords", true));
if (chunkLabel != null) chunkLabel.visible(getSetting("show_chunk", true));
if (biomeLabel != null) biomeLabel.visible(getSetting("show_biome", true));
if (directionLabel != null) directionLabel.visible(getSetting("show_direction", true));
if (lightLabel != null) lightLabel.visible(getSetting("show_light", true));
if (memoryLabel != null) memoryLabel.visible(getSetting("show_memory", true));
}
@Override
protected void onTick() {
// Toute la logique de mise a jour est dans onFrame() pour la fluidite.
// onTick() n'est pas utilise ici mais pourrait servir pour des appels reseau.
}
private static String getCardinalDirection(float yaw) {
if (yaw < 0) yaw += 360f;
if (yaw >= 337.5f || yaw < 22.5f) return "S";
if (yaw < 67.5f) return "SW";
if (yaw < 112.5f) return "W";
if (yaw < 157.5f) return "NW";
if (yaw < 202.5f) return "N";
if (yaw < 247.5f) return "NE";
if (yaw < 292.5f) return "E";
return "SE";
}
}
Points cles de cet exemple
L'acces au biome (world.getBiome()), a la lumiere de chunk et
a la memoire JVM sont des operations non triviales. Les executer a chaque frame
(60+ fois/s) gaspillerait du CPU. Le timer heavyUpdateTimer les
limite a une execution toutes les 500ms, tandis que les labels sont mis a jour
depuis le cache a chaque frame — le rendu reste fluide sans surcout.
Quand le parametre compact ou un toggle show_* change,
on appelle rebuild() plutot que de simplement changer la visibilite
des labels. En mode compact, le format du texte lui-meme change
("XYZ: 10 / 64 / -30" au lieu de "X: 10.0 Y: 64.0 Z: -30.0"),
ce qui necessite une reconstruction complete des composants.
Si seule la visibilite changeait, un simple label.visible(false)
suffirait sans rebuild().
Editeur d'overlays : aimantation automatique v1.2.0
Quand vous ouvrez l'editeur in-game (GuiOverlayHub) et que vous deplacez un overlay en le faisant glisser, le systeme d'aimantation automatique entre en jeu. L'overlay en cours de deplacement se colle ("snaps") automatiquement aux bords et centres des autres overlays ainsi qu'aux reperes fixes de l'ecran, facilitant un alignement precis sans avoir a entrer des coordonnees manuellement.
Toutes les coordonnees sont exprimees en pixels de design 1920x1080, coheremment avec le reste du systeme de positionnement EriAPI.
Types d'aimantation
Le systeme detecte 10 types d'accrochage distincts. L'aimantation se declenche quand la distance entre deux reperes est inferieure ou egale a 5 pixels de design.
| Type | Description | Repere source | Repere cible |
|---|---|---|---|
LEFT_TO_LEFT |
Bord gauche aligne sur le bord gauche d'un autre overlay | x de l'overlay deplace | x d'un overlay voisin |
LEFT_TO_RIGHT |
Bord gauche aligne sur le bord droit d'un autre overlay | x de l'overlay deplace | x + largeur d'un overlay voisin |
RIGHT_TO_RIGHT |
Bord droit aligne sur le bord droit d'un autre overlay | x + largeur de l'overlay deplace | x + largeur d'un overlay voisin |
RIGHT_TO_LEFT |
Bord droit aligne sur le bord gauche d'un autre overlay | x + largeur de l'overlay deplace | x d'un overlay voisin |
TOP_TO_TOP |
Bord haut aligne sur le bord haut d'un autre overlay | y de l'overlay deplace | y d'un overlay voisin |
TOP_TO_BOTTOM |
Bord haut aligne sur le bord bas d'un autre overlay | y de l'overlay deplace | y + hauteur d'un overlay voisin |
BOTTOM_TO_BOTTOM |
Bord bas aligne sur le bord bas d'un autre overlay | y + hauteur de l'overlay deplace | y + hauteur d'un overlay voisin |
BOTTOM_TO_TOP |
Bord bas aligne sur le bord haut d'un autre overlay | y + hauteur de l'overlay deplace | y d'un overlay voisin |
CENTER_H |
Centre horizontal aligne sur le centre horizontal d'un autre overlay ou du centre ecran (960) | x + largeur/2 de l'overlay deplace | centre horizontal de la cible |
CENTER_V |
Centre vertical aligne sur le centre vertical d'un autre overlay ou du centre ecran (540) | y + hauteur/2 de l'overlay deplace | centre vertical de la cible |
Aimantation aux bords de l'ecran
En plus des autres overlays, les quatre bords de l'ecran sont des cibles d'aimantation :
- Bord gauche — x = 0
- Bord droit — x = 1920
- Bord haut — y = 0
- Bord bas — y = 1080
Le centre de l'ecran (960, 540) est egalement une cible pour les aimantations CENTER_H
et CENTER_V.
Retour visuel
Deux indicateurs visuels s'affichent pendant le deplacement pour guider le positionnement :
Lignes guides (cyan)
Quand au moins un accrochage est actif, une ligne guide semi-transparente de couleur cyan s'affiche sur l'axe de l'aimantation :
- Une ligne verticale pour un accrochage sur l'axe X (bords gauche/droit ou centre horizontal).
- Une ligne horizontale pour un accrochage sur l'axe Y (bords haut/bas ou centre vertical).
Ces lignes couvrent toute la largeur ou hauteur de l'ecran afin d'etre visibles quelle que soit la position de l'overlay. Elles disparaissent immediatement quand l'overlay est relache ou quand le snap n'est plus actif.
Indicateur de chevauchement (rouge)
Si l'overlay deplace se superpose (au moins partiellement) a un autre overlay pendant le drag, il prend une teinte rouge semi-transparente. Cela signale qu'un chevauchement est en cours. L'overlay peut tout de meme etre pose a cet endroit — c'est un avertissement visuel, pas un blocage.
Desactiver l'aimantation (touche Shift)
Pour deplacer un overlay librement sans aucune aimantation, maintenez la touche Shift pendant le drag. L'overlay suit alors exactement le deplacement de la souris pixel par pixel, sans aucun accrochage ni correction de position.
Utilisez le drag normal (sans Shift) pour aligner les overlays entre eux ou sur les bords de l'ecran. Utilisez Shift uniquement quand vous avez besoin d'une position tres specifique qui ne correspond a aucun repere de grille.