Jump to content

onClientGUIClick sur dx


Recommended Posts

Bonsoir,

Enfaite, je cherche une fonction semblable au onClientGUIClick mais pour les dx par exemple j'ai des dxCreateLabel, j'aimerai exercer la fonction onClientGUIClick la dessus, un peu m'a conseillé de mettre un gui avec un alpha dessus, mais ça me génerai pour ce que j'aimerai faire ensuite.

J'ai trouvé cette fonction: https://wiki.multitheftauto.com/wiki/Dx ... entDXClick

Mais elle me semble assez bugué et l'exemple ne marche pas.

Link to comment
  • Moderators
J'ai trouvé cette fonction: https://wiki.multitheftauto.com/wiki/Dx ... entDXClick

Mais elle me semble assez bugué et l'exemple ne marche pas.

Tu es en train de regarder un event d'une ressource spécifique (à savoir dxGui), si tu n'utilises pas dxGui, tu n'as effectivement aucune chance de pouvoir utiliser un des events de cette ressource.

Pourrais-tu nous montrer à quoi ressemble la création de (je suppose) ton dx button ainsi que son affichage ?

Si tu ne crées pas d'élement (dxCreateElement) pour représenter d'une manière logique ton bouton et que tu fais juste des dxDraw, on va être incapable de faire un onClientDXClick de façon simple.

Link to comment

Le lien vers dxCreateElement est mort, mais de ce que j'ai compris, on peut pas intéragir directement sur des dxDrawImage ?

Concernant mon code, rien de compliquer.

  
    for numColonne, player in pairs(players) do 
        local w, h = 880, 450; 
        local x, y = (scr[1]/2)-(w/2), (scr[2]/2)-(h/2); 
        local y = (scr[2]/2)-(h/2) + 110 
        numColonne = numColonne + 15 
        ly = y + numColonne 
        username = string.gsub(getPlayerName(player), "_", " ") 
        local rank = tonumber(getElementData(player, "rankfaction")) or 0 
        if rank == 6 then 
            name = dxDrawText(username.." (Leader)", x+155, ly, left, top, white, 1.2, "default", "center", "center", false, false, true) 
        else 
            name = dxDrawText(username, x+155, ly, left, top, white, 1.2, "default", "center", "center", false, false, true) 
        end 
    end 

D'ailleurs, ce code possède un bug, je n'arrive pas à ajuster la taille du dxDrawText "name" en fonction du numColonne, mais c'est un autre problème.

Link to comment
  • Moderators

Ouai je voulais écrire createElement, dxCreateElement n'existe pas et j'utilise les balises [ wiki][ /wiki] qui met le lien automatiquement en fonction du nom que tu mets, et donc forcément, la page de wiki dxCreateElement n'éxiste pas.

Les dxDraw servent juste à afficher quelque chose à l'écran, c'est des fonctions ultra basiques qui ne stockent ou ne créent aucune donnée.

Les fonctions de GUI en revanche sont représentés par un élément que tu dois créer via guiCreateWindow, guiCreateButtonetc.

Une fois que ton élément est créé, MTA se charge de l'afficher avec ses propres fonctions "guiDraw" (j'explique de façon simple, mais la logique globale reste correcte).

Tu peux ensuite interagir avec tes éléments GUI avec les guiSetPosition, onClientGUIClicketc.

Dans ton cas tu n'utilises que les fonctions de draw et tu n'as donc aucun élément avec lequel tu pourrais interagir.

Dans ton code c'est quoi left et top ? tu les as réellement définit plus haut ? oO

Parce que si t'as juste recopié les noms de variables du wiki, sans les avoir définit ça ne devrait pas marcher !

Voici ma solution (non testée car je ne suis pas sur mon pc).

Commence par copier coller ces fonctions:

