Jump to content

[Tutoriel] Interface en DX d'un panel de connexion


Recommended Posts

Ce tutoriel est la suite du tutoriel disponible ici

 

Il était préférable de créer un autre sujet au lieu de poster une réponse dans l'autre afin que tout ça soit clair et bien présenté.

Aujourd'hui, on fait la même chose que plus haut mais en version DX !

 

II) Méthode N° 2: DX

Les DX sont différent des GUI dans le sens ou ceux-ci ne peuvent pas être positionné par rapport à un autre élément, je m'explique, prenons la fonction "guiCreateEdit", l'argument facultatif (le 7ème) est "element parent = nil" c'est à dire que si nous lui mettons un élément parent en argument, la position et la taille se feront par rapport à cet élément dit "parent"

En DX, tout est calculé en absolu depuis le haut à gauche de notre écran, ce qu'il faut, c'est faire notre "propre" système de relatif et de parent, grâce aux maths, aux variables et à votre cerveau !

 

Commençons par recréer notre meta.xml dans une nouvelle ressource.

741ae5242ea3a54b8d00111d7ad12e28.png

 

Créons ensuite le fichier "c_dx.lua" dans lequel nous allons développer notre petit panel de connexion.

Comme fait dans le panel GUI, nous allons afficher notre panel au lancement de la ressource, le changement avec les GUI, c'est que les DX sont affichés à chaque frame affiché par GTA (FPS)!

Pour ça, il faut utiliser l'event "onClientRender" combiné à la fonction "addEventHandler" dans notre fonction "resourceStart" pour afficher la fonction "panelConnexion" qui contiendra notre panel en DX, ce qui donne :

db83b84d646ff8734743d1e8a2b0a04d.png

Maintenant, il nous faut rajouter une variable au dessus de la fonction "resourceStart" afin de connaître la résolution utilisée par l'utilisateur, pour ça, utilisons la fonction "guiGetScreenSize" qui nous retourne deux valeurs: la largeur et la hauteur de l'écran du joueur, nous allons donc mettre le code suivant au début de notre fichier :

local largeur, hauteur = guiGetScreenSize()

Pour rappel, "local" signifie que nous pouvons utiliser ces variables seulement dans ce fichier "c_dx.lua" et pas dans un autre fichier client.

Il faut remplir la fonction "panelConnexion" car si elle s'exécute à chaque frame sans rien faire c'est problématique.

Assignons plusieurs variable, la largeur, la hauteur mais aussi la positionX et la position Y. Ces deux dernières seront calculées par rapport à la largeur et la hauteur afin de centrer notre panel.

Mais vous allez me dire :

Quote

Dans la première partie, nous avons écris 0.271 en largeur et 0.384 en hauteur mais tu viens de nous dire que en DX on travaille en absolue, on fé komen ?

Et bien Jamy, il suffit de multiplier notre taille relative par la taille de l'écran du joueur afin que la taille soit adaptée peu importe la résolution du joueur.

Comme dans la partie I sur le GUI, le centre est défini par la moitié de l'écran - la moitié de notre panel.

Si on suit cette logique, on doit donc écrire le code suivant dans notre fonction "panelConnexion" : 

function panelConnexion()
  local panelLargeur, panelHauteur = 0.271 *largeur, 0.384 *hauteur
  local panelX, panelY = largeur/2 - panelLargeur/2, hauteur/2 - panelHauteur/2
end

 

 

C'est déjà pas mal de maths, malheureusement pour les non matheux, c'est pas fini, nous devons afficher l'arrière plan de notre panel.

Ce qui est cool avec les DX, c'est qu'il y a déjà une fonction pour afficher un rectangle, donc on a pas besoin d'image inutile pour simuler ce rectangle, il nous suffit d'appeler la fonction "dxDrawRectangle" qui débutera à notre panelX, panelY avec pour taille panelLargeur et panelHauteur.

bool dxDrawRectangle ( float startX, float startY, float width, float height [, int color = white, bool postGUI = false, bool subPixelPositioning = false ] )

Vous pouvez voir que cette fonction possède en argument optionnel une variable "color" qui peut être utilisée avec "tocolor", c'est une fonction MTA SA dans laquelle nous mettons le RGBA de la couleur que nous voulons et MTA nous la convertie pour l'utilisation dans les dx notamment. MTA nous mâche bien le travail quand même!

