Menus, clavier et souris
Côté client, le Lua peut ouvrir de vraies fenêtres (boutons, listes, champs de texte, onglets…) avec vgui, comme le Derma de Garry's Mod, et réagir aux touches du joueur. De quoi faire un choix d'équipe, une boutique, un vote ou une capacité spéciale.
Première fenêtre
Les fenêtres se créent dans un fichier client (cl_init.lua d'un gamemode, lua/autorun/client/ d'un addon). Ce menu s'ouvre et se ferme avec F3 :
local menu
local function ouvrir_menu()
menu = vgui.Create("DFrame")
menu:SetTitle("Choisis ton équipe")
menu:SetSize(320, 160)
menu:Center()
menu:MakePopup() -- affiche le curseur
for i, nom in ipairs({ "Rouges", "Bleus" }) do
local bouton = vgui.Create("DButton", menu)
bouton:SetText(nom)
bouton:SetTall(40)
bouton:DockMargin(0, 0, 0, 6)
bouton:Dock(TOP)
bouton.DoClick = function()
net.Start("choix_equipe")
net.WriteInt(i)
net.SendToServer()
menu:Close()
end
end
end
hook.Add("PlayerButtonDown", "menu_equipe", function(ply, touche)
if touche ~= KEY_F3 then return end
if IsValid(menu) then menu:Close() else ouvrir_menu() end
end)
Côté serveur, net.Receive("choix_equipe", …) reçoit le choix et appelle ply:SetTeam(…) (voir Messages net). Le serveur doit toujours vérifier ce que le client demande.
Panneaux
vgui.Create(type, parent) crée un panneau et le renvoie. Sans parent, il est posé sur l'écran ; avec un parent, ses coordonnées sont relatives au parent. Les tailles et positions sont en pixels, l'origine en haut à gauche.
| Type | C'est quoi | Méthodes et rappels propres |
|---|---|---|
DFrame | Fenêtre avec titre, bouton ✕, déplaçable à la souris | SetTitle, ShowCloseButton(bool), SetDraggable(bool), SetDeleteOnClose(bool), Close() ; rappel OnClose |
DPanel | Rectangle de fond | SetBackgroundColor(Color), SetPaintBackground(bool) |
EditablePanel | Conteneur invisible | — |
DLabel | Texte | SetText, SetFont, SetTextColor, SetWrap(bool), SetContentAlignment(1-9), SizeToContents() |
DButton | Bouton | SetText, SetEnabled ; rappels DoClick, DoRightClick |
DTextEntry | Champ de texte (une ligne) | GetValue, SetValue, SetPlaceholderText, SetNumeric(bool), SetEditable, SetMaxLength, RequestFocus() ; rappels OnEnter(self, texte), OnChange(self) |
DCheckBox, DCheckBoxLabel | Case à cocher (avec texte pour la seconde) | SetChecked, GetChecked, Toggle, SetText ; rappel OnChange(self, coché) |
DNumSlider | Curseur avec texte et nombre | SetText, SetMin, SetMax, SetMinMax, SetDecimals, SetValue, GetValue ; rappel OnValueChanged(self, valeur) |
DComboBox | Liste déroulante | AddChoice(texte, donnée, choisir), ChooseOptionID(n), ChooseOption(texte), GetSelected() → texte, donnée, GetSelectedID, GetOptionText(n), GetOptionData(n), SetValue(texte) (texte affiché avant un choix), Clear ; rappel OnSelect(self, n, texte, donnée) |
DListView | Tableau à colonnes, triable | AddColumn(nom) → colonne (:SetFixedWidth(px)), AddLine(...) → ligne, RemoveLine(id), GetLine(id), GetLines(), GetSelectedLine() → id, ligne, GetSelected(), ClearSelection, SortByColumn(n, décroissant), SetMultiSelect, SetHideHeaders, Clear ; rappels OnRowSelected(self, id, ligne), DoDoubleClick, OnRowRightClick |
DScrollPanel | Zone qui défile verticalement | Les enfants dockés en TOP s'empilent ; ScrollToChild(panneau) |
DPropertySheet | Onglets | AddSheet(nom, panneau), SwitchToName(nom) |
DIconLayout | Grille qui passe à la ligne | SetSpaceX, SetSpaceY ; les enfants gardent leur SetSize |
DProgress | Barre de progression | SetFraction(0 à 1), GetFraction |
SpawnIcon | Vignette d'un prop (boutique) | SetModel(id) (ex. "builtin:crate"), GetModelName ; rappel DoClick |
DImage | Image | SetImage(chemin, secours), SetImageColor, SetKeepAspect, SizeToContents |
DImageButton | Bouton image | Comme DImage, plus SetStretchToFit, SetDepressImage ; rappels DoClick, DoRightClick |
DModelPanel | Aperçu 3D d'un modèle | SetModel, SetFOV, SetCamPos, SetLookAt, SetColor, SetAutoRotate, SetAnimated… |
DMenu | Menu contextuel (avec DermaMenu()) | AddOption, AddSpacer, AddSubMenu, Open ; rappel OptionSelected |
DColorMixer | Sélecteur de couleur | SetColor, GetColor, SetAlphaBar, SetPalette, SetWangs ; rappel ValueChanged |
RichText | Texte en couleurs qui défile (journal, discussion) | AppendText, InsertColorChange, liens cliquables ; rappel OnTextClicked |
DBinder | Bouton pour choisir une touche | SetValue(code), GetValue ; rappel OnChange |
DTree | Arborescence de nœuds dépliables | AddNode(texte, icône) → nœud, Root, GetSelectedItem, Clear ; rappels OnNodeSelected, DoClick, DoRightClick, OnNodeExpanded |
DHTML, HTML | Page de HTML simple (sans navigateur) | SetHTML, GetHTML, OpenURL ; rappel OnLinkClicked |
DCollapsibleCategory, DCategoryList | Catégorie repliable, liste de catégories | SetLabel, SetContents, Add, SetExpanded, Toggle ; rappel OnToggle |
DNumberWang | Champ numérique borné | SetMinMax, SetDecimals, SetValue, SetInterval ; rappel OnValueChanged |
DColumnSheet | Onglets en colonne à gauche | AddSheet(nom, panneau), SetActiveButton ; rappel OnActiveButtonChanged |
DHorizontalDivider, DVerticalDivider | Deux panneaux séparés par une barre déplaçable | SetLeft / SetRight, SetTop / SetBottom, SetLeftWidth, SetTopHeight… |
DGrid, DTileLayout | Grille à colonnes fixes ; grille qui passe à la ligne | SetCols, SetColWide, SetRowHeight, AddItem… |
DAdjustableModelPanel | Aperçu 3D qu'on fait tourner à la souris | Comme DModelPanel ; rappel OnCameraChanged |
DFileBrowser | Explorateur du dossier de données | SetBaseFolder, SetFileTypes, SetCurrentFolder ; rappels OnSelect, OnDoubleClick… |
Une ligne de DListView a ligne:GetValue(colonne) (aussi GetColumnText), ligne:SetValue(colonne, valeur), ligne:GetID() et ligne:IsLineSelected(). Tu peux y ranger tes propres champs (ligne.prix = 50).
Méthodes de tous les panneaux
| Méthode | Effet |
|---|---|
SetPos(x, y), GetPos() | Position dans le parent |
SetSize(l, h), GetSize(), SetWide, SetTall, GetWide, GetTall | Taille |
Center(), CenterHorizontal(), CenterVertical() | Centre dans le parent (ou l'écran) |
Dock(type), DockMargin(g, h, d, b), DockPadding(g, h, d, b) | Placement automatique (voir plus bas) |
SetVisible(bool), Show(), Hide(), IsVisible() | Afficher / cacher |
MakePopup() | Met la fenêtre devant et libère le curseur de la souris |
Remove(), IsValid() | Supprime le panneau et ses enfants (rappel OnRemove) |
Add(type) | Comme vgui.Create(type, self) |
SetText / GetText, SetValue / GetValue | Texte ou valeur, selon le type |
SetFont(police), SetTextColor(Color) | Polices : "Default", "Small", "Bold", "Big", "Title", ou une taille en nombre |
SetEnabled(bool), SetDisabled(bool), IsEnabled() | Grise le panneau |
SetTooltip(texte), SetAlpha(0-255), MoveToFront(), MoveToBack() | Bulle d'aide, transparence, ordre |
GetParent(), GetChildren(), Clear() | Parent, enfants, supprimer les enfants |
SizeToContents(), SizeToChildren(l, h), InvalidateLayout() | Ajuster la taille, refaire le placement |
IsHovered(), RequestFocus(), HasFocus() | Survol, focus clavier |
SetMouseInputEnabled(bool), SetKeyboardInputEnabled(bool) | Le panneau reçoit-il la souris / le clavier |
Rappels de tous les panneaux : Paint(self, l, h), Think(self) (chaque image), PerformLayout(self, l, h), OnCursorEntered, OnCursorExited, OnMousePressed(self, bouton), OnMouseReleased, OnKeyCodePressed(self, touche) (quand le panneau a le focus), OnRemove.
Placer avec Dock
Plutôt que de calculer les positions, accroche les enfants aux bords du parent : TOP, BOTTOM, LEFT, RIGHT prennent leur hauteur (ou largeur) sur le bord, dans l'ordre de création, puis FILL prend tout ce qui reste. NODOCK revient à SetPos. DockMargin écarte un enfant de ses voisins, DockPadding réserve une marge à l'intérieur du parent (une DFrame réserve déjà sa barre de titre).
local fenetre = vgui.Create("DFrame")
fenetre:SetTitle("Boutique")
fenetre:SetSize(420, 320)
fenetre:Center()
fenetre:MakePopup()
local liste = vgui.Create("DListView", fenetre)
liste:Dock(FILL)
liste:AddColumn("Objet")
liste:AddColumn("Prix"):SetFixedWidth(70)
liste:AddLine("Caisse", 50).modele = "builtin:crate"
liste:AddLine("Baril", 20).modele = "builtin:barrel"
local acheter = vgui.Create("DButton", fenetre)
acheter:SetText("Acheter")
acheter:SetTall(32)
acheter:DockMargin(0, 6, 0, 0)
acheter:Dock(BOTTOM)
acheter.DoClick = function()
local id, ligne = liste:GetSelectedLine()
if not id then return end
net.Start("boutique_achat")
net.WriteString(ligne.modele)
net.SendToServer()
end
Dessiner soi-même : Paint
Définis Paint pour dessiner un panneau avec draw.* et surface.* (voir Dessiner le HUD), en coordonnées locales. Le fond par défaut disparaît. Le panneau est redessiné à chaque image.
local argent = vgui.Create("DPanel", fenetre)
argent:SetTall(26)
argent:Dock(TOP)
argent.Paint = function(self, l, h)
draw.RoundedBox(4, 0, 0, l, h, Color(40, 110, 200))
draw.SimpleText("Argent : " .. LocalPlayer():GetNWInt("argent") .. " $", "Default", 8, h / 2, Color(255, 255, 255), TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER)
end
Tes propres types : vgui.Register
vgui.Register(nom, PANEL, base) crée un type réutilisable à partir d'une table de fonctions et d'un type de base. PANEL:Init() est appelée à la création (celle de la base d'abord).
local PANEL = {}
function PANEL:Init()
self:SetTitle("Vote")
self:SetSize(300, 140)
self:Center()
self:MakePopup()
end
function PANEL:AjouterChoix(texte, action)
local bouton = self:Add("DButton")
bouton:SetText(texte)
bouton:Dock(TOP)
bouton.DoClick = function() action() self:Close() end
end
vgui.Register("MonVote", PANEL, "DFrame")
local vote = vgui.Create("MonVote")
vote:AjouterChoix("Encore une manche", function() print("oui") end)
Images, modèles, menus et texte riche
Ces panneaux ont toutes les méthodes communes (SetPos, SetSize, Dock, SetVisible, Remove, SetTooltip, Think, Paint…). Les images viennent d'un dossier materials/ du gamemode ou d'un addon activé ("mon_addon/logo.png") ou des images intégrées "physicore/logo.png", "physicore/icon_physgun.png" et "physicore/icon_toolgun.png" ; formats png, jpg, jpeg, webp et svg. Une image introuvable écrit une erreur dans la console. Comme pour les sons, les autres joueurs ne voient que les images du jeu et du Workshop : celles d'un addon local restent chez l'hôte. Pour dessiner des images dans le HUD, voir Material.
DImage : image
| Méthode | Description |
|---|---|
img:SetImage(chemin, secours) | Affiche l'image. secours (facultatif) : autre chemin essayé si le premier est introuvable. |
img:GetImage() | Chemin donné à SetImage ("" sinon). |
img:SetMaterial(chemin) | Comme SetImage (accepte seulement un chemin texte). |
img:SetImageColor(Color) / img:GetImageColor() | Teinte multipliée à l'image (alpha compris). |
img:SetKeepAspect(bool) | true : garde les proportions (centrée, bandes vides). Par défaut, l'image est étirée à la taille du panneau. |
img:SizeToContents() | Donne au panneau la taille en pixels de l'image. |
Taille par défaut : 64 × 64.
local frame = vgui.Create("DFrame")
frame:SetSize(300, 260) frame:Center() frame:MakePopup()
local logo = vgui.Create("DImage", frame)
logo:Dock(FILL)
logo:SetImage("mon_addon/logo.png")
logo:SetKeepAspect(true)
DImageButton : bouton image
Bouton sans fond qui affiche une image (étirée par défaut). L'image s'éclaircit au survol et s'enfonce de 2 px au clic.
| Méthode / rappel | Description |
|---|---|
SetImage(chemin, secours), GetImage(), SetImageColor(Color), SetKeepAspect(bool), SizeToContents() | Comme DImage. |
btn:SetStretchToFit(bool) | false : image à sa taille réelle, centrée. true par défaut. |
btn:SetDepressImage(bool) | false : l'image ne s'enfonce plus au clic. true par défaut. |
btn.DoClick(self), btn.DoRightClick(self) | Clic gauche, clic droit. |
local close = vgui.Create("DImageButton", frame)
close:SetSize(24, 24)
close:SetPos(frame:GetWide() - 30, 30)
close:SetImage("mon_addon/fermer.png")
close.DoClick = function() frame:Close() end
SetImage et SetIcon(chemin) marchent aussi sur un DButton et sur une option de DMenu : une icône à gauche du texte (16 px au plus dans un menu).
DModelPanel : aperçu 3D d'un modèle
Affiche un prop (id du menu de spawn : "builtin:crate", "addon:mon_pack/models/chaise.glb", véhicules…) dans une petite scène à part (fond transparent, lumière fixe). Par défaut, le modèle tourne sur lui-même (10° par seconde) et la caméra le cadre automatiquement. Unités en mètres, Y en haut, comme Vector. Le rendu ne tourne que quand le panneau est visible ; un id inconnu affiche le modèle d'erreur.
| Méthode / rappel | Description |
|---|---|
mdl:SetModel(id) / mdl:GetModelName() | Modèle affiché ("" ou nil : rien). |
mdl:SetFOV(degrés) / GetFOV() | Champ de vision (1 à 170, 70 par défaut). |
mdl:SetCamPos(Vector) / GetCamPos() | Position de la caméra (sinon cadrage automatique). |
mdl:SetLookAt(Vector) / GetLookAt() | Point regardé (sinon le centre du modèle). |
mdl:SetColor(Color) / GetColor() | Teinte du modèle ; alpha < 255 = transparent ; blanc = couleurs d'origine. |
mdl:SetAutoRotate(bool, vitesse) | Rotation automatique (vitesse en degrés par seconde, 10 par défaut). false remet l'angle à 0. Propre à PhysiCore. |
mdl:SetModelAngle(degrés) | Tourne le modèle (axe vertical) à cet angle. Propre à PhysiCore. |
mdl:SetAnimated(bool) / GetAnimated() | Joue en boucle la première animation du modèle (modèles importés qui en ont). |
mdl:SetAmbientLight(Color) | Couleur de la lumière ambiante. |
mdl.DoClick(self), mdl.DoRightClick(self) | Clics sur l'aperçu. |
mdl.LayoutEntity | Si tu définis ce rappel, la rotation automatique s'arrête ; il n'est pas appelé (il n'y a pas d'objet entité dans l'aperçu). Il sert à reprendre le code GMod function mdl:LayoutEntity() end. |
DModelPanel en même temps, vgui.Create renvoie nil et écrit une erreur. Pour une grande boutique, utilise des SpawnIcon et un seul DModelPanel pour l'objet choisi.local frame = vgui.Create("DFrame")
frame:SetSize(320, 360) frame:Center() frame:MakePopup()
frame:SetTitle("Caisse")
local mdl = vgui.Create("DModelPanel", frame)
mdl:Dock(FILL)
mdl:SetModel("builtin:crate")
mdl:SetFOV(40)
mdl:SetColor(Color(255, 200, 150))
mdl.DoClick = function() mdl:SetAutoRotate(false) end
DMenu et DermaMenu : menu contextuel
DermaMenu(garderOuverts, parent) crée un menu (caché) et ferme d'abord les autres menus, sauf si le premier argument vaut true ou est un autre DMenu. Avec un parent, le menu disparaît avec ce panneau. menu:Open() l'affiche au-dessus de toutes les fenêtres et libère le curseur tant qu'il est ouvert. Il se ferme (et est supprimé) quand on choisit une option, qu'on clique ailleurs ou qu'on appuie sur Échap. CloseDermaMenus() ferme tous les menus ouverts.
| Méthode / rappel | Description |
|---|---|
menu:AddOption(texte, fonction) | Ajoute une option et la renvoie. La fonction (facultative) est son DoClick et reçoit l'option. |
option:SetIcon(chemin) | Petite icône devant le texte. Les options ont aussi SetText / GetText, SetTooltip, SetEnabled… |
menu:AddSpacer() | Ligne de séparation. |
menu:AddSubMenu(texte, fonction) | Option qui ouvre un sous-menu (au survol, ou au clic s'il n'y a pas de fonction) ; renvoie sousMenu, option. Avec une fonction, cliquer l'option l'appelle et ferme le menu. 8 niveaux au plus. |
menu:Open(x, y) | Ouvre aux coordonnées de l'écran ; sans argument, à la position de la souris. Le menu reste dans l'écran. |
menu:Hide() / menu:Remove() | Cache / supprime (avec ses sous-menus). |
menu:SetDeleteSelf(bool) / GetDeleteSelf() | false : le menu est seulement caché quand il se ferme (réutilisable avec Open). true par défaut. |
menu:SetMinimumWidth(px) | Largeur minimale (120 par défaut). |
menu:ChildCount() / menu:GetChild(n) | Nombre d'options / n-ième option (à partir de 1). |
menu.OptionSelected(self, option, texte) | Appelé après le DoClick d'une option. |
200 éléments au plus par menu.
local liste = vgui.Create("DListView", frame)
liste:Dock(FILL)
liste:AddColumn("Joueur")
for _, ply in ipairs(player.GetAll()) do liste:AddLine(ply:Nick()) end
liste.OnRowRightClick = function(self, id, ligne)
local menu = DermaMenu()
menu:AddOption("Copier le nom", function()
print("Nom :", ligne:GetValue(1))
end):SetIcon("mon_addon/copier.png")
menu:AddSpacer()
local equipe = menu:AddSubMenu("Changer d'équipe")
equipe:AddOption("Rouges", function() net.Start("equipe") net.WriteString("rouge") net.SendToServer() end)
equipe:AddOption("Bleus", function() net.Start("equipe") net.WriteString("bleu") net.SendToServer() end)
menu:Open()
end
DColorMixer : sélecteur de couleur
Le sélecteur de Godot (carré teinte / saturation, curseurs, code hexadécimal, palette). Sa taille minimale est d'environ 300 × 400 px : plus petit, il garde sa taille minimale.
| Méthode / rappel | Description |
|---|---|
mixer:SetColor(Color) / mixer:GetColor() | Couleur choisie (SetValue / GetValue font pareil). SetColor n'appelle pas ValueChanged. |
mixer:SetAlphaBar(bool) | Réglage de la transparence (affiché par défaut). |
mixer:SetPalette(bool) | Couleurs enregistrées sous le sélecteur. |
mixer:SetWangs(bool) | Curseurs et cases numériques R, G, B. |
mixer.ValueChanged(self, couleur) | Quand le joueur change la couleur. |
local mixer = vgui.Create("DColorMixer", frame)
mixer:Dock(FILL)
mixer:SetAlphaBar(false)
mixer:SetColor(Color(255, 120, 0))
mixer.ValueChanged = function(self, couleur)
net.Start("couleur_vehicule") net.WriteColor(couleur) net.SendToServer()
end
RichText : texte riche défilant
Du texte en couleurs, ajouté morceau par morceau (journal, discussion). Il défile tout seul vers le bas quand on ajoute du texte (si on était déjà en bas), et il est sélectionnable. Pas de BBCode : le texte est affiché tel quel. 8192 caractères au plus par AppendText ; au-delà de 2000 paragraphes, les plus anciens sont retirés.
| Méthode / rappel | Description |
|---|---|
rt:AppendText(texte) | Ajoute du texte ("\n" = nouvelle ligne) avec la couleur courante. |
rt:InsertColorChange(r, g, b, a) | Couleur du texte ajouté ensuite (0 à 255 ; accepte aussi une Color). Blanc par défaut. |
rt:InsertClickableTextStart(signal) / rt:InsertClickableTextEnd() | Le texte ajouté entre les deux devient un lien : un clic appelle OnTextClicked avec signal. |
rt.OnTextClicked(self, signal) | Clic sur un lien. |
rt:SetText(texte) / rt:GetText() | Efface tout puis ajoute ce texte / texte affiché (sans couleurs). |
rt:GotoTextEnd() / rt:GotoTextStart() | Défile tout en bas / en haut. |
rt:SetVerticalScrollbarEnabled(bool) | Cache la barre de défilement (la molette marche toujours). |
rt:SetFontInternal(police) / rt:SetFont(police) | Police (nom de surface.CreateFont ou police intégrée). |
local journal = vgui.Create("RichText", frame)
journal:Dock(FILL)
journal:SetFontInternal("Default")
local function log(couleur, texte)
journal:InsertColorChange(couleur) -- une Color, ou quatre nombres de 0 à 255
journal:AppendText(texte .. "\n")
end
log(Color(255, 80, 80), "Erreur !")
journal:InsertColorChange(120, 200, 255, 255)
journal:InsertClickableTextStart("regles")
journal:AppendText("[Lire les règles]\n")
journal:InsertClickableTextEnd()
journal.OnTextClicked = function(self, signal)
if signal == "regles" then Derma_Message("Pas de spawnkill !", "Règles") end
end
DBinder : choisir une touche
Un bouton qui affiche une touche. Au clic, il affiche « Appuie sur une touche… » et la prochaine touche du clavier ou bouton de la souris (gauche, droit, molette, 4, 5) devient sa valeur ; Échap annule. Le clic droit remet « Aucune » (0), sauf si le script définit DoRightClick. Les valeurs sont les codes KEY_* / MOUSE_* (les mêmes que input.IsKeyDown et PlayerButtonDown) ; 0 = aucune touche.
| Méthode / rappel | Description |
|---|---|
binder:SetValue(code) / binder:GetValue() | Touche choisie (un code inconnu devient 0). |
binder:SetSelectedNumber(code) / GetSelectedNumber() | Mêmes choses (noms GMod). |
binder.OnChange(self, code) | Quand le joueur choisit une touche, ou quand SetValue change la valeur. |
local touche = vgui.Create("DBinder", frame)
touche:SetSize(140, 26)
touche:SetPos(20, 40)
touche:SetValue(KEY_G)
touche.OnChange = function(self, code)
print("Nouvelle touche :", input.GetKeyName(code))
end
hook.Add("PlayerButtonDown", "mon_addon_touche", function(ply, code)
if code == touche:GetValue() then print("Action !") end
end)
cvar:SetInt(code) dans OnChange) pour la retrouver à la prochaine partie.Autres panneaux Derma
Ces panneaux reprennent ceux de Garry's Mod. Ils sont écrits en Lua au-dessus des panneaux du jeu (DTree et DHTML s'appuient sur les natifs __DTree et __DHTML) : ils ont toutes les méthodes communes, et on peut s'en servir de base avec vgui.Register.
DTree : arborescence
Une liste de nœuds qu'on déplie (dossiers, catégories). Les nœuds sont repliés à leur création. 5000 nœuds au plus par arbre.
| Méthode / rappel de l'arbre | Description |
|---|---|
tree:AddNode(texte, icône) | Ajoute un nœud de premier niveau et le renvoie (nil au-delà de la limite). L'icône (facultative) est un chemin d'image, comme Material. |
tree:Root() | Le nœud racine invisible : AddNode, GetChildNodes, GetChildNode(i), Clear. |
tree:GetSelectedItem() / tree:SetSelectedItem(nœud) | Nœud choisi. |
tree:Clear() | Retire tous les nœuds. |
tree.OnNodeSelected(self, nœud), tree.DoClick(self, nœud) | Quand un nœud est choisi. |
tree.DoRightClick(self, nœud) | Clic droit sur un nœud. |
tree.OnNodeExpanded(self, nœud, ouvert) | Un nœud est déplié ou replié. |
| Méthode / rappel d'un nœud | Description |
|---|---|
node:AddNode(texte, icône) | Ajoute un sous-nœud et le renvoie. |
SetText / GetText, SetIcon(chemin), SetTooltip(texte) | Texte, icône, bulle d'aide. |
SetExpanded(bool) / GetExpanded(), ExpandRecurse(bool), ExpandTo(bool) | Déplier ce nœud, ce nœud et tous ses descendants, ou tous ses parents. |
GetChildNodes(), GetChildNode(i) (à partir de 0), GetChildNodeCount(), HasChildren(), GetParentNode() | Parcourir l'arbre. |
SetSelected(), Remove(), Clear(), IsValid() | Choisir, supprimer (avec ses enfants), vider, encore là ? |
node.DoClick(self) | Clic sur ce nœud ; renvoyer true empêche OnNodeSelected / DoClick de l'arbre. |
node.DoDoubleClick(self), node.DoRightClick(self) (true = stop), node.OnNodeExpanded(self, ouvert) | Double-clic, clic droit, dépliage. |
local arbre = vgui.Create("DTree", frame)
arbre:Dock(FILL)
local armes = arbre:AddNode("Armes", "mon_addon/icone_armes.png")
armes:AddNode("Pistolet").classe = "weapon_pistol"
armes:AddNode("Fusil").classe = "weapon_ar2"
armes:SetExpanded(true)
arbre.OnNodeSelected = function(self, node)
if node.classe then print("Choisi :", node.classe) end
end
DHTML et HTML : page HTML simple
Il n'y a pas de navigateur dans le jeu : DHTML (et HTML, le même) affiche une page claire de HTML simple, convertie en texte mis en forme. Balises reconnues : h1 à h6, p, div, br, b / strong, i / em, u, s, a href (liens http(s) seulement), font color, span style="color: …", ul / ol / li, pre / code, hr, center, et les entités , <, >, ", &… Les scripts, styles et autres balises sont ignorés.
| Méthode / rappel | Description |
|---|---|
html:SetHTML(html) / html:GetHTML() | Contenu de la page (200 000 caractères au plus). |
html:OpenURL(url) / GetURL() | Affiche un lien vers cette adresse, qui s'ouvre dans le navigateur du joueur ; appelle OnDocumentReady(url) s'il est défini. |
html.OnLinkClicked(self, url) | Clic sur un lien. Renvoyer true pour le gérer soi-même ; sinon gui.OpenURL(url) (avec confirmation). |
QueueJavascript, RunJavascript, AddFunction, SetAllowLua, Call, SetScrollbars, Refresh, StopLoading | Acceptés, sans effet (pas de JavaScript). IsLoading() renvoie false. |
local regles = vgui.Create("DHTML", frame)
regles:Dock(FILL)
regles:SetHTML([[
<h2>Règles du serveur</h2>
<ul><li>Pas de <b>spawnkill</b></li><li><font color="#c03030">Pas de triche</font></li></ul>
<p><a href="https://example.com/forum">Forum</a></p>
]])
DCollapsibleCategory et DCategoryList : catégories repliables
DCollapsibleCategory est un en-tête (bouton ▾ / ▸) au-dessus d'un contenu qu'on replie ; sa hauteur suit celle du contenu. DCategoryList est une zone qui défile (DScrollPanel) où l'on empile des catégories.
| Méthode / rappel | Description |
|---|---|
cat:SetLabel(texte) / GetLabel() | Texte de l'en-tête. |
cat:SetContents(panneau) | Le panneau affiché sous l'en-tête (docké en haut ; l'ancien est supprimé). |
cat:Add(texte) | Ajoute un DButton à la liste de la catégorie et le renvoie. |
cat:SetExpanded(bool) / GetExpanded(), DoExpansion(bool), Toggle() | Déplier / replier. |
cat:SetHeaderHeight(px) | Hauteur de l'en-tête (22 par défaut). |
cat.OnToggle(self, ouvert) | Quand le joueur clique l'en-tête (ou Toggle). |
liste:Add(nom) / liste:AddItem(panneau) | DCategoryList : ajoute une catégorie (renvoyée) / un panneau quelconque. |
local liste = vgui.Create("DCategoryList", frame)
liste:Dock(FILL)
local vehicules = liste:Add("Véhicules")
vehicules:Add("Voiture").DoClick = function() RunConsoleCommand("say", "voiture") end
vehicules:Add("Bateau")
liste:Add("Outils"):SetExpanded(false)
DNumberWang : champ numérique
Un DTextEntry qui n'accepte qu'un nombre, gardé entre un minimum et un maximum (0 à 100 par défaut) et arrondi au nombre de décimales. Les flèches ↑ / ↓ ajoutent ou retirent l'intervalle.
| Méthode / rappel | Description |
|---|---|
SetMin, SetMax, SetMinMax(min, max), GetMin, GetMax | Bornes. |
SetDecimals(n) / GetDecimals() | Décimales (0 par défaut, 8 au plus). |
SetValue(n) / GetValue() | Valeur (un nombre, bornée et arrondie). |
SetInterval(n) / GetInterval() | Pas des flèches (1 par défaut). |
SetFraction(0 à 1) / GetFraction() | Valeur en fraction de l'intervalle min–max. |
wang.OnValueChanged(self, valeur) | Quand la valeur change (frappe, flèches ou SetValue). |
DColumnSheet : onglets en colonne
Une colonne de boutons à gauche (sheet.Navigation, un DScrollPanel de 120 px) et la zone de contenu à droite (sheet.Content). Le premier onglet ajouté est affiché.
| Méthode / rappel | Description |
|---|---|
sheet:AddSheet(nom, panneau) | Ajoute un onglet ; renvoie { Button = bouton, Panel = panneau }. |
sheet:SetActiveButton(bouton) / GetActiveButton() | Onglet affiché. |
sheet.OnActiveButtonChanged(self, bouton) | Quand l'onglet affiché change. |
local sheet = vgui.Create("DColumnSheet", frame)
sheet:Dock(FILL)
local general = vgui.Create("DPanel")
local touches = vgui.Create("DPanel")
sheet:AddSheet("Général", general)
local onglet = sheet:AddSheet("Touches", touches)
sheet:SetActiveButton(onglet.Button)
DHorizontalDivider et DVerticalDivider : diviseurs
Deux panneaux séparés par une barre que le joueur fait glisser à la souris. Chaque côté garde une taille minimale (30 px par défaut).
| DHorizontalDivider | DVerticalDivider | Description |
|---|---|---|
SetLeft(p) / SetRight(p), GetLeft / GetRight | SetTop(p) / SetBottom(p), GetTop / GetBottom | Les deux panneaux (le second prend la place restante). |
SetLeftWidth(px) / GetLeftWidth() | SetTopHeight(px) / GetTopHeight() | Taille du premier côté (150 par défaut). |
SetLeftMin(px), SetRightMin(px) | SetTopMin(px), SetBottomMin(px) | Tailles minimales. |
SetDividerWidth(px) | SetDividerHeight(px) | Épaisseur de la barre (6 par défaut). |
local div = vgui.Create("DHorizontalDivider", frame)
div:Dock(FILL)
div:SetLeft(vgui.Create("DTree"))
div:SetRight(vgui.Create("DListView"))
div:SetLeftWidth(180)
div:SetLeftMin(100)
DGrid et DTileLayout : grilles
DGrid range ses éléments en colonnes de largeur fixe, ligne par ligne ; sa hauteur suit le nombre de lignes. DTileLayout est un DIconLayout (grille qui passe à la ligne) qui accepte aussi SetBaseSize et SetMinHeight.
| Méthode (DGrid) | Description |
|---|---|
SetCols(n), SetColWide(px), SetRowHeight(px) (et Get…) | Nombre de colonnes (4), largeur d'une colonne (32), hauteur d'une ligne (32). |
AddItem(panneau), RemoveItem(panneau, garder), GetItems() | Ajouter, retirer (supprimé sauf si garder vaut true), lister. |
SortByMember(clé, décroissant) | Trie les éléments selon leur champ clé (panneau.prix…). |
local grille = vgui.Create("DGrid", frame)
grille:Dock(TOP)
grille:SetCols(5)
grille:SetColWide(70)
grille:SetRowHeight(70)
for _, id in ipairs({ "builtin:crate", "builtin:barrel" }) do
local icone = vgui.Create("SpawnIcon")
icone:SetSize(64, 64)
icone:SetModel(id)
grille:AddItem(icone)
end
DAdjustableModelPanel : aperçu 3D orientable
Un DModelPanel sans rotation automatique : le joueur maintient le clic gauche ou droit et glisse pour tourner la caméra autour du modèle (à la même distance). Rappel OnCameraChanged(self) à chaque mouvement. SetFirstPerson et CaptureMouse sont acceptés, sans effet.
DFileBrowser : explorateur de fichiers
Un arbre des dossiers (4 niveaux) à gauche et la liste des fichiers à droite, dans le dossier de données client du gamemode en cours (lua_data_client/gamemode_<nom>/), même quand il est créé par un addon.
| Méthode / rappel | Description |
|---|---|
SetBaseFolder(dossier) / GetBaseFolder() | Dossier racine affiché (à appeler en premier : il remplit l'arbre). |
SetFileTypes("*.txt *.json") / GetFileTypes() | Motifs des fichiers listés, séparés par des espaces ("*" par défaut). |
SetCurrentFolder(dossier) / GetCurrentFolder() | Dossier dont les fichiers sont listés. |
SetPath(chemin) / GetPath() | Second argument passé à file.Find ("DATA" par défaut ; sans effet, c'est toujours ton dossier de données). |
fb.OnSelect(self, chemin, ligne), fb.OnDoubleClick(self, chemin, ligne), fb.OnRightClick(self, chemin, ligne) | Fichier choisi, double-cliqué, clic droit ; chemin est relatif au dossier de données. |
fb.OnFolderChanged(self, dossier) | Le dossier affiché change. |
local fb = vgui.Create("DFileBrowser", frame)
fb:Dock(FILL)
fb:SetBaseFolder("sauvegardes")
fb:SetFileTypes("*.json")
fb.OnDoubleClick = function(self, chemin)
print("Fichier choisi :", chemin) -- relatif au dossier de données du gamemode
end
Peaux Derma
Une peau est une table de fonctions SKIN:Paint<Type>(panneau, l, h) qui dessinent les panneaux à la place du thème du jeu. Quand une peau est active, chaque panneau créé ensuite par vgui.Create est dessiné par la fonction de son type, sauf s'il a déjà son propre Paint. Fonctions reconnues : PaintFrame, PaintPanel, PaintButton, PaintTextEntry, PaintCheckBox, PaintComboBox, PaintListView, PaintProgress, PaintMenu, PaintMenuOption, PaintPropertySheet, PaintScrollPanel, PaintLabel.
| Fonction | Description |
|---|---|
derma.DefineSkin(nom, description, SKIN) | Enregistre une peau et la renvoie. |
derma.GetSkinTable(), derma.GetNamedSkin(nom) | Toutes les peaux (par nom) ; une peau. |
derma.GetDefaultSkin() | La peau active : celle dont le hook client ForceDermaSkin renvoie le nom, sinon "Default" (le thème du jeu). |
derma.SkinHook("Paint", "Button", panneau, l, h) | Appelle SKIN:PaintButton(panneau, l, h) de la peau du panneau. |
derma.Color(nom, panneau, défaut) | Le champ nom de la peau du panneau, sinon défaut. |
panel:SetSkin(nom) / panel:GetSkin() | Change la peau d'un panneau dessiné par une peau / sa peau (la peau active si aucune). |
local SKIN = {}
SKIN.fond = Color(30, 30, 40)
function SKIN:PaintFrame(panel, l, h)
draw.RoundedBox(6, 0, 0, l, h, self.fond)
end
function SKIN:PaintButton(panel, l, h)
local couleur = panel:IsHovered() and Color(90, 140, 230) or Color(60, 60, 80)
draw.RoundedBox(4, 0, 0, l, h, couleur)
end
derma.DefineSkin("MaPeau", "Peau sombre de mon serveur", SKIN)
hook.Add("ForceDermaSkin", "MaPeau", function() return "MaPeau" end)
ForceDermaSkin avant d'ouvrir tes fenêtres. Les menus du jeu lui-même ne changent pas.DForm : formulaire de réglages
vgui.Create("DForm", parent) (aussi "ControlPanel", le même) empile des contrôles de haut en bas et grandit avec eux. C'est le panneau que reçoivent les options d'outils (spawnmenu.AddToolMenuOption). Les contrôles liés à une ConVar prennent sa valeur et la changent avec RunConsoleCommand.
| Méthode | Ajoute |
|---|---|
form:SetName(texte) (aussi SetLabel) | Un titre en gras en haut. |
form:Help(texte), form:ControlHelp(texte) → DLabel | Un texte d'aide (sur plusieurs lignes) ; le même en gris. |
form:CheckBox(texte, convar) → DCheckBoxLabel | Une case à cocher ("1" / "0"). |
form:NumSlider(texte, convar, min, max, décimales) → DNumSlider | Un curseur numérique (2 décimales par défaut). |
form:TextEntry(texte, convar) → DTextEntry, DLabel | Un champ texte avec son étiquette à gauche. |
form:ComboBox(texte, convar) → DComboBox, DLabel | Une liste de choix (remplis-la avec AddChoice(texte, donnée) ; la ConVar reçoit la donnée, ou le texte). |
form:Button(texte, commande, ...) → DButton | Un bouton qui lance RunConsoleCommand(commande, ...) (ou mets ton propre DoClick). |
form:AddItem(gauche, droite) | Tes propres panneaux : un seul en pleine largeur, ou deux sur une ligne (le premier à gauche, 110 px). |
form:AddControl(type, données) | Style ancien de GMod : "Header" et "Label" (Description / Text), "CheckBox", "Slider" (Label, Command, Min, Max, Type = "Integer"), "TextBox", "Button". |
form:Clear() | Retire tous les contrôles ajoutés. |
CreateClientConVar("monaddon_couleur", "rouge", true, false)
CreateClientConVar("monaddon_taille", 1, true, false, "Taille", 0.5, 3)
local form = vgui.Create("DForm", fenetre)
form:Dock(TOP)
form:SetName("Mes réglages")
form:NumSlider("Taille", "monaddon_taille", 0.5, 3, 1)
local choix = form:ComboBox("Couleur", "monaddon_couleur")
choix:AddChoice("Rouge", "rouge")
choix:AddChoice("Bleu", "bleu")
form:ControlHelp("Prend effet à la prochaine apparition.")
Menu de spawn et menu contextuel
Les addons ajoutent leurs propres onglets au menu de spawn avec spawnmenu.AddCreationTab (un panneau vgui créé une fois et gardé), des options dans la liste des outils avec spawnmenu.AddToolMenuOption (un DForm), et des actions au menu contextuel (touche N maintenue : la souris est libre, on clique sur un objet) avec properties.Add. Hooks client : ContextMenuOpen, OnContextMenuOpen, OnContextMenuClose, PopulateToolMenu… Tout est décrit dans Intégration au bac à sable.
Traductions : language
Côté client, language.Add(clé, texte) enregistre une phrase et language.GetPhrase(clé) la renvoie (avec ou sans # devant ; la clé elle-même si elle est inconnue). Un texte qui commence par # est remplacé par sa phrase dans SetText, SetTitle, SetTooltip, SetPlaceholderText, AddChoice, AddColumn, AddLine, AddOption, AddSheet, AddSubMenu et AppendText (au moment de l'appel : ajoute les phrases avant de créer les panneaux).
language.Add("monaddon.acheter", "Acheter")
local bouton = vgui.Create("DButton", fenetre)
bouton:SetText("#monaddon.acheter") -- affiche « Acheter »
Boîtes de dialogue toutes faites
| Fonction | Effet |
|---|---|
Derma_Message(texte, titre, bouton) | Message avec un bouton OK |
Derma_Query(texte, titre, "Oui", fonction, "Non", fonction…) | Question avec jusqu'à 4 boutons |
Derma_StringRequest(titre, texte, défaut, siOK, siAnnulé) | Demande un texte ; siOK(texte) |
Derma_Query("Changer de carte ?", "Vote", "Oui", function()
net.Start("vote") net.WriteBool(true) net.SendToServer()
end, "Non", function()
net.Start("vote") net.WriteBool(false) net.SendToServer()
end)
Curseur et touche Échap
- Tant qu'une fenêtre
MakePopupest visible, le curseur est libre, le joueur ne bouge plus et les raccourcis du jeu (menu de spawn, chat…) sont coupés. Fermer ou cacher la dernière rend la souris à la caméra. gui.EnableScreenClicker(true)libère le curseur sans fenêtre (etfalsele rend) ;gui.IsCursorVisible(),gui.MousePos()→ x, y.- Échap ferme la fenêtre
DFramedu dessus (comme son bouton ✕). AvecShowCloseButton(false), Échap ouvre le menu pause à la place : pratique pour forcer un choix d'équipe. - Les panneaux sans
MakePopuprestent affichés mais ne reçoivent pas les clics tant que la souris tient la caméra. - Tout est supprimé quand la partie se termine ou que les scripts sont rechargés.
Touches et souris
Hooks PlayerButtonDown et PlayerButtonUp
Appelés quand le joueur appuie sur une touche ou un bouton de la souris, puis la relâche, avec (ply, touche). Ils existent des deux côtés : le client les reçoit pour son joueur (ply = LocalPlayer()), et le serveur les reçoit pour chaque joueur. Pas d'appel pendant que le joueur écrit (chat, champ de texte) ni quand un menu du jeu est ouvert. Les clics de souris ne comptent que quand la souris tient la caméra.
-- Serveur : capacité spéciale sur G, une fois toutes les 10 secondes.
local recharge = {}
hook.Add("PlayerButtonDown", "dash", function(ply, touche)
if touche ~= KEY_G or not ply:Alive() then return end
if (recharge[ply] or 0) > CurTime() then return end
recharge[ply] = CurTime() + 10
ply:SetPos(ply:GetPos() + Vector(0, 6, 0))
ply:ChatPrint("Super saut !")
end)
Côté serveur, ply:IsKeyDown(touche) dit si une touche est tenue (pour une action « tant que la touche est enfoncée »). Le jeu limite chaque joueur à 40 appuis par seconde.
Bibliothèque input (client)
| Fonction | Renvoie |
|---|---|
input.IsKeyDown(touche) | Touche tenue (faux pendant la saisie de texte) |
input.IsMouseDown(bouton) | Bouton de souris tenu |
input.IsButtonDown(code) | Touche ou bouton tenu |
input.IsShiftDown(), input.IsControlDown() | Maj / Ctrl tenus |
input.GetKeyName(touche) | Nom affichable ("F3", "G", "Clic gauche"…) |
input.IsTyping() | Le joueur écrit-il (chat, champ de texte) ? |
input.GetCursorPos() | x, y du curseur |
Codes des touches
Les constantes existent des deux côtés : KEY_A à KEY_Z, KEY_0 à KEY_9, KEY_PAD_0 à KEY_PAD_9, KEY_F1 à KEY_F12, KEY_SPACE, KEY_ENTER, KEY_TAB, KEY_BACKSPACE, KEY_ESCAPE, KEY_LSHIFT, KEY_LCONTROL, KEY_LALT, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_INSERT, KEY_DELETE, KEY_HOME, KEY_END, KEY_PAGEUP, KEY_PAGEDOWN, KEY_CAPSLOCK, KEY_PAD_ENTER, KEY_PAD_PLUS, KEY_PAD_MINUS, KEY_PAD_MULTIPLY, KEY_PAD_DIVIDE, et pour la souris MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE, MOUSE_4, MOUSE_5.
KEY_A est la touche A sur un clavier AZERTY comme QWERTY. Affiche toujours input.GetKeyName(touche) au joueur. Les touches du jeu (déplacements, Q, C, E…) continuent de marcher : écouter une touche ne la bloque pas, choisis plutôt des touches libres (F3, F4, G…).