-- Fonction de création d'un élément dxText 
function dxCreateText(text, left, top, width, height, color, scale, font, alignX, alignY, clip, wordBreak, postGUI) 
    -- On crée l'élément 
    local dxText = createElement("dxText") 
    -- On lui set les données nécessaires au dxDrawText 
    setElementData(dxText, "text", text, false) 
    setElementData(dxText, "position", {x=left, y=top}, false) 
    setElementData(dxText, "size", {w=width, h=height}, false) 
    setElementData(dxText, "color", color, false) 
    setElementData(dxText, "scale", scale, false) 
    setElementData(dxText, "font", font, false) 
    setElementData(dxText, "align", {x=alignX, y=alignY}, false) 
    setElementData(dxText, "clip", clip, false) 
    setElementData(dxText, "wordBreak", wordBreak, false) 
    setElementData(dxText, "postGUI", postGUI, false) 
    -- On retourne l'élement 
    return dxText 
end 
  
-- Fonction de render des dx elements 
function dxRenderEverything() 
    -- render des dxText: 
    for k, i in ipairs (getElementsByType("dxText")) do 
        local text = getElementData(dxText, "text") 
        local pos = getElementData(dxText, "position") 
        local size = getElementData(dxText, "size") 
        -- dxDrawText ne prend pas de width ni de height, il prend les positions de fin, donc faut les calculer: 
        local right, bottom = pos.x + size.w, pos.y, size.h 
        local color = getElementData(dxText, "color") 
        local scale = getElementData(dxText, "scale") 
        local font = getElementData(dxText, "font") 
        local align = getElementData(dxText, "align") 
        local clip = getElementData(dxText, "clip") 
        local wordBreak = getElementData(dxText, "wordBreak") 
        local postGUI = getElementData(dxText, "postGUI") 
        -- On render 
        dxDrawText(text, pos.x, pos.y, right, bottom, color, scale, font, align.x, align.y, clip, wordBreak, postGUI) 
    end 
    --[[ on pourra rajouter pour un dxButton: 
    for k, i in ipairs (getElementsByType("dxButton")) do 
        -- faire les calculs nécessaires et faire les dxDraw 
    end]] 
end 
addEventHandler("onClientRender", root, dxRenderEverything) 
  
addEvent("onClientDXClick") 
-- Fonction qui s'occupe de trigger l'event onClientDXClick 
function onClientDXClickWatcher(button, state, clickX, clickY) 
    for k, dxText in ipairs (getElementsByType("dxText")) do 
        if isClickInsideDXElement(clickX, clickY, dxText) then 
            triggerEvent("onClientDXClick", dxText, button, state, clickX, clickY) 
        end 
    end 
    --[[ on pourra rajouter pour un dxButton: 
    for k, dxButton in ipairs (getElementsByType("dxButton")) do 
        if isClickInsideDXElement(clickX, clickY, dxButton) then 
            triggerEvent("onClientDXClick", dxButton, button, state, clickX, clickY) 
        end 
    end]] 
end 
addEventHandler("onClientClick", root, onClientDXClickWatcher) 
  
-- Fonction qui retourne true si les coordonnées d'un clic correspond à la position de l'élément 
function isClickInsideDXElement(clickX, clickY, dxElem) 
    local dxType = getElementType(dxElem) 
    if dxType == "dxText" then 
        local pos = getElementData(dxElem, "position") 
        local size = getElementData(dxElem, "size") 
        return isPosInsideBox(clickX, clickY, pos.x, pos.y, size.w, size.h) 
    end 
    --[[ on pourra rajouter pour un dxButton: 
    if dxType == "dxButton" then 
        ... 
        return isPosInsideBox(...) 
    end]] 
end 
  
-- Fonction qui retourne true si une coordonnées est à l'intérieur d'un rectangle définit 
function isPosInsideBox(posX, posY, boxX, boxY, boxW, boxH) 
    return posX >= boxX && posX <= boxX + boxW && posY >= boxY && posY <= boxY + boxH 
end 
  
-- Fonction qui retourne true si un tableau contient une certaine key 
function table.hasKey(t, key) 
    for k, _ in pairs (t) do 
        if k == key then return true end 
    end 
    return false 
end 