Ici, nous allons mettre "tocolor(0, 0, 0, 100)" car nous voulons un rectangle noir avec un peu de transparence.

Notre fichier ressemble donc à celui-ci:

9d5052f62d86d5bc29cff6f87711ba0c.png

Exécutons notre code et voyons ce que ça fait.

751c906a85ef9188c502c7e429c11f62.jpg

Un magnifique rectangle bien centré comme nous le voulons !

Maintenant, il s'agit de faire une superposition avec le fond de la barre de titre, pour calculer la taille en hauteur de cette barre de titre, nous allons reprendre la taille dans la partie I sur le GUI (0.145) puis la multiplier par la taille du panel donc : 0.145 *panelHauteur

Entrons donc: 

  dxDrawRectangle(panelX, panelY, panelLargeur, 0.145 *panelHauteur, tocolor(0, 0, 0, 255))

f40082a73568891169d486c0342c0589.png

Parfait! C'est exactement ce qu'on veut !

Continuons en ajoutant un texte, pour ça, utilisons la fonction "dxDrawText".

bool dxDrawText ( string text, float left, float top [, float right=left, float bottom=top, int color=white, float scale=1,
                  mixed font="default", string alignX="left", string alignY="top", bool clip=false, bool wordBreak=false,
                  bool postGUI=false, bool colorCoded=false, bool subPixelPositioning=false,
                  float fRotation=0, float fRotationCenterX=0, float fRotationCenterY=0 ] )

Ici, il y a beaucoup d'arguments facultatifs en effet mais ils sont très utiles.

Pour commencer, on ne spécifie pas une taille dans cette fonction mais une position de début et une position de fin, ce qui signifie que si l'on veut que ce soit centrer en largeur, en position de départ nous devons mettre "panelX" et en position de fin "panelX+panelLargeur", si vous avez compris ça, c'est un jeu d'enfant pour compléter ces arguments.

Complétons ensuite les arguments jusqu'au centrage du texte, le font est "pricedown" qui est déjà incluse dans MTA, on a donc pas a créer nous même une police (sachez qu'on peut utiliser des polices personnalisées en DX via dxCreateFont) les valeurs du centrage peuvent être "left", "center" ou "right" pour la largeur et "top", "center" ou "bottom" pour la hauteur, dans notre cas, nous allons mettre "center" aux deux. Ce qui nous donne :

dxDrawText("Panel de connexion", panelX, panelY, panelX+panelLargeur, panelY+0.145 *panelHauteur, tocolor(255, 255, 255, 255), 1.75, "pricedown", "center", "center")

 

Après avoir vu la ligne ci-dessus vous allez me dire :

Quote

La taille est adaptée toute seule ?

Et bien.. Non. Décidément, on vas devoir jouer avec les maths du début jusqu'à la fin.

Pour calculer la taille en relatif, nous allons simplement diviser la taille de la résolution en largeur par la taille qui nous correspond sur notre écran, moi je suis en 1920x1080 et la taille qui me correspond est "1.75", je vais donc diviser 1920 par 1.75 ce qui nous donne 1097, ce résultat est le nombre par lequel la largeur de la résolution du joueur doit être divisée.

Dans mon cas, j'écris donc "largeur/1097".

dxDrawText("Panel de connexion", panelX, panelY, panelX+panelLargeur, panelY+0.145 *panelHauteur, tocolor(255, 255, 255, 255), largeur/1097, "pricedown", "center", "center")

9285e771daf0c0b91357aa24b6049e6e.png

Jusqu'ici, tout vas bien, c'est la magie des maths, pour l'instant, préoccupons nous pas des champs de saisie et continuons avec le même principe avec dxDrawImage pour obtenir le résultat que l'on veut, je vous laisse à vos clavier !

 

Après quelques lignes rajoutées, j'arrive enfin à mon résultat désiré:

ef65657e564c55718fabad36fcb915bd.png

Voici ma fonction "panelConnexion" en entière:

