Pourquoi créer un HUD personnalisé
Le GTA V HUD par défaut a été conçu pour un jeu d'action solo, et non pour les scénarios de jeu de rôle complexes qu'exigent les serveurs FiveM. Un HUD personnalisé tu permet d'afficher des informations spécifiques au jeu de rôle, telles que la faim, la soif, le stress et l'état du travail, qui n'existent tout simplement pas dans Vanilla GTA. Au-delà de la fonctionnalité, un HUD personnalisé définit l'identité visuelle de ton serveur et donne le ton dès l'apparition d'un joueur. Les joueurs remarquent immédiatement la différence de qualité entre un serveur exécutant des éléments d'interface utilisateur par défaut et un serveur doté d'un HUD raffiné et spécialement conçu qui correspond à la marque et au thème du serveur. Construire ton propre HUD à partir de zéro signifie également que tu contrôles tous les aspects des performances, garantissant que la superposition s'exécute avec un coût de ressources minimal tout en fournissant exactement les informations dont tes joueurs ont besoin.
Structure des ressources NUI
FiveM utilise NUI (nouvelle interface utilisateur) pour afficher HTML, CSS et JavaScript dans le client du jeu. Ton ressource HUD nécessite une structure de dossiers spécifique pour fonctionner correctement. Le fxmanifest.lua fichier déclare les métadonnées de la ressource, le client.lua Le script envoie les données du jeu à la couche NUI et le html/ Le dossier contient ton interface Web. Voici un manifeste de ressources minimales pour commencer :
fx_version 'cerulean'
game 'gta5'
description 'Custom HUD'
author 'YourName'
version '1.0.0'
ui_page 'html/index.html'
client_script 'client.lua'
files {
'html/index.html',
'html/style.css',
'html/script.js',
'html/fonts/*.woff2'
}
Le ui_page indique à FiveM quel fichier HTML afficher en tant que superposition NUI, et le files Le tableau répertorie tous les actifs auxquels le navigateur doit accéder. Si tu oublies d'inclure un fichier CSS ou une police dans cette liste, le navigateur ne parvient pas à le charger silencieusement sans aucun message d'erreur, ce qui est une source courante de confusion pour les développeurs novices dans le développement de NUI. Gardez la structure de tes ressources propre en séparant les préoccupations : HTML pour la mise en page, CSS pour le style et JavaScript pour la logique et les animations.
Envoi de données de jeu à l'interface utilisateur
Le script Lua côté client est chargé de lire l'état du jeu et de le transmettre à la couche NUI à intervalles réguliers. Tu dois rassembler la santé, l’armure et toutes les valeurs de statut spécifiques au cadre, comme la faim et la soif. La fonction clé est SendNUIMessage(), qui envoie une charge utile JSON au JavaScript exécuté dans la trame NUI. Soyez stratégique quant à ton fréquence de mise à jour, car l'envoi de données à chaque image gaspille des cycles de processeur tandis qu'une mise à jour trop lente donne l'impression que le HUD est lent. Un taux de tick de 200 à 500 millisecondes constitue le bon équilibre pour les barres d'état :
CreateThread(function()
while true do
local ped = PlayerPedId()
local health = GetEntityHealth(ped) - 100 -- GTA health starts at 100
local maxHealth = GetEntityMaxHealth(ped) - 100
local armor = GetPedArmour(ped)
-- Framework-specific data (QBCore example)
local playerData = QBCore.Functions.GetPlayerData()
local hunger = playerData.metadata['hunger'] or 100
local thirst = playerData.metadata['thirst'] or 100
local stress = playerData.metadata['stress'] or 0
SendNUIMessage({
action = 'updateStatus',
health = math.floor((health / maxHealth) * 100),
armor = armor,
hunger = math.floor(hunger),
thirst = math.floor(thirst),
stress = math.floor(stress)
})
Wait(300)
end
end)
Pour le compteur de vitesse, tu as besoin d'un thread séparé fonctionnant à un rythme plus rapide, car la vitesse change rapidement et les joueurs attendent un retour en temps réel lorsqu'ils conduisent. Un intervalle de 50 à 100 millisecondes fonctionne bien pour les données du véhicule. Exécutez le fil du compteur de vitesse uniquement lorsque le joueur est réellement dans un véhicule pour économiser des ressources, et désactivez-le lorsqu'il quitte :
CreateThread(function()
while true do
local ped = PlayerPedId()
if IsPedInAnyVehicle(ped, false) then
local veh = GetVehiclePedIsIn(ped, false)
local speed = GetEntitySpeed(veh) * 3.6 -- Convert to km/h
local rpm = GetVehicleCurrentRpm(veh)
local gear = GetVehicleCurrentGear(veh)
local fuel = GetVehicleFuelLevel(veh)
SendNUIMessage({
action = 'updateVehicle',
speed = math.floor(speed),
rpm = rpm,
gear = gear,
fuel = math.floor(fuel)
})
Wait(50)
else
SendNUIMessage({ action = 'hideVehicle' })
Wait(500)
end
end
end)
Construire l'interface HTML et CSS
La conception visuelle de ton HUD détermine ce que tu ressentes dans le jeu. Les HUD FiveM modernes utilisent des designs épurés et minimalistes avec des arrière-plans semi-transparents, des coins arrondis et des animations subtiles. Placez tes barres d'état dans le coin inférieur gauche où elles ne gênent pas le jeu, et placez le compteur de vitesse en bas à droite lorsque le joueur conduit. Utilisez les propriétés personnalisées CSS pour les couleurs afin de pouvoir facilement thématiser l'ensemble du HUD en modifiant quelques variables. Les barres de progression animées avec des transitions fluides donnent au HUD une sensation raffinée :
<!-- html/index.html -->
<div id="hud-container">
<div class="status-bars">
<div class="bar-wrapper">
<i class="icon health-icon"></i>
<div class="bar">
<div class="bar-fill health-fill" id="health-bar"></div>
</div>
</div>
<div class="bar-wrapper">
<i class="icon armor-icon"></i>
<div class="bar">
<div class="bar-fill armor-fill" id="armor-bar"></div>
</div>
</div>
<div class="bar-wrapper">
<i class="icon hunger-icon"></i>
<div class="bar">
<div class="bar-fill hunger-fill" id="hunger-bar"></div>
</div>
</div>
<div class="bar-wrapper">
<i class="icon thirst-icon"></i>
<div class="bar">
<div class="bar-fill thirst-fill" id="thirst-bar"></div>
</div>
</div>
</div>
<div class="speedometer" id="speedometer" style="display:none">
<div class="speed-value" id="speed-value">0</div>
<div class="speed-unit">KM/H</div>
<div class="fuel-bar">
<div class="fuel-fill" id="fuel-bar"></div>
</div>
</div>
</div>
Pour le CSS, utilisez transition sur la largeur de la barre pour créer des animations de remplissage fluides et appliquer pointer-events: none à l'ensemble du conteneur HUD afin qu'il n'interfère pas avec la saisie du jeu. Différentes couleurs pour chaque barre d'état aident les joueurs à identifier rapidement quelle ressource est faible sans lire les étiquettes. Le rouge pour la santé, le bleu pour l'armure, l'orange pour la faim et le cyan pour la soif sont des conventions largement adoptées que les joueurs comprennent intuitivement.
Gestion des messages JavaScript
La couche JavaScript reçoit les messages du client Lua et met à jour le DOM en conséquence. Enregistrez un écouteur d'événements de message qui distribue en fonction du champ d'action dans la charge utile. Gardez ton logique de mise à jour légère, car elle s'exécute quelle que soit la fréquence à laquelle tes threads Lua envoient des données, et tout décalage dans la couche JavaScript se traduit directement par un bégaiement visuel. Évitez les requêtes DOM dans le gestionnaire de mise à jour en mettant en cache les références d'éléments lors de l'initialisation :
// html/script.js
const elements = {
healthBar: document.getElementById('health-bar'),
armorBar: document.getElementById('armor-bar'),
hungerBar: document.getElementById('hunger-bar'),
thirstBar: document.getElementById('thirst-bar'),
speedometer: document.getElementById('speedometer'),
speedValue: document.getElementById('speed-value'),
fuelBar: document.getElementById('fuel-bar')
};
window.addEventListener('message', (event) => {
const data = event.data;
switch (data.action) {
case 'updateStatus':
elements.healthBar.style.width = data.health + '%';
elements.armorBar.style.width = data.armor + '%';
elements.hungerBar.style.width = data.hunger + '%';
elements.thirstBar.style.width = data.thirst + '%';
// Color shift when low
if (data.health < 25) {
elements.healthBar.classList.add('critical');
} else {
elements.healthBar.classList.remove('critical');
}
break;
case 'updateVehicle':
elements.speedometer.style.display = 'flex';
elements.speedValue.textContent = data.speed;
elements.fuelBar.style.width = data.fuel + '%';
break;
case 'hideVehicle':
elements.speedometer.style.display = 'none';
break;
}
});
Ajoutez une classe CSS pour les états critiques qui déclenche une animation pulsée sur la barre, attirant l'attention du joueur lorsque sa santé ou sa faim chute dangereusement. Ce type de retour visuel est bien plus efficace que de compter sur les joueurs pour surveiller en permanence leurs numéros de statut, et il ajoute une couche de finition qui sépare les HUD amateurs des professionnels.
Personnalisation de la mini-carte
La mini-carte GTA par défaut est fonctionnelle mais se heurte visuellement à la plupart des conceptions HUD personnalisées. FiveM tu permet de contrôler la position, la taille, la forme et le niveau de zoom de la mini-carte grâce à des fonctions natives. Tu peux créer une mini-carte circulaire, la déplacer pour qu'elle corresponde à ton disposition HUD, ou même la masquer entièrement et la remplacer par une solution personnalisée. L'approche la plus courante consiste à remodeler la mini-carte pour compléter l'esthétique de ton HUD tout en conservant intacte la fonctionnalité sous-jacente de la carte de jeu :
CreateThread(function()
-- Wait for map to load
Wait(500)
-- Set minimap shape and position
local minimapHandle = RequestScaleformMovie('MINIMAP')
SetMinimapClipType(1) -- 0 = rectangle, 1 = circle
-- Adjust minimap position and size
local defaultAspect = 1920 / 1080
local resX, resY = GetActiveScreenResolution()
local aspect = resX / resY
local ratio = defaultAspect / aspect
SetMinimapComponentPosition('minimap', 'L', 'B',
0.0, -0.032, 0.145 * ratio, 0.210)
SetMinimapComponentPosition('minimap_mask', 'L', 'B',
0.0, 0.032, 0.128 * ratio, 0.300)
SetMinimapComponentPosition('minimap_blur', 'L', 'B',
-0.01, -0.032, 0.272 * ratio, 0.420)
-- Hide default health and armor bars
local minimap = RequestScaleformMovie('MINIMAP')
while not HasScaleformMovieLoaded(minimap) do Wait(0) end
while true do
-- Disable default HUD components
HideHudComponentThisFrame(6) -- Vehicle name
HideHudComponentThisFrame(7) -- Area name
HideHudComponentThisFrame(8) -- Vehicle class
HideHudComponentThisFrame(9) -- Street name
Wait(0)
end
end)
Lors de la personnalisation de la mini-carte, tenez toujours compte des différents rapports hauteur/largeur de l'écran. Une mini-carte qui semble parfaite sur un écran 16:9 sera étirée ou mal positionnée sur les moniteurs ultra-larges. Calculez le rapport entre le rapport hauteur/largeur par défaut et le rapport hauteur/largeur réel et appliquez-le aux composants de largeur. Testez ton mini-carte sur au moins trois résolutions courantes (1 920 x 1 080, 2 560 x 1 440 et 3 440 x 1 440) pour garantir un positionnement cohérent dans les différentes configurations de joueurs.
Masquage des éléments GTA HUD par défaut
Lorsque tu crées un HUD personnalisé, tu dois masquer les éléments GTA par défaut que ton interface utilisateur personnalisée remplace, sinon les joueurs verront des informations en double. FiveM fournit le HideHudComponentThisFrame() natif à cet effet, mais il doit être appelé à chaque image car GTA réactive les composants HUD à chaque tick. Les identifiants des composants couvrent tout, des étoiles recherchées au présentoir à billets en passant par la roue des armes. Pour un remplacement complet du HUD, tu souhaites généralement masquer la barre de santé, la barre d'armure, l'affichage de la trésorerie et les indicateurs du véhicule tout en gardant visibles les éléments essentiels tels que les sous-titres et les fenêtres contextuelles de notification. Créez un tableau configurable d'ID de composants afin que les propriétaires de serveurs puissent masquer les éléments par défaut sans modifier ton code. De plus, utilisez DisplayRadar(false) si ton HUD inclut son propre remplacement de mini-carte, mais sachez que masquer le radar désactive également la carte du menu pause, sauf si tu la réactivez lorsque le menu pause est détecté.
Meilleures pratiques en matière de performances
Les performances du HUD sont essentielles car ton HUD fonctionne en permanence pendant que le joueur est en jeu, ce qui transforme même de petites inefficacités en pertes d'images notables au cours de sessions de jeu prolongées. L'optimisation la plus efficace consiste à contrôler ton fréquence de mise à jour. Les barres d'état qui changent lentement, comme la faim et la soif, peuvent être mises à jour toutes les 500 millisecondes ou même toutes les secondes, tandis que les valeurs qui changent rapidement comme la vitesse nécessitent des mises à jour plus fréquentes. Utilisez le rendu conditionnel pour envoyer des messages NUI uniquement lorsque les valeurs changent réellement, plutôt que de transmettre les mêmes données à plusieurs reprises. Du côté du JavaScript, évitez innerHTML pour les mises à jour car cela oblige le navigateur à analyser le HTML et à utiliser textContent ou des modifications directes des propriétés de style à la place. Réduisez les animations CSS sur les éléments qui sont fréquemment mis à jour, car le moteur d'animation du navigateur et tes mises à jour JavaScript peuvent entrer en conflit, provoquant des problèmes visuels. Si ton HUD utilise des polices personnalisées, préchargez-les dans ton en-tête HTML pour éviter les changements de mise en page une fois le chargement du fichier de police terminé. Enfin, profilez ton ressource à l'aide du module intégré de FiveM. resmon commande et visez à ce que ton HUD consomme moins de 0,1 ms par image en moyenne, laissant une marge pour les dizaines d'autres ressources exécutées sur le client.
