HU/Scripting the GUI - Tutorial 1
GUI készítése - Tutorial 1 (Gridlists)
Ebben a tutoriálban fel fogjuk fedezni a gridlist-ek használatát egy járműválasztási kijelzőn egy tetszőleges kliensoldali jármű xml olvasással.
Ne feledje, hogy ez a tutoriál feltételezi azt, hogy tisztában van az előző tutoriálban bemutatott összes tartalommal
A jármű kiválasztásának létrehozása
A GUI létrehozása
Kezdésként nyisson meg egy client oldali lua fájlt (ha követte a bevezetés a scriptelésbe oldalt, akkor ez a gui.lua lesz) amivel majd dolgozni fog.
Ebben a fájlban elfogjuk kezdeni megírni a saját function-ünket a GUI létrehozásához. Ahogy azt az előző tutoriálban említettük, a gui létrehozásakor 2 értéktípus közül tudunk választani: relative és absolute.
Ennek a tutoriálnak a céljából most absolute értékeket fogunk használni.
Ez létre fog hozni egy egyszerű GUI ablakot, egy gridlist-et, és egy gombot:
function createVehicleSelection() -- get the screen width and height local sWidth, sHeight = guiGetScreenSize() -- use some simple maths to position the window in the centre of the screen local Width,Height = 376,259 local X = (sWidth/2) - (Width/2) local Y = (sHeight/2) - (Height/2) windowVehicleSelection = guiCreateWindow(X,Y,Width,Height,"Vehicle Selection Window",false) gridlistVehicleSelection = guiCreateGridList(10,26,357,192,false,windowVehicleSelection) -- add a "Vehicle" and a "Type" collumn to the gridlist guiGridListAddColumn(gridlistVehicleSelection,"Vehicle",0.2) guiGridListAddColumn(gridlistVehicleSelection,"Type",0.2) -- set the default width of the columns to roughly half the size of the gridlist (each) guiGridListSetColumnWidth(gridlistVehicleSelection,1,0.4,true) guiGridListSetColumnWidth(gridlistVehicleSelection,2,0.5,true) buttonCreate = guiCreateButton(121,227,120,20,"Create",false,windowVehicleSelection) -- add the event handler for when the button is clicked addEventHandler("onClientGUIClick",buttonCreate,createVehicleHandler,false) -- hide the GUI guiSetVisible(windowVehicleSelection,false) -- this will add all the vehicle options onto the gridlist, it is explained later in this tutorial populateGridlist() end
Most ezt a function-t meg is kell hívnunk valamivel, különben a GUI sosem lesz létrehozva. Ahogy az előző tutoriálban is az onClientResourceStart-ot hívtuk segítségül, hogy elvégezze ezt a feladatot.
-- when the resource is started, create the GUI and hide it addEventHandler("onClientResourceStart",getResourceRootElement(getThisResource()), function() createVehicleSelection() end )
A GUI megjelenítése
Az előző tutoriállal ellentétben itt azt akarjuk, hogy a játékos képes legyen az ablakot megnyitni, amikor csak szeretné, és ne akkor, amikor a resource elindul. Így hozzáadhatunk egy parancsot ennek elvégzéséhez.
-- create the function that will show the window function showVehicleSelection() -- if the window isnt visible, show it if not guiGetVisible(windowVehicleSelection) then guiSetVisible(windowVehicleSelection,true) showCursor(true,true) end end -- add the command /vehicleselection and set it to call the showVehicleSelection function addCommandHandler("vehicleselection",showVehicleSelection)
A Gridlist feltöltése egy táblából
Most, hogy van egy alap GUI-nk, fel is kell töltenünk a gridlistet az összes járművünkel.
Ezt kezdésként egy egyszerű client oldali táblával fogjuk megcsinálni. A tutoriál későbbi részében ezt a módszert kibővítjük egy .xml fájl használatával az információk tárolásához. Kezdésként létrehozunk egy táblát, mely tartalmazza a kiválasztott járműveket, amelyeket majd le tudunk spawnolni.
A tábla tartalmazza a járműveket ebben a formában ["description"] = vehicle_id :
-- create the table and fill it with a selection of vehicle ids and names local vehicleSelectionTable = { ["Sparrow"] = 469, ["Stuntplane"] = 513, ["BF-400"] = 581, ["Freeway"] = 463, ["Speeder"] = 452, ["Jester"] = 559, ["Sabre"] = 475, ["Police Ranger"] = 599, ["Utility Van"] = 552, ["Tug"] = 583 }
Helyezze ezt a kódot a script legtetejére (nem kell, hogy egy funkción belül legyen).
Most megírhatjuk a "populateGridlist" function, amely feltölti a Gridlist-et a táblázatban szereplő összes járművel. Ehhez egyszerűen csak végig kell mennünk a táblában szereplő értékeken, és hozzáadni a gridlist-hez. Valamint beállítjuk a rejtett adatokat a guiGridListSetItemData használatával, hogy tároljuk a járművek id-jét:
function populateGridlist() -- loop through the table for name,vehicle in pairs(vehicleSelectionTable) do -- add a new row to the gridlist local row = guiGridListAddRow(gridlistVehicleSelection) -- set the text in the first column to the vehicle name guiGridListSetItemText(gridlistVehicleSelection,row,1,name,false,false) -- set the text in the second column to the vehicle type guiGridListSetItemText(gridlistVehicleSelection,row,2,getVehicleType(vehicle),false,false) -- set the data for gridlist slot as the vehicle id guiGridListSetItemData(gridlistVehicleSelection,row,1,tostring(vehicle)) end end
A klikk kezelése
Most, hogy elkészült a GUI-nk, észlelnünk is kell minden kattintást a "Create" gombon. Már hozzácsatoltuk az onClientGUIClick event-et a buttonCreate-hez, így most meg kell írjuk azt a function-t, ami meghívja. Ebben a function-ben elvégzünk néhány alapvető hibaellenőrzést, mint például, hogy meggyőződjünk arról, hogy a jármű az a listából lett-e kiválasztva. Ezután használhatunk néhány számítási feladatot, hogy megtaláljuk a játékos pozícióját, majd elküldjök ezt az infórmációt a szervernek, hogy lespawnolja a járművet a játékos elé (triggerServerEvent használatával)
function createVehicleHandler(button,state) if button == "left" and state == "up" then -- get the selected item in the gridlist local row,col = guiGridListGetSelectedItem(gridlistVehicleSelection) -- if something is selected if row and col and row ~= -1 and col ~= -1 then -- get the vehicle id data from the gridlist that is selected local selected = guiGridListGetItemData(gridlistVehicleSelection,row,col) -- make sure the vehicle id is a number not a string selected = tonumber(selected) -- get the players position and rotation local rotz = getPedRotation(getLocalPlayer()) local x,y,z = getElementPosition(getLocalPlayer()) -- find the position directly infront of the player x = x + ( math.cos ( math.rad ( rotz+90 ) ) * 3) y = y + ( math.sin ( math.rad ( rotz+90 ) ) * 3) if selected and x and y and z then -- trigger the server triggerServerEvent("createVehicleFromGUI",getRootElement(),selected,x,y,z) -- hide the gui and the cursor guiSetVisible(windowVehicleSelection,false) showCursor(false,false) else outputChatBox("Invalid arguments.") end else -- otherwise, output a message to the player outputChatBox("Please select a vehicle.") end end end
A jármű létrehozása
Ezen a ponton az összes szükséges client oldali kódunk már megvan, szóval nyisson meg egy szerver oldalu .lua fájlt, amivel majd dolgozni fog.
Mindenekelőtt a szerver oldalon meg kell adnunk egy egyedi event-et, amit már meghívtunk a client oldalról. Ezt megtehetjük az addEvent és az addEventHandler használatával. Végezetűl szükségünk lesz egy pici function-re, hogy létrehozzuk a járművet:
function createMyVehicle(vehicleid,x,y,z) -- check all the arguments exist if vehicleid and x and y and z then createVehicle(vehicleid,x,y,z) end end addEvent("createVehicleFromGUI",true) addEventHandler("createVehicleFromGUI",root,createMyVehicle)
Vegye figyelembe a "root" változó használatát. Ez egy MTA változó, amely tartalmazza a root elemet; minden resource-nak van egy.
Ezzel be is fejeztük a tutoriál első részét. Mostanra rendelkeznie kell egy alap, működő jármű spawnoló script-el.
A második részben megnézzük a gridlist adatok kezelhetőségét, és a client oldali xml fájlok olvasását.
A kód bővítése
Ez a rész számos módon fogja részletezni a már meglévő kódunk fejlesztését.
Adatok importálása
Egy nagy előrehaladás az előző módszerhez képest, hogy külső fájlokat használunk a jármű információinak tárolásához, ez lehetővé teszi a számunkra, hogy gyorsabban és egyszerűbben kezeljünk nagy mennyiségű adatokat. Ebben a tutoriálban clinet oldali xml fájlt fogunk használni az információ tárolásához.
Mindenek előtt létre kell hoznia egy xml fájlt, szóval keresse meg a resource könyvtárát, és hozzon létre egy új vehicles.xml fájlt:
Ennek a tutoriálnak a céljából csak egy kis részét fogjuk felvenni a teljes járműkészletből, azonban a fájl könnyen bővíthető, így a későbbiekben majd az összeset hozzá fogjuk tudni adni
Ha még nem biztos az xml adatok felépítésében, akkor böngézhet a MTA xml function-ok között a további információkért.
<root> <group type="Bikes"> <vehicle id="581" name="BF-400"/> <vehicle id="463" name="Freeway"/> <vehicle id="481" name="BMX"/> </group> <group type="Boats"> <vehicle id="472" name="Coastguard"/> <vehicle id="452" name="Speeder"/> </group> <group type="Helicopters"> <vehicle id="487" name="Maverick"/> <vehicle id="469" name="Sparrow"/> </group> <group type="Planes"> <vehicle id="593" name="Dodo"/> <vehicle id="513" name="Stuntplane"/> </group> <group type="Sports Cars"> <vehicle id="565" name="Flash"/> <vehicle id="559" name="Jester"/> <vehicle id="477" name="ZR-350"/> </group> <group type="2-Door"> <vehicle id="474" name="Hermes"/> <vehicle id="475" name="Sabre"/> </group> <group type="Emergency"> <vehicle id="416" name="Ambulance"/> <vehicle id="599" name="Police ranger"/> </group> <group type="Industrial"> <vehicle id="573" name="Dune"/> <vehicle id="552" name="Utility van"/> </group> <group type="Misc"> <vehicle id="457" name="Caddy"/> <vehicle id="583" name="Tug"/> </group> </root>
Miután létrehozta a fájlt, ne felejtse el hozzáadni a resource meta.xml fájljához a megfelelő client taggal.
Vegye figyelembe a "type" tagot. Ez a groupon belüli járművek tetszőleges leírása, ami átnevezhető, vagy hozzáadható bármilyen szöveg. Hasonlóképenn a vehicle "name" tag is csak egy szöveges leírás a járműről, és nem kell, hogy a jármű valódi neve legyen.
Most, hogy rendelkezünk az adatokkal, importálni tudjuk a játékba és megjeleníteni a GUI-n. Ha követte ezt a tutoriált az első résztől kezdve, akkor ez az új kód a 'populateGridlist' function-ben lévő kódot cseréli ki. Először be kell töltenünk a fájlt:
function populateGridlist() -- load the file and save the root node value it returns local rootnode = xmlLoadFile("vehicles.xml") -- check that the file exists and has been correctly loaded if rootnode then -- unload the xml file xmlUnloadFile(rootnode) end end
Mindig bizonyosodjon meg arróla, hogy lezárta a fájlt, mikor már végeztél a szerkesztésével.
Most már elkezdhetjük az adatok gyűjtését, és hozzáadhatjuk a gridlist-hez.
We can do this by looping through the xml data (in a similar manner to looping through the vehicle table earlier in this tutorial) and adding each entry into the list.
function populateGridlist() local rootnode = xmlLoadFile("vehicles.xml") if rootnode then -- first, we find every "group" node (they are direct children of the root) for _,group in ipairs(xmlNodeGetChildren(rootnode)) do -- add a new row to the gridlist local row = guiGridListAddRow(gridlistVehicleSelection) -- get the group name local name = xmlNodeGetAttribute(group,"type") -- set the text in the first column to the group type and indicate it is a section ('true') -- (sections on gridlists show up in bold and cannot be clicked) guiGridListSetItemText(gridlistVehicleSelection,row,1,name,true,false) -- then, for every group that we find, loop all of its children (the vehicle nodes) -- and add them into the gridlist for _,vehicle in ipairs(xmlNodeGetChildren(group)) do -- add a new row to the gridlist row = guiGridListAddRow(gridlistVehicleSelection) -- get the vehicle name and id name = xmlNodeGetAttribute(vehicle,"name") local id = xmlNodeGetAttribute(vehicle,"id") -- set the text in the first column to the vehicle name guiGridListSetItemText(gridlistVehicleSelection,row,1,name,false,false) -- set the text in the second column to the vehicle type guiGridListSetItemText(gridlistVehicleSelection,row,2,getVehicleType(tonumber(id)),false,false) -- finally, set the vehicle id as data so we can access it later guiGridListSetItemData(gridlistVehicleSelection,row,1,tostring(id)) end end -- unload the xml file now that we are finished xmlUnloadFile(rootnode) end end
Figyelje meg a for ciklus xmlNodeGetChildren függvénnyel való használatát a kódban. Ez sok időt, és energiát takarít meg nekünk, mivel nem kell manuálisan megírnunk minden egyes group-ot és járművet a listába.
Mostanra már rendelkeznie kell egy teljesen működő járműválasztási menüvel, amely minden adatot a client oldali xml fájlból olvas. Következőnek pedig megnézzük, hogy hogyan csináljuk meg a megjelenését a lenyíló részeknek a gridlistben.
A Gridlist megnyitása/összecsukása
A gridlistben lévő lenyitható menük lehetővé teszik, hogy egy sokkal átláthatóbb panelt készítsünk. Ezzel helyet takarít meg a képernyőn, és sokkal kényelmesebbé teszi az egész menüt.
Az adatok betöltése
Ennek a hatásnak az eléréshez (mivel ez nem a gridlisták természetes tulajdonsága) a járműinformációkat egy táblázatba kell betöltenünke (az xml fájlból), ahelyett, hogy közvetlenül a gridlist-be helyeznénk. Ha követte ezt a tutoriált az első résztől kezdve, akkor ez az új kód a 'populateGridlist' function-ben lévő kódot cseréli ki
Ez is ugyanúgy működik, mint az előző példában szereplő xml betöltési rész, csak ahelyett, hogy az adatokat a grlidlist-be mentenénk, helyette egy táblába mentjük el. Beállítjuk a "header" elementData-t is a gridlist cellákban, hogy meg tudjuk különböztetni, hogy melyik elem egy "group" és melyik egy jármű.
function populateGridlist() local rootnode = xmlLoadFile("vehicles.xml") -- create a blank global table to store our imported data vehicleTable = {} if rootnode then for _,group in ipairs(xmlNodeGetChildren(rootnode)) do -- create an entry in the gridlist for every vehicle "group" local row = guiGridListAddRow(gridlistVehicleSelection) local name = xmlNodeGetAttribute(group,"type") guiGridListSetItemText(gridlistVehicleSelection,row,1,name,false,false) -- add an entry containing the group name into the table vehicleTable[name] = {} -- we will use the custom data "header" to indicate that this entry can be expanded/collapsed guiGridListSetItemData(gridlistVehicleSelection,row,1,"header") -- then, for every group that we find, loop all of its children (the vehicle nodes) and store them in a table for _,vehicle in ipairs(xmlNodeGetChildren(group)) do local vname = xmlNodeGetAttribute(vehicle,"name") local id = xmlNodeGetAttribute(vehicle,"id") -- insert both the vehicle id and the vehicle description into the table table.insert(vehicleTable[name],{id,vname}) end end -- set element data on the gridlist so we know which group is currently showing setElementData(gridlistVehicleSelection,"expanded","none") -- unload the xml file now that we are finished xmlUnloadFile(rootnode) end end
Most, hogy tároltuk az összes adatot, gyorsan kezelni is tudjuk, amikor szükséges.
Dupla kattintás kezelése
Annak engedélyezéséhez, hogy a játékos egyszerűen tudjon duplán kattintani a group nevére, hogy azt lenyissa/összecsukja az onClientGUIDoubleClick event-et kell használunk. Ha ezt a gridlist-hez csatoljuk, akkor az összes dupla kattintást a group neveken észlelni tudjuk:
-- attach the onClientGUIDoubleClick event to the gridlist and set it to call processDoubleClick addEventHandler("onClientGUIDoubleClick",gridlistVehicleSelection,processDoubleClick,false)
Adja hozzá ezt a sort a createVehicleSelection-hoz, a létrehozott gridlist után.
Mivel a gridlist-ek nem önálló GUI elemek, ezért rögzítenünk kell az összes kattintást a gridlist-ről, majd feldolgozni, hogy kitudjuk szűrűni azt, amelyiket nem akarjuk figyelembe venni.
Ezt megtehetjük egy kiválasztott elem ellenőrzésével, majd ellenőrizzük, hogy az elem az a mi egyik "group" értékünk (a korábban említett "header" adat használatával):
function processDoubleClick(button,state) if button == "left" and state == "up" then local row,col = guiGridListGetSelectedItem(gridlistVehicleSelection) -- if something in the gridlist has been selected if row and col and row ~= -1 and col ~= -1 then -- if it is a header if guiGridListGetItemData(gridlistVehicleSelection,row,col) == "header" then local selected = guiGridListGetItemText(gridlistVehicleSelection,row,col) -- call the function to collapse or expand the menu and pass which section it is as an argument changeGridlistState(selected) end end end end
Miután leszűkítettük a dupla kattintás, hogy csak a header-en történő kattintásra reagáljon, azután megfelelően tudjuk lenyitni/összecsuk őket. Ez könnyen megtehető úgy, hogy új szövegértékeket adunk meg a gridlistben az újonnan lenyitott/összezárt csoportot figyelembe véve, ami azt a meggyőző illúziót kelti, hogy a menüelemek lenyitothatók/összezárhatók.
Ne feledje, hogy a következő kódnak a nagy része újra felhasználásra került a tutorial előző részeiből. Bár általában ez rossz gyakorlat a kód részleteinek az ilyen módon történő másolása, ennek a példának a célja, hogy sokkal könnyebb legyen ezt megérteni.
function changeGridlistState(group) if group then -- if the group is already expanded, we want to collapse it if getElementData(gridlistVehicleSelection,"expanded") == group then -- first, we clear all previous data from the gridlist guiGridListClear(gridlistVehicleSelection) -- now, loop all the group entries in the vehicle table that we created earlier for group,_ in pairs(vehicleTable) do -- add each group to the list and mark them with "header" local row = guiGridListAddRow(gridlistVehicleSelection) guiGridListSetItemText(gridlistVehicleSelection,row,1,group,false,false) guiGridListSetItemData(gridlistVehicleSelection,row,1,"header") end -- update the data to indicate that no groups are currently expanded setElementData(gridlistVehicleSelection,"expanded","none") -- otherwise, we want to expand it else guiGridListClear(gridlistVehicleSelection) local row = guiGridListAddRow(gridlistVehicleSelection) guiGridListSetItemText(gridlistVehicleSelection,row,1,group,false,false) guiGridListSetItemData(gridlistVehicleSelection,row,1,"header") -- loop every vehicle in the specified groups table for _,vehicle in ipairs(vehicleTable[group]) do -- add them to the gridlist and set the vehicle id as data row = guiGridListAddRow(gridlistVehicleSelection) -- format a "-" into the string to make it visually easier to distinguish between groups and vehicles guiGridListSetItemText(gridlistVehicleSelection,row,1,"- "..vehicle[2],false,false) guiGridListSetItemText(gridlistVehicleSelection,row,2,getVehicleType(tonumber(vehicle[1])),false,false) guiGridListSetItemData(gridlistVehicleSelection,row,1,tostring(vehicle[1])) end -- update the data to indicate which group is currently expanded setElementData(gridlistVehicleSelection,"expanded",group) end end end
Ezzel be is fejeztük a tutoriál második részét.
A további GUI tutoriálokért látogassa meg a GUI készítése - Tutorial 2 (Gates and Keypads) oldalt.
Fordította
2018.11.25. Surge