function panelConnexion()
  local panelLargeur, panelHauteur = 0.271 *largeur, 0.384 *hauteur
  local panelX, panelY = largeur/2 - panelLargeur/2, hauteur/2 - panelHauteur/2

  -- Fond du panel
  dxDrawRectangle(panelX, panelY, panelLargeur, panelHauteur, tocolor(0, 0, 0, 100))

  -- Barre de titre (fond)
  dxDrawRectangle(panelX, panelY, panelLargeur, 0.145 *panelHauteur, tocolor(0, 0, 0, 255))
  -- Barre de titre (texte)
  dxDrawText("Panel de connexion", panelX, panelY, panelX+panelLargeur, panelY+0.145 *panelHauteur, tocolor(255, 255, 255, 255), largeur/1097, "pricedown", "center", "center")

  local identifiantsY = panelY+ 0.2 *panelHauteur -- Variables pour moins de bordel dans l'utilisation de la fonction dxDrawText
  dxDrawText("Entrez vos identifiants :", panelX+ 0.0385 *panelLargeur, identifiantsY, panelX+panelLargeur- 0.0385 *panelLargeur, identifiantsY+ 0.0482 *identifiantsY, tocolor(255, 255, 255, 255), largeur/1097, "clear", "left", "center")

  -- Nom d'utilisateur (Image)
  dxDrawImage(panelX+ 0.077 *panelLargeur, panelY+ 0.313 *panelHauteur, 0.088 *panelLargeur, 0.111 *panelHauteur, "images/nom_utilisateur.png")

  -- Mot de passe (Image)
  dxDrawImage(panelX+ 0.077 *panelLargeur, panelY+ 0.525 *panelHauteur, 0.088 *panelLargeur, 0.111 *panelHauteur, "images/mot_de_passe.png")

  local connexionX, connexionY = panelX+ 0.477 *panelLargeur, panelY+ 0.783 *panelHauteur -- Variables pour moins de bordel dans l'utilisation de la fonction dxDrawText
  dxDrawImage(connexionX, connexionY, 0.3846 *panelLargeur, 0.12 *panelHauteur, "images/connexion_fond.png") -- Afficher l'image avant le texte pour qu'elle soit en dessous du texte
  dxDrawText("Connexion", connexionX, connexionY, connexionX+ 0.3846 *panelLargeur, connexionY+ 0.12 *panelHauteur, tocolor(255, 255, 255, 255), largeur/1280, "clear", "center", "center")

  -- Icône en bas à gauche
  dxDrawImage(panelX+ 0.029 *panelLargeur, panelY+ 0.723 *panelHauteur, 0.1731 *panelLargeur, 0.217 *panelHauteur, "images/icon_bas_gauche.png")
end

 

Maintenant, passons à la réalisation du champ de saisie entièrement en DX.

Je vous ai facilité la tâche en rédigeant les champs de saisies, vous n'avez plus qu'à les appeler en faisant

afficherSaisie(x, y, saisieLargeur, saisieHauteur, texteDeBase)

b0dea874ae65f461a85c90e405c43eec.png

 

Voici tout ce qui concerne les champs de saisies.

function afficherSaisie(x, y, saisieLargeur, saisieHauteur, texte)
  saisies[texte] = texte -- Implémentation du champ de saisie dans notre liste des champs de saisie
  dxDrawRectangle(x, y, saisieLargeur, saisieHauteur, tocolor(255, 255, 255, 200)) -- Affichage du fond de notre champ de saisie
  -- Affiche notre texte avec opération quaternaire si le champ n'a jamais été édité
  dxDrawText((editSaisie[texte]) and editSaisie[texte] or texte, x+ 0.05 *saisieLargeur, y, x+saisieLargeur, y+saisieHauteur, tocolor(0, 0, 0, 255), largeur/1920, "default", "left", "center")
  local curseurX, curseurY = getCursorPosition() -- Récupération de la position du curseur
  if (getKeyState("backspace") and edit == texte) then -- Si appuie sur "Retour" ou le champ en édition est égal au champ affiché
    editSaisie[texte] = string.sub(editSaisie[texte], 1, string.len(editSaisie[texte])-1) -- Retirer la dernière lettre du champ de saisie
  end
  if (curseurX and curseurY) then -- Pour éviter les erreurs dans le débug
    if (isInBox(curseurX*largeur, curseurY*hauteur, x, x+saisieLargeur, y, y+saisieHauteur)) then -- Fonction utile, vérifie la position du curseur dans un carré
      tooltip(curseurX, curseurY, "Cliquez pour editer") -- Fonction utile, affiche un message sur la souris
      if (justClicked) then -- Fonction utile, si joueur clique
        if (edit ~= texte) then -- Et que le champ en édition est différent de celui affiché
          edit = texte -- On édite alors le champ affiché
          if (not editSaisie[texte]) then -- S'il n'existe pas
            editSaisie[texte] = edit -- On le créé
          end
          if (editSaisie[texte] == texte) then -- S'il est égal au texte de base
            editSaisie[texte] = "" -- On le supprime en remplaçant par ""
          end
        end
      end
      justClicked = nil
    end
  end
