HU/Scripting the GUI - Tutorial 1: Difference between revisions
Line 319: | Line 319: | ||
===A Gridlist megnyitása/lenyílása=== | ===A Gridlist megnyitása/lenyílá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. | Ezzel helyet takarít meg a képernyőn, és sokkal kényelmesebbé teszi az egész menüt. | ||
Revision as of 11:12, 24 November 2018
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
Note the use of for loops in the code with xmlNodeGetChildren. 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/lenyílá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.
Loading the data
To be able to achieve this effect (as it is not a natural feature of gridlists) we will need to load the vehicle information into a table (from the xml file), rather than directly including it all in the gridlist. If you are following on from a previous section of this tutorial, this new code will replace what is inside your existing 'populateGridlist' function.
This works in much the same way the previous xml loading example does, only this time instead of saving the data into the gridlist, we save it into a table entry instead. We also set the custom data "header" on the gridlist slots to denote which gridlist entries are our "group" entries and which are vehicles.
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
Now that we have all the data stored, we can quickly manipulate it when neccessary.
Managing the double click
To enable the player to simply double click on a group name to expand/collapse it we need to use the onClientGUIDoubleClick event. If we attach it to the gridlist, we can then catch any double clicks on the group names in it:
-- attach the onClientGUIDoubleClick event to the gridlist and set it to call processDoubleClick addEventHandler("onClientGUIDoubleClick",gridlistVehicleSelection,processDoubleClick,false)
Add this line of code in your createVehicleSelection, after the gridlist has been created.
As the gridlist items are not separate GUI elements, we must capture all clicks from anywhere on the gridlist then process them to filter out the ones we do not want.
This can be done by checking if an item is selected, then checking if the item is one of our "group" values (using our previously mentioned "header" data):
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
Once we have narrowed down the double clicks to only those done on our group headers, we can expand/collapse them appropriately. This can be done by simply setting new text values in the gridlist according to the newly expanded/collapsed group, which creates the convincing illusion of collapsable menus.
Note that much of the following code is reused from previous areas of this tutorial. While it is generally bad practice to clone sections of code in this way, for the purposes of this example it is far easier to understand.
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
This comletes the second section of the tutorial.
For further GUI tutorials, see Tutorial 2 (Gates and Keypads)