Puis remplace exactement les lignes que tu m'as copiées par ceci:

for numColonne, player in pairs(players) do 
    --- hack 
    if not _rankGrid then 
        _rankGrid = {} -- Va juste nous aider à ne créer qu'un seul dxElement par ligne (et donc par player) vu qu'on est dans un onClientRender 
    elseif #_rankGrid ~= #players then -- Si la liste de player à changée, il faut update 
        for player, dxElem in pairs(_rankGrid) do 
            destroyElement(dxElem) -- On supprime tout avant recréation pour ne pas avoir de doublons 
        end 
        _rankGrid = {} 
    end 
    --- 
    local w, h = 880, 450; 
    local x, y = (scr[1]/2)-(w/2)+155, (scr[2]/2)-(h/2)+110; 
    ly = y + numColonne + 15 
    username = string.gsub(getPlayerName(player), "_", " ") 
    local text = username -- de base on prend son username 
    local rank = tonumber(getElementData(player, "rankfaction")) or 0 
    if rank == 6 then 
        text = username.." (Leader)" -- mais s'il est rang 6, on rajoute " (Leader)" au username 
    end 
     
    -- Au lieu de draw directement, on crée un dxText 
    -- dxDrawText(text, x+155, ly, left, top, white, 1.2, "default", "center", "center", false, false, true) 
    if not table.hasKey(_rankGrid, player) then 
        -- Une seule création du dxText avec son onClientDXClick handler 
        local dxText = dxCreateText(text, x, ly, left-x, top-ly, white, 1.2, "default", "center", "center", false, false, true) 
        setElementData(dxText, "player", player, false) -- On stock une donnée dans le dxText, ce qu'on ne pouvait pas faire avec un simple dxDrawText 
        addEventHandler("onClientDXClick", dxElem, testHandler, false) 
        --- hack 
        _rankGrid[player] = dxText 
        --- 
    end 
end 

et enfin le testHandler auquel j'ai attaché l'event onClientDXClick:

--- TEST --- 
function testHandler(button, state, clickX, clickY) 
    if button ~= "left" then return end -- je fais rien si c'est pas le clic gauche de la souris qui a produit le trigger 
     
    local text = getElementData(source, "text") 
    local player = getElementData(source, "player") 
    local realPlayerName = getPlayerName(player) 
    outputChatBox("You clicked on \""..text.."\" (Real name: "..realPlayerName..")") 
    -- Tu peux utiliser player comme bon te semblera 
end 

Cette solution est censée (sauf erreur dans mon code) faire exactement le même rendu que ton précédent code mais si tu auras maintenant un élément avec lequel tu pourras intéragir (en faisant un setElementData pas exemple) et si tu cliques gauche sur une ligne, tu devrais voir apparaître un message dans la chatbox qui contient le texte de la ligne ainsi que le nom original du player (parce que j'ai vu que tu replaçais les _ par des espaces au moment de l'affichage et surtout pour montrer que j'arrive bien à récupérer le joueur depuis la ligne cliquée sans faire de calcul ou de getPlayerFromName).

Pour ton problème de positionnement vertical, il faut que tu définisses la hauteur d'une ligne en pixels (par exemple lineH = 40).

Puis il faut que tu saches à quel Y de l'écran ta liste doit commencer (par exemple startY = 363).

Une fois que tu as ces 2 donées, tu peux facilement calculer la position Y de chaque ligne dans ton for que tu m'as montré en faisant:

local lineY = startY + (numColonne * lineH) 

(D'ailleurs numLigne serait un nom plus approprié, et pourrait éviter des erreurs à cause d'un mal entendu).

Link to comment

Un grand merci à toi une fois de plus! :)

ça me fait plaisir que t'aies pris la peine de rédiger ce code.

Merci de m'avoir réalisé le code, seulement, je me sens vraiment nul car je n'aurais pas pu le faire..

En fait, je comprends à 25% ton code que tu m'as envoyé, devrais-je aller relire mes cours de LUA ?

Il y avait quelques erreurs dans ton code que j'ai su corrigé, seul, heureusement :)

