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 opcionális 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 készítése
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