DE/Introduction to Scripting the GUI

From Multi Theft Auto: Wiki
Jump to navigation Jump to search

Eine wichtige Funktion in MTA:SA ist die Möglichkeit, eine angepasste grafische Benutzeroberfläche (GUI) zu erstellen. Die GUI besteht aus Fenstern, Schaltflächen, Bearbeitungsfeldern, Kontrollkästchen... Fast alle Standard-Formkomponenten in grafischen Umgebungen. Sie können angezeigt werden, während der Benutzer im Spiel ist, und für Ein- und Ausgaben anstelle von traditionellen Befehlen verwendet werden.

Admin Console GUI

A tutorial to make a login window

In diesem Tutorial erstellen wir ein einfaches Anmeldefenster mit zwei Eingabefeldern und einer Schaltfläche. Das Fenster erscheint, wenn der Spieler dem Spiel beitritt, und sobald der Button angeklickt wird, wird der Spieler gespawnt. Das Tutorial setzt den Spielmodus fort, den wir in Introduction to Scripting (If you have used the Introduction to Scripting verwendet haben, müssen Sie die spawnPlayer-Zeile in der "joinHandler"-Funktion in Ihrem Code entfernen oder auskommentieren, da wir sie in diesem Tutorial durch eine GUI-Alternative ersetzen werden)'. Wir werden auch einen Blick auf die clientseitige Skripterstellung werfen.

Draw the window

Die gesamte grafische Benutzeroberfläche muss client-seitig erstellt werden. Es ist auch eine gute Praxis, alle Client-Skripte in einem separaten Ordner aufzubewahren.

Wechseln Sie in das Verzeichnis /Ihr MTA Server/mods/deathmatch/resources/myserver/ und erstellen Sie einen Ordner namens "client". Erstellen Sie im Verzeichnis /client/ eine Textdatei und nennen Sie sie "gui.lua".

In dieser Datei werden wir eine Funktion schreiben, die das Fenster zeichnet. Um ein Fenster zu erstellen, werden wir guiCreateWindow verwenden:

function createLoginWindow()
	-- define the X and Y positions of the window
	local X = 0.375
	local Y = 0.375
	-- define the width and height of the window
	local Width = 0.25
	local Height = 0.25
	-- create the window and save its element value into the variable 'wdwLogin'
	-- click on the function's name to read its documentation
	wdwLogin = guiCreateWindow(X, Y, Width, Height, "Please Log In", true)
end

Relative and Absolute

Beachten Sie, dass das letzte Argument, das im obigen Beispiel an guiCreateWindow übergeben wird, true ist. Dies bedeutet, dass die Koordinaten und Abmessungen des Fensters relativ sind, d.h. sie sind ein Prozentsatz der gesamten Bildschirmgröße. Das heißt, wenn die linke Seite des Bildschirms 0 und die rechte Seite 1 ist, würde eine X-Position von 0,5 den Mittelpunkt des Bildschirms darstellen. Wenn der obere Teil des Bildschirms 0 und der untere Teil 1 ist, würde eine Y-Position von 0,2 20 % des unteren Teils des Bildschirms ausmachen. Die gleichen Prinzipien gelten auch für Breite und Höhe (wobei ein Breitenwert von 0,5 bedeutet, dass das Fenster halb so breit wie der Bildschirm ist).

Die Alternative zur Verwendung relativer Werte ist die Verwendung absoluter (durch Übergabe von false anstelle von true an guiCreateWindow). Absolute Werte werden als die Gesamtzahl der Pixel von der linken oberen Ecke des übergeordneten Elements berechnet (wenn kein übergeordnetes GUI-Element angegeben wird, ist das übergeordnete Element der Bildschirm selbst). Wenn wir von einer Bildschirmauflösung von 1920x1200 ausgehen, wobei der äußerste linke Rand des Bildschirms 0 Pixel und der äußerste rechte Rand 1920 Pixel beträgt, stellt eine X-Position von 960 den Mittelpunkt des Bildschirms dar. Wenn der obere Rand des Bildschirms 0 Pixel und der untere Rand 1200 Pixel beträgt, würde eine Y-Position von 20 20 Pixel unter dem oberen Rand des Bildschirms liegen. Die gleichen Prinzipien gelten auch für Breite und Höhe (wobei ein Breitenwert von 50 bedeutet, dass das Fenster 50 Pixel breit sein wird). Sie können guiGetScreenSize und ein wenig Mathematik verwenden, um bestimmte absolute Positionen zu berechnen.