end

local touches = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k",
"l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "backspace", "space", "'"} -- Liste des touches utilisables

addEventHandler( "onClientKey", getRootElement(), function(bouton, press)
  if (press) then -- Si touche enfoncée
    for _, touche in pairs(touches) do -- On vérifie qu'on autorise la touche
      if (touche == bouton) then -- Si on la trouve
        for k, texte in pairs(editSaisie) do -- On cherche notre champ en édition
          if (k == edit) then -- Si on le trouve
            if (touche == "backspace") then -- Et que la touche "Retour" est enfoncée
              editSaisie[k] = string.sub(texte, 1, string.len(texte)-1) -- On efface la dernière lettre
            elseif (touche == "space") then -- Sinon si la touche "Espace" est enfoncée
              editSaisie[k] = texte .. " " -- On rajoute un espace
            else -- Sinon
              if (getKeyState("lshift") or getKeyState("rshift")) then -- Si une touche pour majuscule est enfoncée
                touche = string.upper(touche) -- Mettre la lettre en majuscule
              end
              editSaisie[k] = texte .. touche -- Ajouter la lettre au champ de saisie
            end
            break
          end
        end
        break
      end
    end
  end
  return false
end )

-- Fonctions utiles
function isInBox( x, y, xmin, xmax, ymin, ymax)
    if x and y and xmin and xmax and ymax and ymin then
        return x >= xmin and x <= xmax and y >= ymin and y <= ymax
    end
end

local tooltip_text_color = tocolor( 255, 255, 255, 255 )
local tooltip_background_color = tocolor( 0, 0, 0, 190 )
local tooltipYet = false
function tooltip( x, y, text, text2 )
	tooltipYet = true

	text = tostring( text )
	if text2 then -- Argument text2 optionnel
		text2 = tostring( text2 )
	end

	if text == text2 then -- Si text est équivalent au text2, alors on supprime ce dernier
		text2 = nil
	end

	local width = dxGetTextWidth( text, 1, "clear" ) + 20 -- On récupère la largeur pour l'encadré du text
	if text2 then -- Si text2
		width = math.max( width, dxGetTextWidth( text2, 1, "clear" ) + 20 ) -- On récupère la largeur pour l'encadré du text2
		text = text .. "\n" .. text2
	end
	local height = 10 * ( text2 and 5 or 3 ) -- On calcule la hauteur de notre encadré
	x = x *largeur
	y = y *hauteur

  -- On affiche le tout
	dxDrawRectangle( x, y, width, height, tooltip_background_color, true )
	dxDrawText( text, x, y, x + width, y + height, tooltip_text_color, 1, "clear", "center", "center", false, false, true )
end


addEventHandler("onClientClick", getRootElement(), function (button, state)
	if (button == "left" and state == "down") then -- Si le clic gauche est enfoncé
		justClicked = true -- Alors on vient de cliquer
	elseif (button == "left" and state == "up") then -- Sinon s'il est relaché
		justClicked = false -- On ne clique plus
	end
end)

 

Si vous voulez vraiment que je détaille tout ce travail, j'en ferai un tutoriel.

Vous pouvez télécharger ma ressource utilisée pendant le tutoriel ici :

Télécharger via Mega.NZ

J'espère que ce tutoriel vous a plus, on se retrouve pour la partie III sur le CEF !

Bon jeu !

Link to comment
5 hours ago, Wumbaloo said:

Ouais, tu peux, il est top ton login panel, à part le design il est top

Tu as utilisé mon tuto avec les edit field pour le faire ou tu as tout refais ?

Ton tuto n'était pas encore publié lorsque j'ai réalisé mon script, j'ai tout refait moi-même.

D'ailleurs je ne l'ai pas précisé mais j'ai tout fait en gui et non pas en DX, les formes géométriques étant donc des images.

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...