Y'a juste un problème avec le clickX et clickY, quand je clique sur mon écran, peut importe où, il m'affiche le testHandler.

Et le name ne se destroy pas quand je removeEventHandler la fonction avec le username justement.

Je vais continuer d'étudier ton code du coup, merci.

Link to comment
  • Moderators
En fait, je comprends à 25% ton code que tu m'as envoyé, devrais-je aller relire mes cours de LUA ?

Si tu comprends pas la partie qui utilise le _rankGrid et table.hasKey, c'est pas grave parce j'ai dû pondre du code supplémentaire un peu hardcore pour que ça marche avec le seul bout de code que tu as montré.

Si on prends l'exemple du système GUI, tu crées tes gui element que tu crèes, cache, montre et update selon tes besoins, et au moment où tu as besoin. En aucun cas tu auras des guiCreateXXX dans un onClientRender mais le code que tu avais était dans un onClientRender donc pour éviter de te demander d'autres parties de ton code qu'on aurait dû en plus changer pour faire propre, je me suis forcer à faire un peu de hack pour éviter de spammer le dxCreateText qui devait quand même se recréer si un joueur a quitté ou rejoins le serveur (donc si le nombre de players est différent du nombre de dxText qu'on a créé pour ça).

Après avec tous les commentaires que j'ai placés, tu devrais normalement comprendre les fonctions suivantes:

- dxCreateText

- dxRenderEverything

- isPosInsideBox

- isClickInsideDXElement

et

- onClientDXClickWatcher

Si ce n'est pas le cas, demande moi ici ce lesquels tu n'as pas compris, j'essayerai d'expliquer de façon plus détaillée.

Y'a juste un problème avec le clickX et clickY, quand je clique sur mon écran, peut importe où, il m'affiche le testHandler.

Ça j'en étais quasiment certains mais j'ai quand même laissé le bénéfice du doute.

Enfait, les valeurs que tu as calculées pour x, ly, left et top ne sont pas corrects.

Pour bien visualiser, un dxDrawText va tout d'abord créer un rectangle (ou aussi une bounding box) dont tu définis les coordonnées de son angle en haut à gauche (top et left sur le wiki) et les coordonnées de son angle en bas à droite (right et bottom sur le wiki). Ensuite une fois qu'il a créé son rectangle, il va placer son texte en fonction de l'alignement horizontal et vertical que tu lui as spécifié (alignX et alignY).

Donc visuellement ton texte paraît bien placé et paraît ne pas occuper une grosse partie de ton écran, mais l'élément dans son entièreté en occupe une énorme. Et comme j'utilise les coordonnées et les dimensions de l'élément (donc les coordonnées et les dimensions de la bounding box) pour déterminer si un clic souris à été fait sur lui, si la bounding box prends tout l'écran, le onClientDXClick va être trigger sur tout l'écran.

Pour corriger tout ça, il juste corriger la bounding box.

Colle ce code n'importe où et tu pourras visualiser la bounding box de chaque dxDrawText réalisé dans ton script.

local _dxDrawText = dxDrawText -- On copie la fonction originale de MTA 
local dxDrawText = function(text, left, top, right, bottom, color, scale, font, alignX, alignY, clip, wordBreak, postGUI) 
    dxDrawRectangle(left, top, right-left, bottom-top, tocolor(200, 0, 0, 150)) -- on affiche la bounding box en rouge semi transparent 
    _dxDrawText(text, left, top, right, bottom, color, scale, font, alignX, alignY, clip, wordBreak, postGUI) -- et on appelle normalement le vrai _dxDrawText 
end 

Je peux te faire un mini-cours sur skype, ça me sera plus simple et plus rapide pour t'expliquer et montrer des choses.

Là je mets un temps fou à écrire toutes mes explications ^^. Ajoute-moi quand tu pourras (check tes MP).

J'en profiterai pour t'expliquer dans le détail le process de mon code précédent (genre comment et pourquoi il fonctionne xD).

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...