Die Unterschiede zwischen der Verwendung von relativen und absoluten Werten sind ganz einfach: Eine mit absoluten Werten erstellte GUI hat immer genau dieselbe Pixelgröße und Position, während eine mit relativen Werten erstellte GUI immer einen Prozentsatz der Größe des übergeordneten Elements hat.

Absolute ist in der Regel einfacher zu pflegen, wenn der Code von Hand bearbeitet wird, aber die Wahl des Typs hängt von der Situation ab, in der Sie ihn verwenden.

Für die Zwecke dieser Einführung werden wir relative Werte verwenden.

Adding the components

Als Nächstes fügen wir die Textbeschriftungen (mit den Bezeichnungen "username:" und "password:"), die Bearbeitungsfelder (für die Eingabe Ihrer Daten) und eine Schaltfläche für die Anmeldung hinzu.

Zum Erstellen von Schaltflächen verwenden wir guiCreateButton und zum Erstellen von Eingabefeldern guiCreateEdit:

Beachten Sie, dass wir jetzt mehr Code für unsere bestehende Funktion 'createLoginWindow' schreiben. Dies ist keine neue Funktion und soll das ersetzen, was Sie bereits haben.

function createLoginWindow()
	local X = 0.375
	local Y = 0.375
	local Width = 0.25
	local Height = 0.25
	wdwLogin = guiCreateWindow(X, Y, Width, Height, "Please Log In", true)
	
	-- define new X and Y positions for the first label
	X = 0.0825
	Y = 0.2
	-- define new Width and Height values for the first label
	Width = 0.25
	Height = 0.25
	-- create the first label, note the final argument passed is 'wdwLogin' meaning the window
	-- we created above is the parent of this label (so all the position and size values are now relative to the position of that window)
	guiCreateLabel(X, Y, Width, Height, "Username", true, wdwLogin)
	-- alter the Y value, so the second label is slightly below the first
	Y = 0.5
	guiCreateLabel(X, Y, Width, Height, "Password", true, wdwLogin)
	

	X = 0.415
	Y = 0.2
	Width = 0.5
	Height = 0.15
	edtUser = guiCreateEdit(X, Y, Width, Height, "", true, wdwLogin)
	Y = 0.5
	edtPass = guiCreateEdit(X, Y, Width, Height, "", true, wdwLogin)
	-- set the maximum character length for the username and password fields to 50
	guiEditSetMaxLength(edtUser, 50)
	guiEditSetMaxLength(edtPass, 50)
	
	X = 0.415
	Y = 0.7
	Width = 0.25
	Height = 0.2
	btnLogin = guiCreateButton(X, Y, Width, Height, "Log In", true, wdwLogin)
	
	-- make the window invisible
	guiSetVisible(wdwLogin, false)
end

Beachten Sie, dass jede erstellte GUI-Komponente ein Kind des Fensters ist. Dies geschieht durch die Angabe des übergeordneten Elements (in diesem Fall wdwLogin) beim Erstellen der Komponente.

Dies ist sehr nützlich, denn es bedeutet nicht nur, dass alle Komponenten an das Fenster angehängt sind und sich mit ihm bewegen, sondern auch, dass alle Änderungen, die am übergeordneten Fenster vorgenommen werden, auf die untergeordneten Komponenten übertragen werden. Zum Beispiel können wir jetzt die gesamte grafische Benutzeroberfläche, die wir gerade erstellt haben, ausblenden, indem wir das Fenster einfach ausblenden:

guiSetVisible(wdwLogin, false) --hides all the GUI we made so we can show them to the player at the appropriate moment. 

Using the function we wrote

Die Funktion createLoginWindow ist jetzt vollständig, aber sie wird nichts tun, bis wir sie aufrufen. Es wird empfohlen, alle GUI zu erstellen, wenn die Client-Ressource startet, sie auszublenden und sie dem Spieler später bei Bedarf zu zeigen. Deshalb schreiben wir einen Event-Handler für "onClientResourceStart", um das Fenster zu erstellen:

-- attach the event handler to the root element of the resource
-- this means it will only trigger when its own resource is started
addEventHandler("onClientResourceStart", getResourceRootElement(), 
	function ()
		createLoginWindow()
	end
)	

Da es sich um ein Anmeldefenster handelt, müssen wir das Fenster nun anzeigen, wenn der Spieler dem Spiel beitritt. Dies kann mit demselben Ereignis "onClientResourceStart" geschehen, so dass wir den obigen Code so ändern können, dass das Fenster angezeigt wird:

Beachten Sie, dass wir jetzt mehr Code für unseren bestehenden 'onClientResourceStart'-Handler schreiben. Dies ist kein neuer Event-Handler und soll das ersetzen, was Sie bereits haben.

addEventHandler("onClientResourceStart", getResourceRootElement(), 
	function ()
		-- create the log in window and its components
		createLoginWindow()

		-- output a brief welcome message to the player
                outputChatBox("Welcome to My MTA:SA Server, please log in.")

		-- if the GUI was successfully created, then show the GUI to the player
	        if (wdwLogin ~= nil) then
			guiSetVisible(wdwLogin, true)
		else
			-- if the GUI hasn't been properly created, tell the player
			outputChatBox("An unexpected error has occurred and the login GUI has not been created.")
	        end 

		-- enable the player's cursor (so they can select and click on the components)
	        showCursor(true)
		-- set the input focus onto the GUI, allowing players (for example) to press 'T' without the chatbox opening
	        guiSetInputEnabled(true)
	end
)	

Beachten Sie, dass wir eine einfache Sicherheitsprüfung durchführen, bevor wir das Fenster sichtbar machen. Sollte also der unwahrscheinliche Fall eintreten, dass das Fenster nicht erstellt wurde, d. h. wdwLogin kein gültiges Element ist, erhalten wir keine Fehlermeldung und informieren den Spieler lediglich darüber, was passiert ist. Im nächsten Schritt werden wir die Schaltflächenfunktionalität für die Login-Schaltfläche erstellen.

Scripting the button

Nun, da wir unsere grafische Benutzeroberfläche erstellt und dem Spieler gezeigt haben, müssen wir sie zum Laufen bringen.

Detecting the click

Wenn der Spieler auf einen beliebigen Teil der GUI klickt, wird das Ereignis "onClientGUIClick" für die GUI-Komponente ausgelöst, auf die Sie geklickt haben. Dies ermöglicht es uns, Klicks auf die GUI-Elemente, die wir verwenden möchten, leicht zu erkennen. Zum Beispiel können wir das Ereignis an die Schaltfläche btnLogin anhängen, um alle Klicks darauf zu erfassen:

-- attach the event onClientGUIClick to btnLogin and set it to trigger the 'clientSubmitLogin' function
addEventHandler("onClientGUIClick", btnLogin, clientSubmitLogin, false)

Beachten Sie, dass das letzte übergebene Argument "false" ist. Dies zeigt an, dass das Ereignis nur direkt auf btnLogin ausgelöst wird, nicht wenn sich das Ereignis im Baum nach oben oder unten ausgebreitet hat. Setzt man dies auf "true", während man sich an GUI-Elemente anhängt, bedeutet dies, dass ein Klick auf ein beliebiges Element im selben Zweig dieses Ereignis auslöst.

Diese Codezeile kann nun innerhalb der Funktion createLoginWindow eingefügt werden. Es ist ein häufiger Fehler, zu versuchen, Ereignisse an nicht existierende GUI-Elemente anzuhängen. Stellen Sie also sicher, dass Sie Ihre Ereignisse immer after der Erstellung des GUI-Elements (in diesem Fall die Schaltfläche) anhängen:

Beachten Sie, dass wir jetzt mehr Code für unsere bestehende Funktion "createLoginWindow" schreiben.

function createLoginWindow()
	-- create all our GUI elements
	...

	-- now add our onClientGUIClick event to the button we just created
	addEventHandler("onClientGUIClick", btnLogin, clientSubmitLogin, false)

Managing the click

Nun, da wir erkennen können, wann der Spieler auf die Schaltfläche klickt, müssen wir Code schreiben, um zu steuern, was passiert, wenn der Spieler klickt. In unserem onClientGUIClick-Ereignis-Handle haben wir ihm gesagt, dass es die Funktion clientSubmitLogin aufrufen soll, wenn btnLogin angeklickt wird. Daher können wir jetzt die Funktion clientSubmitLogin verwenden, um zu steuern, was passiert, wenn die Schaltfläche angeklickt wird:

-- create the function and define the 'button' and 'state' parameters
-- (these are passed automatically by onClientGUIClick)
function clientSubmitLogin(button,state)
	-- if our login button was clicked with the left mouse button, and the state of the mouse button is up
	if button == "left" and state == "up" then
		-- move the input focus back onto the game (allowing players to move around, open the chatbox, etc)
		guiSetInputEnabled(false)
		-- hide the window and all the components
		guiSetVisible(wdwLogin, false)
		-- hide the mouse cursor
		showCursor(false)
	end
end

Wenn nun die Schaltfläche angeklickt wird, wird das Fenster ausgeblendet und alle Steuerelemente werden an den Spieler zurückgegeben. Als Nächstes werden wir dem Server mitteilen, dass der Spieler spawnen darf.

Triggering the server

Das Auslösen des Servers kann mit triggerServerEvent erfolgen. Damit können Sie vom Client aus ein bestimmtes Ereignis auf dem Server auslösen. Das Gleiche kann in umgekehrter Weise mit triggerClientEvent gemacht werden. Hier verwenden wir die Funktion triggerServerEvent, um unser eigenes benutzerdefiniertes Ereignis mit dem Namen "submitLogin" auf dem Server aufzurufen, das dann das Spawnen des Spielers serverseitig steuert.

Beachten Sie, dass wir jetzt mehr Code für unsere bestehende Funktion "clientSubmitLogin" schreiben. Dies ist keine neue Funktion und soll das ersetzen, was Sie bereits haben.

function clientSubmitLogin(button,state)
	if button == "left" and state == "up" then
		-- get the text entered in the 'username' field
		local username = guiGetText(edtUser)
		-- get the text entered in the 'password' field
		local password = guiGetText(edtPass)

		-- if the username and password both exist
		if username and username ~= "" and password and password ~= "" then
			-- trigger the server event 'submitLogin' and pass the username and password to it
			triggerServerEvent("submitLogin", getRootElement(), username, password)

			-- hide the gui, hide the cursor and return control to the player
			guiSetInputEnabled(false)
			guiSetVisible(wdwLogin, false)
			showCursor(false)
		else
			-- otherwise, output a message to the player, do not trigger the server
			-- and do not hide the gui
			outputChatBox("Please enter a username and password.")
		end
	end
end

Creating the serverside event

An diesem Punkt haben wir nun den gesamten Code, der auf der Client-Seite benötigt wird. Öffnen Sie also Ihre serverseitige Datei "script.lua" (aus der Introduction to Scripting) oder eine andere geeignete serverseitige Datei, mit der Sie arbeiten können.

Auf der Serverseite, erinnern Sie sich, dass wir den Spieler spawnen, sobald er sich anmeldet. Daher müssen wir zunächst das benutzerdefinierte Ereignis definieren, das wir zuvor auf dem Client verwendet haben. Dies kann mit addEvent und addEventHandler geschehen.

-- create our loginHandler function, with a username and password parameters (passed from the client GUI)
function loginHandler(username,password)

end

-- define our custom event, and allow it to be triggered from the client ('true')
addEvent("submitLogin",true)
-- add an event handler so that when submitLogin is triggered, the function loginHandler is called
addEventHandler("submitLogin",root,loginHandler)

Logging in

Jetzt haben wir eine Funktion, die über das benutzerdefinierte Ereignis "submitLogin" aufgerufen wird, und wir können mit dem Einloggen und dem Spawnen des Spielers beginnen, indem wir unsere Funktion "loginHandler" verwenden:

function loginHandler(username,password)
	-- check that the username and password are correct
	if username == "user" and password == "apple" then
		-- the player has successfully logged in, so spawn them
		if (client) then
			spawnPlayer(client, 1959.55, -1714.46, 10)
			fadeCamera(client, true)
                        setCameraTarget(client, client)
			outputChatBox("Welcome to My Server.", client)
		end
	else
		-- if the username or password are not correct, output a message to the player
		outputChatBox("Invalid username and password. Please re-connect and try again.",client)
        end			
end

addEvent("submitLogin",true)
addEventHandler("submitLogin",root,loginHandler)

Für die Zwecke dieses Tutorials wird ein sehr einfaches System für Benutzernamen und Passwörter gezeigt. Für eine umfassendere Alternative können Sie das Account-System oder eine MySQL-Datenbank verwenden.

Beachten Sie auch die Verwendung der Variable "client", eine interne Variable, die von MTA verwendet wird, um den Spieler zu identifizieren, der das Ereignis ausgelöst hat.


Vergessen Sie nicht, die neue Datei gui.lua in die meta.xml der Hauptressource aufzunehmen und sie als Client-Skript zu kennzeichnen:

<script src="client/gui.lua" type="client" />


Jetzt haben wir ein einfaches Anmeldefenster, das den Benutzernamen und das Passwort des Spielers prüft, wenn die Anmeldeschaltfläche angeklickt wird. Wenn sie korrekt sind, wird der Spieler automatisch eingeloggt.

Weitere Hilfe zur GUI finden Sie in den GUI tutorials.