<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.multitheftauto.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Skyvzla</id>
	<title>Multi Theft Auto: Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.multitheftauto.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Skyvzla"/>
	<link rel="alternate" type="text/html" href="https://wiki.multitheftauto.com/wiki/Special:Contributions/Skyvzla"/>
	<updated>2026-05-28T13:16:02Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.39.3</generator>
	<entry>
		<id>https://wiki.multitheftauto.com/index.php?title=ZH-CN/%E8%84%9A%E6%9C%AC%E8%B0%83%E8%AF%95%E6%95%99%E7%A8%8B&amp;diff=50476</id>
		<title>ZH-CN/脚本调试教程</title>
		<link rel="alternate" type="text/html" href="https://wiki.multitheftauto.com/index.php?title=ZH-CN/%E8%84%9A%E6%9C%AC%E8%B0%83%E8%AF%95%E6%95%99%E7%A8%8B&amp;diff=50476"/>
		<updated>2017-02-19T05:37:37Z</updated>

		<summary type="html">&lt;p&gt;Skyvzla: /* 例 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;在编写脚本时，你会经常遇到脚本编写完成后不能执行或显示的问题。在本页面中我们通过一些方法定位脚本中的错误位置。&lt;br /&gt;
&lt;br /&gt;
==控制台调试==&lt;br /&gt;
MTA游戏内具有调试控制台（游戏按 '''F8''' 键即可打开），显示MTA函数或脚本的输出调试信息。但是，你必须拥有管理员权限才可以（你可以在acl.xml中添加管理员权限），你可以在控制台中输入 '''debugscript''' ''x'' 打开控制台，这里的 ''x'' 代表调试的等级，一共有3个级别，如下。&lt;br /&gt;
* '''1:''' 只输出错误信息&lt;br /&gt;
* '''2:''' 输出错误和警告信息&lt;br /&gt;
* '''3:''' 错误，警告和消息（消息指输出在控制台中的信息，比如服务器中玩家聊天 T ，dayz 除外）信息&lt;br /&gt;
一般情况下，建议使用调试等级2或3来进行调试，这样可以输出所有的脚本错误信息，以便修改和更正。如果你只是调试脚本错误，使用等级二即可（在游戏内控制台输入 '''debugscript 2'''）。&lt;br /&gt;
&lt;br /&gt;
===排错实例===&lt;br /&gt;
下面这串代码有两处错误。&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function SayHello(message, player)&lt;br /&gt;
    if (getPlayerName(player) == &amp;quot;Fedor&amp;quot;)&lt;br /&gt;
        outputChatbox(&amp;quot;Hello Fedor&amp;quot;)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onChatMessage&amp;quot;, root, SayHello)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
当这个脚本在启动时，debugscript 就会输出类似这样的信息：&lt;br /&gt;
:{{Debug info|Loading script failed: myResource\script.lua:2: 'then' expected near ´outputChatbox'}}&lt;br /&gt;
这说明脚本存在着语法错误，就不能执行。它输出了脚本相对于根目录的脚本路径，以便你找出是哪个脚本的错误。文件名后面会带着一个冒号和数，这个数字代表错误所在的行数，这对于代码比较多的脚本是非常有用的，你可以根据提示的行数进行脚本检测。清楚错误信息，我们就很容易看出是脚本  '''myResource''' 中的 '''script.lua''' 文件出现错误。我们现在已经清楚错误出现在哪里，就可以很容易的解决这个错误。&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function SayHello(message, player)&lt;br /&gt;
    if (getPlayerName(player) == &amp;quot;Fedor&amp;quot;) then&lt;br /&gt;
        outputChatbox(&amp;quot;Hello Fedor&amp;quot;)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onChatMessage&amp;quot;, root, SayHello)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
再次启动脚本，这次并没有出现任何错误提示，直到一个玩家在发出聊天信息后，调试控制台又出现了下面一条错误信息。&lt;br /&gt;
:{{Debug error|myResource\script.lua:2: attempt to call global 'outputChatbox' (a nil value)}}&lt;br /&gt;
This error means that the function '''outputChatbox''' is a nil value, that is - it doesn't exist! This is because the function is actually called [[outputChatBox]], not [[outputChatbox]] - take care of that capital B.:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function SayHello(message, player)&lt;br /&gt;
    if (getPlayerName(player) == &amp;quot;Fedor&amp;quot;) then&lt;br /&gt;
        outputChatBox(&amp;quot;Hello Fedor&amp;quot;)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onChatMessage&amp;quot;, root, SayHello)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Of course, this is just an example and I've only skimmed the surface of error messages. There are plenty of other messages and scenarios, but you should get the general idea.&lt;br /&gt;
&lt;br /&gt;
==Server &amp;amp; client debug logging==&lt;br /&gt;
====Server====&lt;br /&gt;
Navigate to: ''(MTA root folder)&amp;gt;server&amp;gt;mods&amp;gt;deathmatch''&lt;br /&gt;
&lt;br /&gt;
There are two nearly identical files:&lt;br /&gt;
&lt;br /&gt;
*The local.conf is the settings for the &amp;quot;host game&amp;quot; menu item in the main menu of MTA. This is a quick and easy way of starting a temporary server from inside the client. When the client is turned off the server will also turn off.&lt;br /&gt;
&lt;br /&gt;
*The mtaserver.conf is used when &amp;quot;MTA Server.exe&amp;quot; is executed from (MTA root folder)&amp;gt;server. This runs the server in its own console window which does not need the client to exist or to be run. This is can be useful if you're interested in hosting servers for a longer time period or if you want to experiment with a real world console.&lt;br /&gt;
&lt;br /&gt;
Depending on which way you host, you will want to edit those files. The settings in question are:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&amp;lt;!-- Specifies the location and name of the debugscript log file. If left blank, server won't be saving this file. --&amp;gt;&lt;br /&gt;
	&amp;lt;scriptdebuglogfile&amp;gt;logs/scripts.log&amp;lt;/scriptdebuglogfile&amp;gt; &lt;br /&gt;
	&lt;br /&gt;
	&amp;lt;!-- Specifies the level of the debugscript log file. Available values: 0, 1, 2, 3. When not set, defaults to 0. --&amp;gt;&lt;br /&gt;
	&amp;lt;scriptdebugloglevel&amp;gt;0&amp;lt;/scriptdebugloglevel&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You want to make sure you have a log name specified. Also you want to specify what level of errors will be logged. If it is 0 nothing will be logged. The levels were explained at the top of this article. If the logging level was changed to 3, then in this case all '''serverside''' script errors log to (MTA root folder)&amp;gt;server&amp;gt;mods&amp;gt;deathmatch&amp;gt;logs&amp;gt;scripts.log&lt;br /&gt;
&lt;br /&gt;
====Client====&lt;br /&gt;
Navigate to: ''(MTA root folder)&amp;gt;server&amp;gt;clientscript.log''&lt;br /&gt;
&lt;br /&gt;
This file logs all '''clientside''' script errors. This is set to log by default, no setup required.&lt;br /&gt;
&lt;br /&gt;
==Debug strategies==&lt;br /&gt;
There are several strategies that support finding errors, apart from going through the code. Most of them include outputting debug messages, with have different information depending on the situtation.&lt;br /&gt;
&lt;br /&gt;
===Useful functions===&lt;br /&gt;
There are some functions that may come in handy for debugging.&lt;br /&gt;
* [[outputDebugString]] or [[outputChatBox]] for outputting any information (use outputDebugString for technical output)&lt;br /&gt;
* [http://www.lua.org/manual/5.1/manual.html#pdf-tostring tostring()] on a variable to turn the value into a string. Useful if the value is not a number or string.&lt;br /&gt;
* [[getElementType]] to check the type of the MTA element.&lt;br /&gt;
* [[isElement]] to check if the MTA element exists.&lt;br /&gt;
&lt;br /&gt;
===Add debugmessages to check ''if'', ''when'' or ''how often'' a section of code is executed===&lt;br /&gt;
A typical example would be verify whether an ''if''-section is executed or not. To do that, just add any message you will recognize later within the ''if''-section.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
if (variable1 == variable2) then&lt;br /&gt;
    outputDebugString(&amp;quot;variable1 is the same as variable2!&amp;quot;)&lt;br /&gt;
    -- do anything&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Another application would be to check when variable values are modified. First search for all occurences of the variable being edited and add a message just beside it.&lt;br /&gt;
&lt;br /&gt;
===Add debugmessages to check the ''value'' of a variable===&lt;br /&gt;
Let's say you want to create a marker, but it doesn't appear at the position you expect it to be. The first thing you might want to do is check if the [[createMarker]] function is executed. But while doing this, you can also check the values being used in the [[createMarker]] function in one run.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
outputChatBox(&amp;quot;posX is: &amp;quot;..x..&amp;quot; posY is: &amp;quot;..y..&amp;quot; posZ is: &amp;quot;..z)&lt;br /&gt;
createMarker(x,y,z)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This would output all three variables that are used as coordinates for the marker. Assuming you read those from a map file, you can now compare the debug output to the desired values. The [http://www.lua.org/manual/5.1/manual.html#pdf-tostring tostring()] will ensure that the values of the variables can be concatenated (put together) as a string, even if it's a boolean value.&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
Imagine you created a [[Colshape|collision shape]] somewhere and you want an action to perform after the player stays for ten seconds inside it, you would do this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function colShapeHit(player)&lt;br /&gt;
	-- set a timer to output a message (could as well execute another function)&lt;br /&gt;
	-- store the timer id in a table, using the player as index&lt;br /&gt;
	colshapeTimer[player] = setTimer(outputChatBox,10000,1,&amp;quot;The player stayed 10 seconds in the colshape!&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onColShapeHit&amp;quot;, root, colShapeHit)&lt;br /&gt;
&lt;br /&gt;
function colShapeLeave(player)&lt;br /&gt;
	-- kill the timer when the player leaves the colshape&lt;br /&gt;
	killTimer(colshapeTimer[player])&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onColShapeLeave&amp;quot;, root, colShapeLeave)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
When a player enters the colshape, debugscript outputs the following message:&lt;br /&gt;
:{{Debug error|..[path]: attempt to index global 'colshapeTimer' (a nil value)}}&lt;br /&gt;
This means you tried to index a table that does not exist (because the table is a nil value, it doesn't exist). In the example above, this is done when storing the timer id in the table. We need to add a check if the table exists and if it does not exist we should create it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function colShapeHit(player)&lt;br /&gt;
	if (colshapeTimer == nil) then&lt;br /&gt;
		colshapeTimer = {}&lt;br /&gt;
	end&lt;br /&gt;
	-- set a timer to output a message (could as well execute another function)&lt;br /&gt;
	-- store the timer id in a table, using the player as index&lt;br /&gt;
	colshapeTimer[player] = setTimer(outputChatBox,10000,1,&amp;quot;The player stayed 10 seconds in the colshape!&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onColShapeHit&amp;quot;, root, colShapeHit)&lt;br /&gt;
&lt;br /&gt;
function colShapeLeave(player)&lt;br /&gt;
	-- kill the timer when the player leaves the colshape&lt;br /&gt;
	killTimer(colshapeTimer[player])&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onColShapeLeave&amp;quot;,root,colShapeLeave)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we will still receive a warning when a player enters the colshape, waits for the message and leaves it again:&lt;br /&gt;
&lt;br /&gt;
:{{Debug warning|[..]: Bad argument @ 'killTimer' Line: ..}}&lt;br /&gt;
&lt;br /&gt;
Except for that (we will talk about that later) everything seems to work fine. A player enters the colshape, the timer is started, if he stays the message occurs, if he leaves the timer is killed.&lt;br /&gt;
&lt;br /&gt;
===A more inconspicuous error===&lt;br /&gt;
But for some reason the message gets outputted twice when you stay in the colcircle while in a vehicle. Since it would appear some code is executed twice, we add debug messages to check this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function colShapeHit(player)&lt;br /&gt;
	if (colshapeTimer == nil) then&lt;br /&gt;
		colshapeTimer = {}&lt;br /&gt;
	end&lt;br /&gt;
	-- add a debug message&lt;br /&gt;
	outputDebugString(&amp;quot;colShapeHit&amp;quot;)&lt;br /&gt;
	-- set a timer to output a message (could as well execute another function)&lt;br /&gt;
	-- store the timer id in a table, using the player as index&lt;br /&gt;
	colshapeTimer[player] = setTimer(outputChatBox,10000,1,&amp;quot;The player stayed 10 seconds in the colshape!&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onColShapeHit&amp;quot;,getRootElement(),colShapeHit)&lt;br /&gt;
&lt;br /&gt;
function colShapeLeave(player)&lt;br /&gt;
	-- add a debug message&lt;br /&gt;
	outputDebugString(&amp;quot;colShapeLeave&amp;quot;)&lt;br /&gt;
	-- kill the timer when the player leaves the colshape&lt;br /&gt;
	killTimer(colshapeTimer[player])&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onColShapeLeave&amp;quot;,getRootElement(),colShapeLeave)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we notice that both handler functions get executed twice when we are in a vehicle, but only once when we are on-foot. It would appear the vehicle triggers the colshape as well. To confirm this theory, we ensure that the ''player'' variable actually holds a reference to a player element.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function colShapeHit(player)&lt;br /&gt;
	if (colshapeTimer == nil) then&lt;br /&gt;
		colshapeTimer = {}&lt;br /&gt;
	end&lt;br /&gt;
	-- add a debug message, with the element type&lt;br /&gt;
	outputDebugString(&amp;quot;colShapeHit &amp;quot;..getElementType(player))&lt;br /&gt;
	-- set a timer to output a message (could as well execute another function)&lt;br /&gt;
	-- store the timer id in a table, using the player as index&lt;br /&gt;
	colshapeTimer[player] = setTimer(outputChatBox,10000,1,&amp;quot;The player stayed 10 seconds in the colshape!&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onColShapeHit&amp;quot;,getRootElement(),colShapeHit)&lt;br /&gt;
&lt;br /&gt;
function colShapeLeave(player)&lt;br /&gt;
	-- add a debug message, with the element type&lt;br /&gt;
	outputDebugString(&amp;quot;colShapeLeave &amp;quot;..getElementType(player))&lt;br /&gt;
	-- kill the timer when the player leaves the colshape&lt;br /&gt;
	killTimer(colshapeTimer[player])&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onColShapeLeave&amp;quot;,getRootElement(),colShapeLeave)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The debug messages tell us that one of the ''player'' variables is a player and that the other one is a vehicle element. Since we only want to react when a player enters the colshape, we add an ''if'' that will end the execution of the function if it's '''not''' an player element.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function colShapeHit(player)&lt;br /&gt;
	if (colshapeTimer == nil) then&lt;br /&gt;
		colshapeTimer = {}&lt;br /&gt;
	end&lt;br /&gt;
	-- add a check for the element type&lt;br /&gt;
	if (getElementType(player) ~= &amp;quot;player&amp;quot;) then return end&lt;br /&gt;
	-- add a debug message, with the element type&lt;br /&gt;
	outputDebugString(&amp;quot;colShapeHit &amp;quot;..getElementType(player))&lt;br /&gt;
	-- set a timer to output a message (could as well execute another function)&lt;br /&gt;
	-- store the timer id in a table, using the player as index&lt;br /&gt;
	colshapeTimer[player] = setTimer(outputChatBox,10000,1,&amp;quot;The player stayed 10 seconds in the colshape!&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onColShapeHit&amp;quot;,getRootElement(),colShapeHit)&lt;br /&gt;
&lt;br /&gt;
function colShapeLeave(player)&lt;br /&gt;
	-- add a check for the element type&lt;br /&gt;
	if (getElementType(player) ~= &amp;quot;player&amp;quot;) then return end&lt;br /&gt;
	-- add a debug message, with the element type&lt;br /&gt;
	outputDebugString(&amp;quot;colShapeLeave &amp;quot;..getElementType(player))&lt;br /&gt;
	-- kill the timer when the player leaves the colshape&lt;br /&gt;
	killTimer(colshapeTimer[player])&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onColShapeLeave&amp;quot;,getRootElement(),colShapeLeave)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now the script should work as desired, but it will still output the warning mentioned above. This happens because the timer we try to kill when a player leaves the colshape will not exist anymore when it has reached the 10 seconds (and therefore executed after the 10th second has completed). There are different ways to get rid of that warning (since you know that the timer might not exist anymore and you only want to kill it if it exists). One way would be to check if the timer referenced in the table really exists. To do this, we need to use [[isTimer]], which we will use when we kill the timer:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
if (isTimer(colshapeTimer[player])) then&lt;br /&gt;
	killTimer(colshapeTimer[player])&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So the complete working code would be:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function colShapeHit(player)&lt;br /&gt;
	if (colshapeTimer == nil) then&lt;br /&gt;
		colshapeTimer = {}&lt;br /&gt;
	end&lt;br /&gt;
	-- add a check for the element type&lt;br /&gt;
	if (getElementType(player) ~= &amp;quot;player&amp;quot;) then return end&lt;br /&gt;
	-- add a debug message, with the element type&lt;br /&gt;
	outputDebugString(&amp;quot;colShapeHit &amp;quot;..getElementType(player))&lt;br /&gt;
	-- set a timer to output a message (could as well execute another function)&lt;br /&gt;
	-- store the timer id in a table, using the player as index&lt;br /&gt;
	colshapeTimer[player] = setTimer(outputChatBox,10000,1,&amp;quot;The player stayed 10 seconds in the colshape!&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onColShapeHit&amp;quot;,getRootElement(),colShapeHit)&lt;br /&gt;
&lt;br /&gt;
function colShapeLeave(player)&lt;br /&gt;
	-- add a check for the element type&lt;br /&gt;
	if (getElementType(player) ~= &amp;quot;player&amp;quot;) then return end&lt;br /&gt;
	-- add a debug message, with the element type&lt;br /&gt;
	outputDebugString(&amp;quot;colShapeLeave &amp;quot;..getElementType(player))&lt;br /&gt;
	-- kill the timer when the player leaves the colshape&lt;br /&gt;
	if (isTimer(colshapeTimer[player])) then&lt;br /&gt;
		killTimer(colshapeTimer[player])&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onColShapeLeave&amp;quot;,getRootElement(),colShapeLeave)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Debugging Performance Issues==&lt;br /&gt;
&lt;br /&gt;
If your server is using up more resources than it should or you just want to make sure your scripts are efficient, you can find the source of the issue by using a great tool that comes with the default resource package called [[Resource:Performancebrowser|performancebrowser]]. You can start it with ''''start performancebrowser''''. If it doesn't exist then you can get the latest resources from the [https://github.com/multitheftauto/mtasa-resources GitHub repository]. This tool provides an incredible amount of information for performance debugging. Memory leaks, element leaks and CPU intensive scripts are all easily findable via performancebrowser. If you use the ''''d'''' option in Lua timing you can see which functions are using up the CPU.&lt;br /&gt;
&lt;br /&gt;
To access performancebrowser you will need to go to your web browser and enter the address: http://serverIPHere:serverHTTPPortHere/performancebrowser/ Note that the / at the end is required. So for example: http://127.0.0.1:22005/performancebrowser/ You will then need to login with an in-game admin account or any account that has access to ''''resource.performancebrowser.http'''' and ''''resource.ajax.http''''. Most of the information you will need are in the categories Lua timing and Lua memory, look for values that are much higher than other values.&lt;br /&gt;
&lt;br /&gt;
===Examples of scripts that could cause performance problems===&lt;br /&gt;
&lt;br /&gt;
Adding data to a table but never removing it. This would take months/years before it causes a problem though.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local someData = {}&lt;br /&gt;
&lt;br /&gt;
function storeData()&lt;br /&gt;
    someData[source] = true&lt;br /&gt;
    -- There is no handling for when a player quits, this is considered a memory leak&lt;br /&gt;
    -- Using the Lua timing tab you can detect the RAM usage of each resource.&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerJoin&amp;quot;, root, storeData)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Element leaking is possible if you use temporary colshapes for whatever reason and may not destroy them. This would cause bandwidth, CPU and memory performance issues over time.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function useTemporaryCol()&lt;br /&gt;
    local col = createColCircle(some code here)&lt;br /&gt;
    if (normally this should happen) then&lt;br /&gt;
        destroyElement(col)&lt;br /&gt;
    end&lt;br /&gt;
    -- But sometimes it didn't so the script ended but the collision area remained and over time&lt;br /&gt;
    -- you may end up with hundreds to thousands of pointless collision areas. &lt;br /&gt;
    -- The Lua timing tab allows you to see the amount of elements each script has created.&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
High CPU usage resulting in the server FPS dropping so much that the server is unplayable. In under 24 hours this can create havoc on a very busy server. The amount of &amp;quot;refs&amp;quot; in the Lua timing detect this type of build up, surprisingly the Lua timing tab didn't help in this case but Lua memory did.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerJoin&amp;quot;, root, function()&lt;br /&gt;
    -- Code for joiner&lt;br /&gt;
    addEventHandler(&amp;quot;onPlayerQuit&amp;quot;, root, function()&lt;br /&gt;
        -- Code for when they have quit&lt;br /&gt;
        -- See the problem? It's bound to root which the event handler is being added again and again and again&lt;br /&gt;
    end)&lt;br /&gt;
end)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function uses up a lot of your CPU because whatever it does takes a long time. This is just some function that takes a long time to complete. Without performancebrowser you'd have no idea its the cause but with performancebrowser you can see that a resource is using lots of CPU in the Lua timing tab. If you then enter: ''''d'''' into the options edit box it will even tell you what file name and first line of the function that is using up so much CPU.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function someDodgyCode()&lt;br /&gt;
    for i=1, 100000 do&lt;br /&gt;
        -- some code&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[it:Guida al Debug]]&lt;br /&gt;
[[Category:Scripting Concepts]]&lt;br /&gt;
[[ru:Debugging]]&lt;br /&gt;
[[en:Debugging]]&lt;br /&gt;
[[Category:Tutorials]]&lt;/div&gt;</summary>
		<author><name>Skyvzla</name></author>
	</entry>
	<entry>
		<id>https://wiki.multitheftauto.com/index.php?title=Hibakeres%C3%A9s&amp;diff=50459</id>
		<title>Hibakeresés</title>
		<link rel="alternate" type="text/html" href="https://wiki.multitheftauto.com/index.php?title=Hibakeres%C3%A9s&amp;diff=50459"/>
		<updated>2017-02-14T12:49:45Z</updated>

		<summary type="html">&lt;p&gt;Skyvzla: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;在编写脚本时，你会经常遇到脚本编写完成后不能执行或显示的问题。在本页面中我们通过一些方法定位脚本中的错误位置。&lt;br /&gt;
&lt;br /&gt;
==控制台调试==&lt;br /&gt;
MTA features a built-in debug console that shows debug messages output from MTA functions or from scripts. Keeping it mind that you should have administrator access (unless you modify the ACL to change this), you can open the debug console by typing ''debugscript *x*'' in the console, where ''x'' is the debug level:&lt;br /&gt;
* '''1:''' only errors&lt;br /&gt;
* '''2:''' errors and warnings&lt;br /&gt;
* '''3:''' errors, warnings and info messages&lt;br /&gt;
By typing ''debugscript 3'' all messages are visible. Level 3 and level 2 are recommended for most occasions. You should have ''debugscript'' enabled whenever you are testing your scripts as it will help you easily detect and solve typos and other issues.&lt;br /&gt;
&lt;br /&gt;
===例===&lt;br /&gt;
下面这串代码有两处错误。&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function SayHello(message, player)&lt;br /&gt;
    if (getPlayerName(player) == &amp;quot;Fedor&amp;quot;)&lt;br /&gt;
        outputChatbox(&amp;quot;Hello Fedor&amp;quot;)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onChatMessage&amp;quot;, root, SayHello)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
When the script this piece of code is in is tried to be loaded, debugscript will output something similiar to this:&lt;br /&gt;
:{{Debug info|Loading script failed: myResource\script.lua:2: 'then' expected near ´outputChatbox'}}&lt;br /&gt;
This means the script could not be parsed because there was a syntax error. It shows the script path relative to the resource directory so you can identify to script where the error originated from. After the filename it shows a colon and a number, this number presents the line number - this is so you can find out where in your script the error is - it is very helpful for large scripts. After that is the error message, which varies depending on the error made. Looking at the error message, we can easily determine that the error originated from the resource '''myResource''' and line two of the script file '''script.lua'''. The error message is very clear, we forgot the &amp;quot;then&amp;quot;! We can easily fix that!&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function SayHello(message, player)&lt;br /&gt;
    if (getPlayerName(player) == &amp;quot;Fedor&amp;quot;) then&lt;br /&gt;
        outputChatbox(&amp;quot;Hello Fedor&amp;quot;)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onChatMessage&amp;quot;, root, SayHello)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Now the script will load fine and won't output any errors until a player with the name 'Fedor' says something in the chat. At that moment, the debug console will output:&lt;br /&gt;
:{{Debug error|myResource\script.lua:2: attempt to call global 'outputChatbox' (a nil value)}}&lt;br /&gt;
This error means that the function '''outputChatbox''' is a nil value, that is - it doesn't exist! This is because the function is actually called [[outputChatBox]], not [[outputChatbox]] - take care of that capital B.:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function SayHello(message, player)&lt;br /&gt;
    if (getPlayerName(player) == &amp;quot;Fedor&amp;quot;) then&lt;br /&gt;
        outputChatBox(&amp;quot;Hello Fedor&amp;quot;)&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onChatMessage&amp;quot;, root, SayHello)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Of course, this is just an example and I've only skimmed the surface of error messages. There are plenty of other messages and scenarios, but you should get the general idea.&lt;br /&gt;
&lt;br /&gt;
==Server &amp;amp; client debug logging==&lt;br /&gt;
====Server====&lt;br /&gt;
Navigate to: ''(MTA root folder)&amp;gt;server&amp;gt;mods&amp;gt;deathmatch''&lt;br /&gt;
&lt;br /&gt;
There are two nearly identical files:&lt;br /&gt;
&lt;br /&gt;
*The local.conf is the settings for the &amp;quot;host game&amp;quot; menu item in the main menu of MTA. This is a quick and easy way of starting a temporary server from inside the client. When the client is turned off the server will also turn off.&lt;br /&gt;
&lt;br /&gt;
*The mtaserver.conf is used when &amp;quot;MTA Server.exe&amp;quot; is executed from (MTA root folder)&amp;gt;server. This runs the server in its own console window which does not need the client to exist or to be run. This is can be useful if you're interested in hosting servers for a longer time period or if you want to experiment with a real world console.&lt;br /&gt;
&lt;br /&gt;
Depending on which way you host, you will want to edit those files. The settings in question are:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&amp;lt;!-- Specifies the location and name of the debugscript log file. If left blank, server won't be saving this file. --&amp;gt;&lt;br /&gt;
	&amp;lt;scriptdebuglogfile&amp;gt;logs/scripts.log&amp;lt;/scriptdebuglogfile&amp;gt; &lt;br /&gt;
	&lt;br /&gt;
	&amp;lt;!-- Specifies the level of the debugscript log file. Available values: 0, 1, 2, 3. When not set, defaults to 0. --&amp;gt;&lt;br /&gt;
	&amp;lt;scriptdebugloglevel&amp;gt;0&amp;lt;/scriptdebugloglevel&amp;gt;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You want to make sure you have a log name specified. Also you want to specify what level of errors will be logged. If it is 0 nothing will be logged. The levels were explained at the top of this article. If the logging level was changed to 3, then in this case all '''serverside''' script errors log to (MTA root folder)&amp;gt;server&amp;gt;mods&amp;gt;deathmatch&amp;gt;logs&amp;gt;scripts.log&lt;br /&gt;
&lt;br /&gt;
====Client====&lt;br /&gt;
Navigate to: ''(MTA root folder)&amp;gt;server&amp;gt;clientscript.log''&lt;br /&gt;
&lt;br /&gt;
This file logs all '''clientside''' script errors. This is set to log by default, no setup required.&lt;br /&gt;
&lt;br /&gt;
==Debug strategies==&lt;br /&gt;
There are several strategies that support finding errors, apart from going through the code. Most of them include outputting debug messages, with have different information depending on the situtation.&lt;br /&gt;
&lt;br /&gt;
===Useful functions===&lt;br /&gt;
There are some functions that may come in handy for debugging.&lt;br /&gt;
* [[outputDebugString]] or [[outputChatBox]] for outputting any information (use outputDebugString for technical output)&lt;br /&gt;
* [http://www.lua.org/manual/5.1/manual.html#pdf-tostring tostring()] on a variable to turn the value into a string. Useful if the value is not a number or string.&lt;br /&gt;
* [[getElementType]] to check the type of the MTA element.&lt;br /&gt;
* [[isElement]] to check if the MTA element exists.&lt;br /&gt;
&lt;br /&gt;
===Add debugmessages to check ''if'', ''when'' or ''how often'' a section of code is executed===&lt;br /&gt;
A typical example would be verify whether an ''if''-section is executed or not. To do that, just add any message you will recognize later within the ''if''-section.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
if (variable1 == variable2) then&lt;br /&gt;
    outputDebugString(&amp;quot;variable1 is the same as variable2!&amp;quot;)&lt;br /&gt;
    -- do anything&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Another application would be to check when variable values are modified. First search for all occurences of the variable being edited and add a message just beside it.&lt;br /&gt;
&lt;br /&gt;
===Add debugmessages to check the ''value'' of a variable===&lt;br /&gt;
Let's say you want to create a marker, but it doesn't appear at the position you expect it to be. The first thing you might want to do is check if the [[createMarker]] function is executed. But while doing this, you can also check the values being used in the [[createMarker]] function in one run.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
outputChatBox(&amp;quot;posX is: &amp;quot;..x..&amp;quot; posY is: &amp;quot;..y..&amp;quot; posZ is: &amp;quot;..z)&lt;br /&gt;
createMarker(x,y,z)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
This would output all three variables that are used as coordinates for the marker. Assuming you read those from a map file, you can now compare the debug output to the desired values. The [http://www.lua.org/manual/5.1/manual.html#pdf-tostring tostring()] will ensure that the values of the variables can be concatenated (put together) as a string, even if it's a boolean value.&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
Imagine you created a [[Colshape|collision shape]] somewhere and you want an action to perform after the player stays for ten seconds inside it, you would do this:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function colShapeHit(player)&lt;br /&gt;
	-- set a timer to output a message (could as well execute another function)&lt;br /&gt;
	-- store the timer id in a table, using the player as index&lt;br /&gt;
	colshapeTimer[player] = setTimer(outputChatBox,10000,1,&amp;quot;The player stayed 10 seconds in the colshape!&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onColShapeHit&amp;quot;, root, colShapeHit)&lt;br /&gt;
&lt;br /&gt;
function colShapeLeave(player)&lt;br /&gt;
	-- kill the timer when the player leaves the colshape&lt;br /&gt;
	killTimer(colshapeTimer[player])&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onColShapeLeave&amp;quot;, root, colShapeLeave)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
When a player enters the colshape, debugscript outputs the following message:&lt;br /&gt;
:{{Debug error|..[path]: attempt to index global 'colshapeTimer' (a nil value)}}&lt;br /&gt;
This means you tried to index a table that does not exist (because the table is a nil value, it doesn't exist). In the example above, this is done when storing the timer id in the table. We need to add a check if the table exists and if it does not exist we should create it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function colShapeHit(player)&lt;br /&gt;
	if (colshapeTimer == nil) then&lt;br /&gt;
		colshapeTimer = {}&lt;br /&gt;
	end&lt;br /&gt;
	-- set a timer to output a message (could as well execute another function)&lt;br /&gt;
	-- store the timer id in a table, using the player as index&lt;br /&gt;
	colshapeTimer[player] = setTimer(outputChatBox,10000,1,&amp;quot;The player stayed 10 seconds in the colshape!&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onColShapeHit&amp;quot;, root, colShapeHit)&lt;br /&gt;
&lt;br /&gt;
function colShapeLeave(player)&lt;br /&gt;
	-- kill the timer when the player leaves the colshape&lt;br /&gt;
	killTimer(colshapeTimer[player])&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onColShapeLeave&amp;quot;,root,colShapeLeave)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we will still receive a warning when a player enters the colshape, waits for the message and leaves it again:&lt;br /&gt;
&lt;br /&gt;
:{{Debug warning|[..]: Bad argument @ 'killTimer' Line: ..}}&lt;br /&gt;
&lt;br /&gt;
Except for that (we will talk about that later) everything seems to work fine. A player enters the colshape, the timer is started, if he stays the message occurs, if he leaves the timer is killed.&lt;br /&gt;
&lt;br /&gt;
===A more inconspicuous error===&lt;br /&gt;
But for some reason the message gets outputted twice when you stay in the colcircle while in a vehicle. Since it would appear some code is executed twice, we add debug messages to check this.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function colShapeHit(player)&lt;br /&gt;
	if (colshapeTimer == nil) then&lt;br /&gt;
		colshapeTimer = {}&lt;br /&gt;
	end&lt;br /&gt;
	-- add a debug message&lt;br /&gt;
	outputDebugString(&amp;quot;colShapeHit&amp;quot;)&lt;br /&gt;
	-- set a timer to output a message (could as well execute another function)&lt;br /&gt;
	-- store the timer id in a table, using the player as index&lt;br /&gt;
	colshapeTimer[player] = setTimer(outputChatBox,10000,1,&amp;quot;The player stayed 10 seconds in the colshape!&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onColShapeHit&amp;quot;,getRootElement(),colShapeHit)&lt;br /&gt;
&lt;br /&gt;
function colShapeLeave(player)&lt;br /&gt;
	-- add a debug message&lt;br /&gt;
	outputDebugString(&amp;quot;colShapeLeave&amp;quot;)&lt;br /&gt;
	-- kill the timer when the player leaves the colshape&lt;br /&gt;
	killTimer(colshapeTimer[player])&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onColShapeLeave&amp;quot;,getRootElement(),colShapeLeave)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we notice that both handler functions get executed twice when we are in a vehicle, but only once when we are on-foot. It would appear the vehicle triggers the colshape as well. To confirm this theory, we ensure that the ''player'' variable actually holds a reference to a player element.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function colShapeHit(player)&lt;br /&gt;
	if (colshapeTimer == nil) then&lt;br /&gt;
		colshapeTimer = {}&lt;br /&gt;
	end&lt;br /&gt;
	-- add a debug message, with the element type&lt;br /&gt;
	outputDebugString(&amp;quot;colShapeHit &amp;quot;..getElementType(player))&lt;br /&gt;
	-- set a timer to output a message (could as well execute another function)&lt;br /&gt;
	-- store the timer id in a table, using the player as index&lt;br /&gt;
	colshapeTimer[player] = setTimer(outputChatBox,10000,1,&amp;quot;The player stayed 10 seconds in the colshape!&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onColShapeHit&amp;quot;,getRootElement(),colShapeHit)&lt;br /&gt;
&lt;br /&gt;
function colShapeLeave(player)&lt;br /&gt;
	-- add a debug message, with the element type&lt;br /&gt;
	outputDebugString(&amp;quot;colShapeLeave &amp;quot;..getElementType(player))&lt;br /&gt;
	-- kill the timer when the player leaves the colshape&lt;br /&gt;
	killTimer(colshapeTimer[player])&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onColShapeLeave&amp;quot;,getRootElement(),colShapeLeave)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The debug messages tell us that one of the ''player'' variables is a player and that the other one is a vehicle element. Since we only want to react when a player enters the colshape, we add an ''if'' that will end the execution of the function if it's '''not''' an player element.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function colShapeHit(player)&lt;br /&gt;
	if (colshapeTimer == nil) then&lt;br /&gt;
		colshapeTimer = {}&lt;br /&gt;
	end&lt;br /&gt;
	-- add a check for the element type&lt;br /&gt;
	if (getElementType(player) ~= &amp;quot;player&amp;quot;) then return end&lt;br /&gt;
	-- add a debug message, with the element type&lt;br /&gt;
	outputDebugString(&amp;quot;colShapeHit &amp;quot;..getElementType(player))&lt;br /&gt;
	-- set a timer to output a message (could as well execute another function)&lt;br /&gt;
	-- store the timer id in a table, using the player as index&lt;br /&gt;
	colshapeTimer[player] = setTimer(outputChatBox,10000,1,&amp;quot;The player stayed 10 seconds in the colshape!&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onColShapeHit&amp;quot;,getRootElement(),colShapeHit)&lt;br /&gt;
&lt;br /&gt;
function colShapeLeave(player)&lt;br /&gt;
	-- add a check for the element type&lt;br /&gt;
	if (getElementType(player) ~= &amp;quot;player&amp;quot;) then return end&lt;br /&gt;
	-- add a debug message, with the element type&lt;br /&gt;
	outputDebugString(&amp;quot;colShapeLeave &amp;quot;..getElementType(player))&lt;br /&gt;
	-- kill the timer when the player leaves the colshape&lt;br /&gt;
	killTimer(colshapeTimer[player])&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onColShapeLeave&amp;quot;,getRootElement(),colShapeLeave)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now the script should work as desired, but it will still output the warning mentioned above. This happens because the timer we try to kill when a player leaves the colshape will not exist anymore when it has reached the 10 seconds (and therefore executed after the 10th second has completed). There are different ways to get rid of that warning (since you know that the timer might not exist anymore and you only want to kill it if it exists). One way would be to check if the timer referenced in the table really exists. To do this, we need to use [[isTimer]], which we will use when we kill the timer:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
if (isTimer(colshapeTimer[player])) then&lt;br /&gt;
	killTimer(colshapeTimer[player])&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So the complete working code would be:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function colShapeHit(player)&lt;br /&gt;
	if (colshapeTimer == nil) then&lt;br /&gt;
		colshapeTimer = {}&lt;br /&gt;
	end&lt;br /&gt;
	-- add a check for the element type&lt;br /&gt;
	if (getElementType(player) ~= &amp;quot;player&amp;quot;) then return end&lt;br /&gt;
	-- add a debug message, with the element type&lt;br /&gt;
	outputDebugString(&amp;quot;colShapeHit &amp;quot;..getElementType(player))&lt;br /&gt;
	-- set a timer to output a message (could as well execute another function)&lt;br /&gt;
	-- store the timer id in a table, using the player as index&lt;br /&gt;
	colshapeTimer[player] = setTimer(outputChatBox,10000,1,&amp;quot;The player stayed 10 seconds in the colshape!&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onColShapeHit&amp;quot;,getRootElement(),colShapeHit)&lt;br /&gt;
&lt;br /&gt;
function colShapeLeave(player)&lt;br /&gt;
	-- add a check for the element type&lt;br /&gt;
	if (getElementType(player) ~= &amp;quot;player&amp;quot;) then return end&lt;br /&gt;
	-- add a debug message, with the element type&lt;br /&gt;
	outputDebugString(&amp;quot;colShapeLeave &amp;quot;..getElementType(player))&lt;br /&gt;
	-- kill the timer when the player leaves the colshape&lt;br /&gt;
	if (isTimer(colshapeTimer[player])) then&lt;br /&gt;
		killTimer(colshapeTimer[player])&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onColShapeLeave&amp;quot;,getRootElement(),colShapeLeave)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Debugging Performance Issues==&lt;br /&gt;
&lt;br /&gt;
If your server is using up more resources than it should or you just want to make sure your scripts are efficient, you can find the source of the issue by using a great tool that comes with the default resource package called [[Resource:Performancebrowser|performancebrowser]]. You can start it with ''''start performancebrowser''''. If it doesn't exist then you can get the latest resources from the [https://github.com/multitheftauto/mtasa-resources GitHub repository]. This tool provides an incredible amount of information for performance debugging. Memory leaks, element leaks and CPU intensive scripts are all easily findable via performancebrowser. If you use the ''''d'''' option in Lua timing you can see which functions are using up the CPU.&lt;br /&gt;
&lt;br /&gt;
To access performancebrowser you will need to go to your web browser and enter the address: http://serverIPHere:serverHTTPPortHere/performancebrowser/ Note that the / at the end is required. So for example: http://127.0.0.1:22005/performancebrowser/ You will then need to login with an in-game admin account or any account that has access to ''''resource.performancebrowser.http'''' and ''''resource.ajax.http''''. Most of the information you will need are in the categories Lua timing and Lua memory, look for values that are much higher than other values.&lt;br /&gt;
&lt;br /&gt;
===Examples of scripts that could cause performance problems===&lt;br /&gt;
&lt;br /&gt;
Adding data to a table but never removing it. This would take months/years before it causes a problem though.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local someData = {}&lt;br /&gt;
&lt;br /&gt;
function storeData()&lt;br /&gt;
    someData[source] = true&lt;br /&gt;
    -- There is no handling for when a player quits, this is considered a memory leak&lt;br /&gt;
    -- Using the Lua timing tab you can detect the RAM usage of each resource.&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerJoin&amp;quot;, root, storeData)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Element leaking is possible if you use temporary colshapes for whatever reason and may not destroy them. This would cause bandwidth, CPU and memory performance issues over time.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function useTemporaryCol()&lt;br /&gt;
    local col = createColCircle(some code here)&lt;br /&gt;
    if (normally this should happen) then&lt;br /&gt;
        destroyElement(col)&lt;br /&gt;
    end&lt;br /&gt;
    -- But sometimes it didn't so the script ended but the collision area remained and over time&lt;br /&gt;
    -- you may end up with hundreds to thousands of pointless collision areas. &lt;br /&gt;
    -- The Lua timing tab allows you to see the amount of elements each script has created.&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
High CPU usage resulting in the server FPS dropping so much that the server is unplayable. In under 24 hours this can create havoc on a very busy server. The amount of &amp;quot;refs&amp;quot; in the Lua timing detect this type of build up, surprisingly the Lua timing tab didn't help in this case but Lua memory did.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerJoin&amp;quot;, root, function()&lt;br /&gt;
    -- Code for joiner&lt;br /&gt;
    addEventHandler(&amp;quot;onPlayerQuit&amp;quot;, root, function()&lt;br /&gt;
        -- Code for when they have quit&lt;br /&gt;
        -- See the problem? It's bound to root which the event handler is being added again and again and again&lt;br /&gt;
    end)&lt;br /&gt;
end)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A function uses up a lot of your CPU because whatever it does takes a long time. This is just some function that takes a long time to complete. Without performancebrowser you'd have no idea its the cause but with performancebrowser you can see that a resource is using lots of CPU in the Lua timing tab. If you then enter: ''''d'''' into the options edit box it will even tell you what file name and first line of the function that is using up so much CPU.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function someDodgyCode()&lt;br /&gt;
    for i=1, 100000 do&lt;br /&gt;
        -- some code&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[it:Guida al Debug]]&lt;br /&gt;
[[Category:Scripting Concepts]]&lt;br /&gt;
[[ru:Debugging]]&lt;br /&gt;
[[Category:Tutorials]]&lt;/div&gt;</summary>
		<author><name>Skyvzla</name></author>
	</entry>
	<entry>
		<id>https://wiki.multitheftauto.com/index.php?title=ZH-CN/Main_Page&amp;diff=50458</id>
		<title>ZH-CN/Main Page</title>
		<link rel="alternate" type="text/html" href="https://wiki.multitheftauto.com/index.php?title=ZH-CN/Main_Page&amp;diff=50458"/>
		<updated>2017-02-14T12:42:00Z</updated>

		<summary type="html">&lt;p&gt;Skyvzla: Undo revision 50457 by Skyvzla (talk)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{| width=&amp;quot;100%&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;0&amp;quot;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; |&lt;br /&gt;
&amp;lt;div style=&amp;quot;/*border: 1px solid #D8D8D8;*/ padding-left: 15px; padding-right: 15px; height: 100%;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Mtalogo.png|left|100px|link=http://wiki.multitheftauto.com/]]'''欢迎来到 [[Multi Theft Auto]] Wiki。'''您可以在这里查询与 Multi Theft Auto 游戏及开发有关的任何相关信息。&lt;br /&gt;
&lt;br /&gt;
您可以'''创建地图及游戏模式'''；帮助我们'''编写脚本 API 文档'''、'''示例代码'''及'''教程'''；或是将'''游戏过程中所发现的 Bugs 告诉我们'''。&lt;br /&gt;
&lt;br /&gt;
如果您在编写脚本时遇到了问题，欢迎随时[[IRC Channel|联系我们]]。&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;[ 从此不再一个人玩 ]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;0&amp;quot;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; |&lt;br /&gt;
|-&lt;br /&gt;
|width=&amp;quot;50%&amp;quot; style=&amp;quot;vertical-align:top;&amp;quot; |&amp;lt;div style=&amp;quot;border: 1px solid #D8D8D8; padding:4px 8px 8px 8px; margin:10px; background: #FFFCF2;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; width: 32px;&amp;quot;&amp;gt;[[File:Input-gaming.png‎|link=]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;h3&amp;gt;开始游戏&amp;lt;/h3&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #FFEEAA; border: 1px solid #FFCD19;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Go-down.png|link=http://mtasa.com/]] ''' [http://mtasa.com/ 下载 Multi Theft Auto: San Andreas {{Current Version|full}}]'''&amp;lt;/div&amp;gt;&lt;br /&gt;
* [[ZH-CN/在那里可以购买GTASA|在那里可以购买GTASA]]&lt;br /&gt;
* [[ZH-CN/客户端手册|客户端手册]]&lt;br /&gt;
* [[ZH-CN/{{padleft:|5|{{Current Version|full}}}} 版本新特性|{{padleft:|5|{{Current Version|full}}}} 版本新特性]]&lt;br /&gt;
* [[ZH-CN/已知问题|已知问题]]&lt;br /&gt;
* [[Upgrading_from_MTA:Race|从 MTA:Race 升级到 MTA:SA {{padleft:|3|{{Current Version|full}}}}]]&lt;br /&gt;
* [[ZH-CN/服务端手册|服务端手册]]&lt;br /&gt;
* [[ZH-CN/地图管理器|地图管理器]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h3&amp;gt;地图编辑器&amp;lt;/h3&amp;gt;&lt;br /&gt;
*[[Resource:Editor|地图编辑器手册]]&lt;br /&gt;
*[[Resource:Editor/EDF|编辑器定义格式 (EDF)]]&lt;br /&gt;
*[[Resource:Editor/Plugins|插件]]&lt;br /&gt;
*[[Resource:Editor#FAQ|常见问题解答]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border: 1px solid #D8D8D8; padding:4px 8px 8px 8px; margin:10px;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; width: 32px;&amp;quot;&amp;gt;[[File:Package-x-generic.png‎|link=]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;h3&amp;gt;数据库&amp;lt;/h3&amp;gt;&lt;br /&gt;
本节介绍了有关 MTA 或其资源所提供的 Lua 兼容资源。&lt;br /&gt;
* [[:Category:Resource|示例资源]] - 学习编写各类脚本的必经之路&lt;br /&gt;
* [[客户端脚本]]&lt;br /&gt;
* [[ZH-CN/扩展模块|扩展模块]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border: 1px solid #D8D8D8; padding:4px 8px 8px 8px; margin:10px;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; width: 32px;&amp;quot;&amp;gt;[[File:Applications-development.png‎‎‎|link=]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;h3&amp;gt;二次开发 Multi Theft Auto&amp;lt;/h3&amp;gt;&lt;br /&gt;
[[File:Go-down.png|link=http://nightly.mtasa.com/]] [http://nightly.mtasa.com/ 每夜构建版本下载] - 每天会有更新 但是有可能会不稳定&lt;br /&gt;
* [[Compiling_MTASA|在 Windows 平台上编译 MTASA]]&lt;br /&gt;
* [[Building_MTASA_Server_on_Mac_OS_X|在 Mac OS X 平台上编译 MTA SA]]&lt;br /&gt;
* [[Building_MTASA_Server_on_GNU_Linux|在 GNU/Linux 平台上编译 MTA SA]]&lt;br /&gt;
* [http://wiki.multitheftauto.com/wiki/Coding_guidelines/ 二次开发指南]&lt;br /&gt;
* [http://code.google.com/p/mtasa-blue Google Code SVN]&lt;br /&gt;
* [[Roadmap|路线图]]&lt;br /&gt;
* [http://bugs.mtasa.com/ Bug 追踪器]&lt;br /&gt;
* [[Branches|开发分支]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border: 1px solid #D8D8D8; padding:4px 8px 8px 8px; margin:10px;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; width: 32px;&amp;quot;&amp;gt;[[File:Applications-office.png|link=]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;h3&amp;gt;Wiki - 您能够帮助我们&amp;lt;/h3&amp;gt;&lt;br /&gt;
* 编写完全[[:Category:Incomplete|尚未完成的 API 文档]]。&lt;br /&gt;
* [[:Category:Needs_Example |添加 API 及事件的使用示例代码]]。&lt;br /&gt;
* 审查及核实[[:Category:Needs Checking|需要检查的页面]]。&lt;br /&gt;
* 编写帮助新手的各类教程。&lt;br /&gt;
* 将各 Wiki 页面翻译为您所使用的语言。&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border: 1px solid #D8D8D8; padding:4px 8px 8px 8px; margin:10px;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; width: 32px;&amp;quot;&amp;gt;[[File:Internet-group-chat.png‎|link=]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;h3&amp;gt;社区&amp;lt;/h3&amp;gt;&lt;br /&gt;
* [http://forum.multitheftauto.com/ MTA 官方论坛]&lt;br /&gt;
* IRC: [irc://irc.multitheftauto.com/mta irc.multitheftauto.com #mta]&lt;br /&gt;
* [http://community.mtasa.com/ MTA Community] - 分享及资源下载。&lt;br /&gt;
* [http://twitter.com/#!/MTAQA/ Twitter] - [http://www.youtube.com/user/MTAQA Youtube] - [http://plus.google.com/102014133442331779727/ Google+] - [http://www.moddb.com/mods/multi-theft-auto-san-andreas ModDB]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
| width=&amp;quot;50%&amp;quot; style=&amp;quot;vertical-align:top;&amp;quot; |&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border: 1px solid #D8D8D8; padding:4px 8px 8px 8px; margin:10px;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; width: 32px;&amp;quot;&amp;gt;[[File:Accessories-text-editor.png|link=]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;h3&amp;gt;脚本编写&amp;lt;/h3&amp;gt;&lt;br /&gt;
* [[ZH-CN/脚本编写介绍|脚本编写介绍]]&lt;br /&gt;
* [[ZH-CN/脚本编写介绍 - 带有图形界面|脚本编写介绍 - 带有图形界面]]&lt;br /&gt;
* [[Debugging|脚本调试教程]] - 用以找出脚本中的错误&lt;br /&gt;
* [[Resources|资源介绍]]&lt;br /&gt;
** [[ZH-CN/访问 Web 资源|访问 Web 资源]] - 如何将资源信息写入网站中&lt;br /&gt;
** [[:Category:Resource|示例资源]]&lt;br /&gt;
** [[Meta.xml]] - 用于定义资源的描述文件&lt;br /&gt;
** [[ACL]] - 访问控制列表 (Access Control List)，用于控制玩家及脚本的各类访问权限&lt;br /&gt;
* [[Writing_Gamemodes|编写游戏模式]]&lt;br /&gt;
* [[Useful_Functions|常用 API]]&lt;br /&gt;
论坛链接&lt;br /&gt;
* [http://forum.mtasa.com/viewforum.php?f=91 脚本板块]&lt;br /&gt;
* [http://forum.mtasa.com/viewforum.php?f=148 脚本教程]&lt;br /&gt;
* [http://forum.mtasa.com/viewtopic.php?f=13&amp;amp;t=29363 离线Wiki下载]&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; width: 32px;&amp;quot;&amp;gt;[[File:start-here.png|link=]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;h3&amp;gt;Lua&amp;lt;/h3&amp;gt;&lt;br /&gt;
了解脚本编写所使用的语言 —— Lua。&lt;br /&gt;
*[http://www.lua.org/pil/index.html Lua编程 手册]&lt;br /&gt;
**[http://www.lua.org/manual/5.1/#index Lua函数参考]&lt;br /&gt;
*[http://lua-users.org/wiki/TutorialDirectory Lua Wiki]&lt;br /&gt;
*[http://nixstaller.berlios.de/manual/0.2/nixstaller_9.html 从Nixstaller到Lua的指南]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border: 1px solid #D8D8D8; padding:4px 8px 8px 8px; margin:10px; background:#F2F2FF;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; width: 32px;&amp;quot;&amp;gt;[[File:Preferences-system.png|link=]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;h3&amp;gt;脚本编写参考&amp;lt;/h3&amp;gt;&lt;br /&gt;
* [[ZH-CN/客户端脚本函数|客户端脚本函数]]&lt;br /&gt;
* [[ZH-CN/客户端事件|客户端事件]]&lt;br /&gt;
* [[ZH-CN/服务端脚本函数|服务端脚本函数]]&lt;br /&gt;
* [[ZH-CN/服务端事件|服务端事件]]&lt;br /&gt;
&amp;lt;!-- Incomplete * [[Module functions|服务器端外部模块的脚本功能列表]] --&amp;gt;&lt;br /&gt;
* [[ZH-CN/MTA 类|MTA 类]] - 详细列明 MTA 中所有的自定义元素类型&lt;br /&gt;
** [[ZH-CN/MTA 元素|MTA 元素]] / [[ZH-CN/元素树|元素树]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border: 1px solid #D8D8D8; padding:4px 8px 8px 8px; margin:10px;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; width: 32px;&amp;quot;&amp;gt;[[File:System-file-manager.png|link=]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;h3&amp;gt;[[ZH-CN/ID 列表|ID 列表]]&amp;lt;/h3&amp;gt;&lt;br /&gt;
*[[ZH-CN/动画|动画]]&lt;br /&gt;
*[[ZH-CN/角色皮肤|角色皮肤]]&lt;br /&gt;
*[[ZH-CN/CJ服饰样式|CJ服饰样式]]&lt;br /&gt;
*[[ZH-CN/车库|车库]]&lt;br /&gt;
*[[ZH-CN/室内空间|室内空间]]&lt;br /&gt;
*[[ZH-CN/材质编号|材质编号]]&lt;br /&gt;
*[[ZH-CN/爆弹类|爆弹类]]&lt;br /&gt;
*[[ZH-CN/雷达图标|雷达图标]]&lt;br /&gt;
*[[ZH-CN/声音及音效|声音及音效]]&lt;br /&gt;
*[[ZH-CN/载具|载具]]&lt;br /&gt;
*[[ZH-CN/载具颜色|载具颜色]]&lt;br /&gt;
*[[ZH-CN/载具组件|载具组件]]&lt;br /&gt;
*[[ZH-CN/载具变体|载具变体]]&lt;br /&gt;
*[[ZH-CN/载具改装操纵|载具改装操纵]]&lt;br /&gt;
*[[ZH-CN/武器|武器]]&lt;br /&gt;
*[[ZH-CN/天气|天气]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding:4px 8px 8px 8px; margin:10px;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Osi symbol.png|75px|link=http://opensource.org/|left]]&lt;br /&gt;
'''Multi Theft Auto''' 是'''一个开源项目'''。 &lt;br /&gt;
&amp;lt;br/&amp;gt;任何人都可以为 MTA 作出更多的贡献！&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; |&lt;br /&gt;
|}&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;0&amp;quot;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; |&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding-left: 15px; padding-right: 15px;&amp;quot; class=&amp;quot;plainlinks&amp;quot;&amp;gt;&lt;br /&gt;
[[File:MTALogo_8ball.png|left|85px|link=Archive]]&lt;br /&gt;
&amp;lt;ul style=&amp;quot;list-style: none; width: 200px; float: left;&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;'''关于 [[Multi Theft Auto]]'''&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[[档案]]&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[[新闻报道]]&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://code.google.com/p/mtasa-blue/people/list 开发者]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;ul style=&amp;quot;list-style: none; width: 200px; float: left;&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;'''Multi Theft Auto 0.5'''&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[[Archive#Multi_Theft_Auto_0.5|下载]]&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[[MTA 0.5r2 Known Issues|已知问题]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;ul style=&amp;quot;list-style: none; width: 200px; float: left;&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;'''Wiki 统计'''&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;共 {{NUMBEROFARTICLES}} 篇文章&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;共 {{NUMBEROFPAGES}} 页页面&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;共 {{NUMBEROFUSERS}} 位注册用户&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
__NOTOC__&lt;br /&gt;
__NOEDITSECTION__&lt;br /&gt;
{{Languages list|en}}&lt;/div&gt;</summary>
		<author><name>Skyvzla</name></author>
	</entry>
	<entry>
		<id>https://wiki.multitheftauto.com/index.php?title=ZH-CN/Main_Page&amp;diff=50457</id>
		<title>ZH-CN/Main Page</title>
		<link rel="alternate" type="text/html" href="https://wiki.multitheftauto.com/index.php?title=ZH-CN/Main_Page&amp;diff=50457"/>
		<updated>2017-02-14T12:40:00Z</updated>

		<summary type="html">&lt;p&gt;Skyvzla: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{| width=&amp;quot;100%&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;0&amp;quot;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; |&lt;br /&gt;
&amp;lt;div style=&amp;quot;/*border: 1px solid #D8D8D8;*/ padding-left: 15px; padding-right: 15px; height: 100%;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Mtalogo.png|left|100px|link=http://wiki.multitheftauto.com/]]'''欢迎来到 [[Multi Theft Auto]] Wiki。'''您可以在这里查询与 Multi Theft Auto 游戏及开发有关的任何相关信息。&lt;br /&gt;
&lt;br /&gt;
您可以'''创建地图及游戏模式'''；帮助我们'''编写脚本 API 文档'''、'''示例代码'''及'''教程'''；或是将'''游戏过程中所发现的 Bugs 告诉我们'''。&lt;br /&gt;
&lt;br /&gt;
如果您在编写脚本时遇到了问题，欢迎随时[[IRC Channel|联系我们]]。&lt;br /&gt;
&amp;lt;br/&amp;gt;&amp;lt;br/&amp;gt;[ 从此不再一个人玩 ]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;0&amp;quot;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; |&lt;br /&gt;
|-&lt;br /&gt;
|width=&amp;quot;50%&amp;quot; style=&amp;quot;vertical-align:top;&amp;quot; |&amp;lt;div style=&amp;quot;border: 1px solid #D8D8D8; padding:4px 8px 8px 8px; margin:10px; background: #FFFCF2;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; width: 32px;&amp;quot;&amp;gt;[[File:Input-gaming.png‎|link=]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;h3&amp;gt;开始游戏&amp;lt;/h3&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #FFEEAA; border: 1px solid #FFCD19;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Go-down.png|link=http://mtasa.com/]] ''' [http://mtasa.com/ 下载 Multi Theft Auto: San Andreas {{Current Version|full}}]'''&amp;lt;/div&amp;gt;&lt;br /&gt;
* [[ZH-CN/在那里可以购买GTASA|在那里可以购买GTASA]]&lt;br /&gt;
* [[ZH-CN/客户端手册|客户端手册]]&lt;br /&gt;
* [[ZH-CN/{{padleft:|5|{{Current Version|full}}}} 版本新特性|{{padleft:|5|{{Current Version|full}}}} 版本新特性]]&lt;br /&gt;
* [[ZH-CN/已知问题|已知问题]]&lt;br /&gt;
* [[Upgrading_from_MTA:Race|从 MTA:Race 升级到 MTA:SA {{padleft:|3|{{Current Version|full}}}}]]&lt;br /&gt;
* [[ZH-CN/服务端手册|服务端手册]]&lt;br /&gt;
* [[ZH-CN/地图管理器|地图管理器]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h3&amp;gt;地图编辑器&amp;lt;/h3&amp;gt;&lt;br /&gt;
*[[Resource:Editor|地图编辑器手册]]&lt;br /&gt;
*[[Resource:Editor/EDF|编辑器定义格式 (EDF)]]&lt;br /&gt;
*[[Resource:Editor/Plugins|插件]]&lt;br /&gt;
*[[Resource:Editor#FAQ|常见问题解答]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border: 1px solid #D8D8D8; padding:4px 8px 8px 8px; margin:10px;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; width: 32px;&amp;quot;&amp;gt;[[File:Package-x-generic.png‎|link=]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;h3&amp;gt;数据库&amp;lt;/h3&amp;gt;&lt;br /&gt;
本节介绍了有关 MTA 或其资源所提供的 Lua 兼容资源。&lt;br /&gt;
* [[:Category:Resource|示例资源]] - 学习编写各类脚本的必经之路&lt;br /&gt;
* [[客户端脚本]]&lt;br /&gt;
* [[ZH-CN/扩展模块|扩展模块]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border: 1px solid #D8D8D8; padding:4px 8px 8px 8px; margin:10px;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; width: 32px;&amp;quot;&amp;gt;[[File:Applications-development.png‎‎‎|link=]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;h3&amp;gt;二次开发 Multi Theft Auto&amp;lt;/h3&amp;gt;&lt;br /&gt;
[[File:Go-down.png|link=http://nightly.mtasa.com/]] [http://nightly.mtasa.com/ 每夜构建版本下载] - 每天会有更新 但是有可能会不稳定&lt;br /&gt;
* [[Compiling_MTASA|在 Windows 平台上编译 MTASA]]&lt;br /&gt;
* [[Building_MTASA_Server_on_Mac_OS_X|在 Mac OS X 平台上编译 MTA SA]]&lt;br /&gt;
* [[Building_MTASA_Server_on_GNU_Linux|在 GNU/Linux 平台上编译 MTA SA]]&lt;br /&gt;
* [http://wiki.multitheftauto.com/wiki/Coding_guidelines/ 二次开发指南]&lt;br /&gt;
* [http://code.google.com/p/mtasa-blue Google Code SVN]&lt;br /&gt;
* [[Roadmap|路线图]]&lt;br /&gt;
* [http://bugs.mtasa.com/ Bug 追踪器]&lt;br /&gt;
* [[Branches|开发分支]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border: 1px solid #D8D8D8; padding:4px 8px 8px 8px; margin:10px;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; width: 32px;&amp;quot;&amp;gt;[[File:Applications-office.png|link=]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;h3&amp;gt;Wiki - 您能够帮助我们&amp;lt;/h3&amp;gt;&lt;br /&gt;
* 编写完全[[:Category:Incomplete|尚未完成的 API 文档]]。&lt;br /&gt;
* [[:Category:Needs_Example |添加 API 及事件的使用示例代码]]。&lt;br /&gt;
* 审查及核实[[:Category:Needs Checking|需要检查的页面]]。&lt;br /&gt;
* 编写帮助新手的各类教程。&lt;br /&gt;
* 将各 Wiki 页面翻译为您所使用的语言。&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border: 1px solid #D8D8D8; padding:4px 8px 8px 8px; margin:10px;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; width: 32px;&amp;quot;&amp;gt;[[File:Internet-group-chat.png‎|link=]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;h3&amp;gt;社区&amp;lt;/h3&amp;gt;&lt;br /&gt;
* [http://forum.multitheftauto.com/ MTA 官方论坛]&lt;br /&gt;
* IRC: [irc://irc.multitheftauto.com/mta irc.multitheftauto.com #mta]&lt;br /&gt;
* [http://community.mtasa.com/ MTA Community] - 分享及资源下载。&lt;br /&gt;
* [http://twitter.com/#!/MTAQA/ Twitter] - [http://www.youtube.com/user/MTAQA Youtube] - [http://plus.google.com/102014133442331779727/ Google+] - [http://www.moddb.com/mods/multi-theft-auto-san-andreas ModDB]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
| width=&amp;quot;50%&amp;quot; style=&amp;quot;vertical-align:top;&amp;quot; |&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border: 1px solid #D8D8D8; padding:4px 8px 8px 8px; margin:10px;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; width: 32px;&amp;quot;&amp;gt;[[File:Accessories-text-editor.png|link=]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;h3&amp;gt;脚本编写&amp;lt;/h3&amp;gt;&lt;br /&gt;
* [[ZH-CN/脚本编写介绍|脚本编写介绍]]&lt;br /&gt;
* [[ZH-CN/脚本编写介绍 - 带有图形界面|脚本编写介绍 - 带有图形界面]]&lt;br /&gt;
* [[ZH-CN/脚本调试教程|脚本调试教程]] - 用以找出脚本中的错误&lt;br /&gt;
* [[Resources|资源介绍]]&lt;br /&gt;
** [[ZH-CN/访问 Web 资源|访问 Web 资源]] - 如何将资源信息写入网站中&lt;br /&gt;
** [[:Category:Resource|示例资源]]&lt;br /&gt;
** [[Meta.xml]] - 用于定义资源的描述文件&lt;br /&gt;
** [[ACL]] - 访问控制列表 (Access Control List)，用于控制玩家及脚本的各类访问权限&lt;br /&gt;
* [[Writing_Gamemodes|编写游戏模式]]&lt;br /&gt;
* [[Useful_Functions|常用 API]]&lt;br /&gt;
论坛链接&lt;br /&gt;
* [http://forum.mtasa.com/viewforum.php?f=91 脚本板块]&lt;br /&gt;
* [http://forum.mtasa.com/viewforum.php?f=148 脚本教程]&lt;br /&gt;
* [http://forum.mtasa.com/viewtopic.php?f=13&amp;amp;t=29363 离线Wiki下载]&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; width: 32px;&amp;quot;&amp;gt;[[File:start-here.png|link=]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;h3&amp;gt;Lua&amp;lt;/h3&amp;gt;&lt;br /&gt;
了解脚本编写所使用的语言 —— Lua。&lt;br /&gt;
*[http://www.lua.org/pil/index.html Lua编程 手册]&lt;br /&gt;
**[http://www.lua.org/manual/5.1/#index Lua函数参考]&lt;br /&gt;
*[http://lua-users.org/wiki/TutorialDirectory Lua Wiki]&lt;br /&gt;
*[http://nixstaller.berlios.de/manual/0.2/nixstaller_9.html 从Nixstaller到Lua的指南]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;border: 1px solid #D8D8D8; padding:4px 8px 8px 8px; margin:10px; background:#F2F2FF;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; width: 32px;&amp;quot;&amp;gt;[[File:Preferences-system.png|link=]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;h3&amp;gt;脚本编写参考&amp;lt;/h3&amp;gt;&lt;br /&gt;
* [[ZH-CN/客户端脚本函数|客户端脚本函数]]&lt;br /&gt;
* [[ZH-CN/客户端事件|客户端事件]]&lt;br /&gt;
* [[ZH-CN/服务端脚本函数|服务端脚本函数]]&lt;br /&gt;
* [[ZH-CN/服务端事件|服务端事件]]&lt;br /&gt;
&amp;lt;!-- Incomplete * [[Module functions|服务器端外部模块的脚本功能列表]] --&amp;gt;&lt;br /&gt;
* [[ZH-CN/MTA 类|MTA 类]] - 详细列明 MTA 中所有的自定义元素类型&lt;br /&gt;
** [[ZH-CN/MTA 元素|MTA 元素]] / [[ZH-CN/元素树|元素树]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;border: 1px solid #D8D8D8; padding:4px 8px 8px 8px; margin:10px;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;float:right; width: 32px;&amp;quot;&amp;gt;[[File:System-file-manager.png|link=]]&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;h3&amp;gt;[[ZH-CN/ID 列表|ID 列表]]&amp;lt;/h3&amp;gt;&lt;br /&gt;
*[[ZH-CN/动画|动画]]&lt;br /&gt;
*[[ZH-CN/角色皮肤|角色皮肤]]&lt;br /&gt;
*[[ZH-CN/CJ服饰样式|CJ服饰样式]]&lt;br /&gt;
*[[ZH-CN/车库|车库]]&lt;br /&gt;
*[[ZH-CN/室内空间|室内空间]]&lt;br /&gt;
*[[ZH-CN/材质编号|材质编号]]&lt;br /&gt;
*[[ZH-CN/爆弹类|爆弹类]]&lt;br /&gt;
*[[ZH-CN/雷达图标|雷达图标]]&lt;br /&gt;
*[[ZH-CN/声音及音效|声音及音效]]&lt;br /&gt;
*[[ZH-CN/载具|载具]]&lt;br /&gt;
*[[ZH-CN/载具颜色|载具颜色]]&lt;br /&gt;
*[[ZH-CN/载具组件|载具组件]]&lt;br /&gt;
*[[ZH-CN/载具变体|载具变体]]&lt;br /&gt;
*[[ZH-CN/载具改装操纵|载具改装操纵]]&lt;br /&gt;
*[[ZH-CN/武器|武器]]&lt;br /&gt;
*[[ZH-CN/天气|天气]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding:4px 8px 8px 8px; margin:10px;&amp;quot;&amp;gt;&lt;br /&gt;
[[File:Osi symbol.png|75px|link=http://opensource.org/|left]]&lt;br /&gt;
'''Multi Theft Auto''' 是'''一个开源项目'''。 &lt;br /&gt;
&amp;lt;br/&amp;gt;任何人都可以为 MTA 作出更多的贡献！&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; |&lt;br /&gt;
|}&lt;br /&gt;
{| width=&amp;quot;100%&amp;quot; cellspacing=&amp;quot;0&amp;quot; cellpadding=&amp;quot;0&amp;quot;&lt;br /&gt;
| colspan=&amp;quot;2&amp;quot; |&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding-left: 15px; padding-right: 15px;&amp;quot; class=&amp;quot;plainlinks&amp;quot;&amp;gt;&lt;br /&gt;
[[File:MTALogo_8ball.png|left|85px|link=Archive]]&lt;br /&gt;
&amp;lt;ul style=&amp;quot;list-style: none; width: 200px; float: left;&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;'''关于 [[Multi Theft Auto]]'''&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[[档案]]&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[[新闻报道]]&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[http://code.google.com/p/mtasa-blue/people/list 开发者]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;ul style=&amp;quot;list-style: none; width: 200px; float: left;&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;'''Multi Theft Auto 0.5'''&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[[Archive#Multi_Theft_Auto_0.5|下载]]&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;[[MTA 0.5r2 Known Issues|已知问题]]&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;ul style=&amp;quot;list-style: none; width: 200px; float: left;&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;'''Wiki 统计'''&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;共 {{NUMBEROFARTICLES}} 篇文章&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;共 {{NUMBEROFPAGES}} 页页面&amp;lt;/li&amp;gt;&lt;br /&gt;
  &amp;lt;li&amp;gt;共 {{NUMBEROFUSERS}} 位注册用户&amp;lt;/li&amp;gt;&lt;br /&gt;
&amp;lt;/ul&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
__NOTOC__&lt;br /&gt;
__NOEDITSECTION__&lt;br /&gt;
{{Languages list|en}}&lt;/div&gt;</summary>
		<author><name>Skyvzla</name></author>
	</entry>
	<entry>
		<id>https://wiki.multitheftauto.com/index.php?title=ZH-CN/%E8%84%9A%E6%9C%AC%E7%BC%96%E5%86%99%E4%BB%8B%E7%BB%8D_-_%E5%B8%A6%E6%9C%89%E5%9B%BE%E5%BD%A2%E7%95%8C%E9%9D%A2&amp;diff=50432</id>
		<title>ZH-CN/脚本编写介绍 - 带有图形界面</title>
		<link rel="alternate" type="text/html" href="https://wiki.multitheftauto.com/index.php?title=ZH-CN/%E8%84%9A%E6%9C%AC%E7%BC%96%E5%86%99%E4%BB%8B%E7%BB%8D_-_%E5%B8%A6%E6%9C%89%E5%9B%BE%E5%BD%A2%E7%95%8C%E9%9D%A2&amp;diff=50432"/>
		<updated>2017-02-12T06:48:08Z</updated>

		<summary type="html">&lt;p&gt;Skyvzla: /* 登陆 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- place holder --&amp;gt;&lt;br /&gt;
在MTA:SA中有一个重要的特点，那就是可以制作自定义的GUI(Graphic User Interface)。GUI由窗口，按钮，编辑框，复选框等组成，拥有在图形环境中大部分的基础控件。它们可以在玩家正在游戏的时候显示，常常被用来代替传统命令的输入和输出。&lt;br /&gt;
&lt;br /&gt;
[[Image:AdminGUI.png|thumb|管理员后台GUI]]&lt;br /&gt;
&lt;br /&gt;
==一个做登陆窗口的教程==&lt;br /&gt;
在这个教程中我们会使用两个编辑框和一个按钮制作一个登录窗口。 窗口会在玩家加入游戏的时候出现，一旦按钮被点击，玩家将会被出生。 这个教程会继续我们在 [[Scripting Introduction|脚本介绍]] 中制作的游戏模式''（如果你已经使用了 [[Scripting Introduction|脚本介绍]]，你需要在你的代码中移除或者注释掉在 &amp;quot;joinHandler&amp;quot; 行的 [[spawnPlayer]] 函数， 我们也将会在这个教程中使用一个gui替换掉它）''。 我们也会接触一下客户端脚本的编辑。&lt;br /&gt;
&lt;br /&gt;
===画一个窗口===&lt;br /&gt;
所有的GUI只能在客户端编写。 这是一个很好的练习机会来把所有客户端脚本放在分开的目录。&lt;br /&gt;
&lt;br /&gt;
浏览到目录 /你的MTA服务端目录/mods/deathmatch/resources/myserver/，创建名为 client 和 server 两个文件夹，在 client 中创建一个名为 gui.lua 的文件 ，在 server 中创建一个名为 server.lua 的文件。&lt;br /&gt;
&lt;br /&gt;
整个 myserver 脚本文件夹中文件情况如下：&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
/myserver&lt;br /&gt;
	/meta.xml&lt;br /&gt;
	/script.lua&lt;br /&gt;
	/server&lt;br /&gt;
		/server.lua&lt;br /&gt;
	/client&lt;br /&gt;
		/gui.lua&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''注意，我们所有编写的代码必须要使用 UTF-8 编码脚本才能常执行（否则你输出的中文等会出现乱码）。'''&lt;br /&gt;
&lt;br /&gt;
现在我们打开 gui.lua文件，我们将会写一个画窗口的函数。使用[[guiCreateWindow]]来创建一个窗口：&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function createLoginWindow()&lt;br /&gt;
	-- 定义窗口的X和Y坐标&lt;br /&gt;
	local X = 0.375&lt;br /&gt;
	local Y = 0.375&lt;br /&gt;
	-- 定义窗口的宽度和高度&lt;br /&gt;
	local Width = 0.25&lt;br /&gt;
	local Height = 0.25&lt;br /&gt;
        -- 创建一个窗口，并且把它放入变量 'wdwLogin' 里&lt;br /&gt;
        -- 点击这个函数的名字可以阅读这个函数的相关信息&lt;br /&gt;
	wdwLogin = guiCreateWindow(X, Y, Width, Height, &amp;quot;请登录&amp;quot;, true)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===相对和绝对坐标===&lt;br /&gt;
注意：传给 guiCreateWindow 的最后一个参数在上面的例子中是 ''true''。这个表明了窗口的坐标和尺寸都是 '''相对的'''，也就是说他们是一个相对于屏幕大小的 ''百分比''。我来解释一下：如果屏幕最左侧是0，那么最右侧就是1，那么X坐标为0.5将代表着屏幕中央。同理，屏幕顶部和底部也是一样的，最顶部为0，最底部为1，Y坐标为0.2则代表着是屏幕高度的20%。宽度和高度也是一样的道理（宽度为0.5则意味着窗口是屏幕的一半宽）。&lt;br /&gt;
&lt;br /&gt;
另外的，也可以用 '''绝对的''' （将传入 guiCreateWindow 的最后一个参数改为 ''false'' 即可）。绝对坐标被计算为父级的左上角开始到右下角的像素总数（如果没有GUI元素指定父级，那么父级是屏幕本身）。 如果我们假设屏幕的分辨率为1920*1200，那么从屏幕左边开始为0像素，到屏幕右边为1920像素，X坐标为960代表屏幕的中点。同理，屏幕顶部为0，到屏幕底部则为1200，Y坐标为20代表着距离屏幕顶部的20个像素。宽度和高度也是相同的道理（宽度为50则意味着窗口为50个像素宽）。 ''你可以使用 [[guiGetScreenSize]] 和一点数学来计算某些绝对坐标。''&lt;br /&gt;
&lt;br /&gt;
使用相对坐标和绝对坐标的不同点非常简单：使用绝对坐标创建gui经常精确地保持着相同的像素大小和坐标，然而使用相对坐标创建gui经常是与它父级gui大小的比值。&lt;br /&gt;
&lt;br /&gt;
当你用手敲代码的时候，绝对坐标一般来说更容易维护。然而你的选择是根据你的目的而变化的。&lt;br /&gt;
&lt;br /&gt;
为了本介绍的目的，我们将使用相对坐标。&lt;br /&gt;
&lt;br /&gt;
===添加组件===&lt;br /&gt;
接下来， 我们要添加文本标签（里面写上 “帐号：” 和 “密码：”）、编辑框（为了输入你的数据）和一个登录按钮。&lt;br /&gt;
&lt;br /&gt;
我们可以使用 [[guiCreateButtonTo]] 创建按钮，创建 [[guiCreateEdit]] 创建编辑框。&lt;br /&gt;
&lt;br /&gt;
'''注意：我们现在正在给我们已经存在的 ‘createLoginWindow’函数写更多的代码。这不是一个新的函数，这个函数是用来替换你脚本中已经存在的那个。''' &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function createLoginWindow()&lt;br /&gt;
	local X = 0.375&lt;br /&gt;
	local Y = 0.375&lt;br /&gt;
	local Width = 0.25&lt;br /&gt;
	local Height = 0.25&lt;br /&gt;
	wdwLogin = guiCreateWindow(X, Y, Width, Height, &amp;quot;请登录&amp;quot;, true)&lt;br /&gt;
	&lt;br /&gt;
	-- 给第一个文本标签定义新的X和Y坐标&lt;br /&gt;
	X = 0.0825&lt;br /&gt;
	Y = 0.2&lt;br /&gt;
	-- 给第一个文本标签定义新的宽度和高度&lt;br /&gt;
	Width = 0.25&lt;br /&gt;
	Height = 0.25&lt;br /&gt;
	-- 创建第一个文本标签，注意最后一个传入的参数是 ‘wdwLogin’，这个参数是你刚刚创建的窗口（window）&lt;br /&gt;
	-- 我们在它的父级gui上方创建了这个文本标签（所以所有的坐标和大小的值都是相对于这个窗口的）&lt;br /&gt;
	guiCreateLabel(X, Y, Width, Height, &amp;quot;帐号&amp;quot;, true, wdwLogin)&lt;br /&gt;
	-- 改变Y的值，所以第二个文本标签是在第一个下方的&lt;br /&gt;
	Y = 0.5&lt;br /&gt;
	guiCreateLabel(X, Y, Width, Height, &amp;quot;密码&amp;quot;, true, wdwLogin)&lt;br /&gt;
	&lt;br /&gt;
&lt;br /&gt;
	X = 0.415&lt;br /&gt;
	Y = 0.2&lt;br /&gt;
	Width = 0.5&lt;br /&gt;
	Height = 0.15&lt;br /&gt;
	edtUser = guiCreateEdit(X, Y, Width, Height, &amp;quot;&amp;quot;, true, wdwLogin)&lt;br /&gt;
	Y = 0.5&lt;br /&gt;
	edtPass = guiCreateEdit(X, Y, Width, Height, &amp;quot;&amp;quot;, true, wdwLogin)&lt;br /&gt;
	-- 设置编辑框的最大文字长度为50&lt;br /&gt;
	guiEditSetMaxLength(edtUser, 50)&lt;br /&gt;
	guiEditSetMaxLength(edtPass, 50)&lt;br /&gt;
	&lt;br /&gt;
	X = 0.415&lt;br /&gt;
	Y = 0.7&lt;br /&gt;
	Width = 0.25&lt;br /&gt;
	Height = 0.2&lt;br /&gt;
	btnLogin = guiCreateButton(X, Y, Width, Height, &amp;quot;Log In&amp;quot;, true, wdwLogin)&lt;br /&gt;
	&lt;br /&gt;
	-- 让这个窗口不显示&lt;br /&gt;
	guiSetVisible(wdwLogin, false)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
注意：每个GUI组件都是创建为window的子级，这是当创建组件时通过指定父级元素（在这种情况下是wdwLogin）来完成的&lt;br /&gt;
&lt;br /&gt;
这是很有用的，因为这不仅意味着所有组件都是附着在window并且移动的时候会带着这些组件一起移动，而且任何对父级window的改变都会被应用到这些子级元素。例如，我们可以隐藏所有的GUI，我们只要隐藏window就行了：&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
guiSetVisible(wdwLogin, false) --隐藏我们制作的所有的GUI，这样我们才能在适当的时候显示GUI&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===使用我们编写完成的功能===&lt;br /&gt;
'''createLoginWindow''' 功能编写完成，但是它'''不会'''执行,必须要我们'''调用'''它才可以执行。建议在'''客户端启动资源时'''创建所有的GUI，当前不需要显示时可以进行隐藏（如果需要立即显示就不用隐藏），或在我们需要用到它的时候显示给玩家。因此，我们需要为GUI窗口创建一个'''事件触发器'''，事件触发器的事件为 &amp;quot;[[onClientResourceStart]]&amp;quot;：&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- 将事件处理程序添加到资源的根元素&lt;br /&gt;
-- 只有在这个资源本启动时才会触发&lt;br /&gt;
addEventHandler(&amp;quot;onClientResourceStart&amp;quot;, getResourceRootElement(), &lt;br /&gt;
	function ()&lt;br /&gt;
		createLoginWindow()&lt;br /&gt;
	end&lt;br /&gt;
)	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
因为脚本是登陆界面，所以我们要在玩家进入游戏后显示出来。&lt;br /&gt;
也可以使用 &amp;quot;[[onClientResourceStart]]&amp;quot; 事件来完成，所以我们将上面的代码进行修改，来达到我们的目的：&lt;br /&gt;
&lt;br /&gt;
'''注意,我们正在为现有的 'onClientResourceStart' 事件处理程序编写更多的代码。不是新创建的一个事件处理程序。''' &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
addEventHandler(&amp;quot;onClientResourceStart&amp;quot;, getResourceRootElement(), &lt;br /&gt;
	function ()&lt;br /&gt;
		-- 创建已经编写好的窗口以及窗口组件&lt;br /&gt;
		createLoginWindow()&lt;br /&gt;
&lt;br /&gt;
		-- 输出一个欢迎信息给玩家&lt;br /&gt;
                outputChatBox(&amp;quot;欢迎来到我的服务器，请登陆。&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
		-- 用if函数判断窗口是否创建成功，如果成功将窗口显示给玩家&lt;br /&gt;
	        if (wdwLogin ~= nil) then&lt;br /&gt;
			guiSetVisible(wdwLogin, true)&lt;br /&gt;
		else&lt;br /&gt;
			-- 如果窗口创建失败时，告诉玩家窗口创建失败&lt;br /&gt;
			outputChatBox(&amp;quot;出现错误，没有成功创建GUI画面&amp;quot;)&lt;br /&gt;
	        end &lt;br /&gt;
&lt;br /&gt;
		-- 启用玩家的鼠标显示，以便点击窗口中的组件&lt;br /&gt;
	        showCursor(true)&lt;br /&gt;
		-- 设置玩家绑定的所有按键都不生效，包括'T '键的本地发言，以保证输入的信息都输入到编辑框内&lt;br /&gt;
	        guiSetInputEnabled(true)&lt;br /&gt;
	end&lt;br /&gt;
)	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
注意,在显示窗口之前,我们使用了一个if函数来判断窗口是否创建成功。创建成功的情况下将显示出来。创建不成功时就意味着 wdwLogin 不是一个元素。服务器也不会出现错误提示。只会提示玩家窗口创建失败。&lt;br /&gt;
下一步我们将给按钮(button)创建点击后的功能&lt;br /&gt;
&lt;br /&gt;
==给按钮写一个功能==&lt;br /&gt;
既然我们已经创建了我们的GUI并且展示给了玩家看，我们需要让它运行。&lt;br /&gt;
&lt;br /&gt;
===检测点击===&lt;br /&gt;
当玩家点击GUI上任何一部分，&amp;quot;[[onClientGUIClick]]&amp;quot; 事件会因为你点击了GUI组建而被触发。这允许我们更简单地去检测我们想要使用的GUI元素上的点击。&lt;br /&gt;
举个例子，我们能够在 bthLogin 按钮上附加这个事件来抓取在这个GUI元素上的任何点击：n it:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- 附加 onClientGUIClick 到 btnLogin 上然后设置触发函数为 'clientSubmitLogin'&lt;br /&gt;
addEventHandler(&amp;quot;onClientGUIClick&amp;quot;, btnLogin, clientSubmitLogin, false)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
'''Note the final argument passed is &amp;quot;false&amp;quot;. This indicates that the event will only trigger directly on btnLogin, not if the event has propagated up or down the tree. Setting this to &amp;quot;true&amp;quot; while attaching to gui elements will mean that clicking on any element in the same branch will trigger this event.'''&lt;br /&gt;
'''注意：最后一个参数传入的是 &amp;quot;false&amp;quot; 。这表明了这个事件只会直接由 btnLogin 触发，不是当时间&lt;br /&gt;
&lt;br /&gt;
This line of code can now be added inside the createLoginWindow function. It is a common mistake to try and attach events to non-existant GUI elements, so make sure you always attach your events '''after''' the gui element (in this case, the button) has been created:&lt;br /&gt;
&lt;br /&gt;
'''注意：我们正在为目前存在的 'createLoginWindow' 函数编写更多的代码。'''&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function createLoginWindow()&lt;br /&gt;
	-- create all our GUI elements&lt;br /&gt;
	...&lt;br /&gt;
&lt;br /&gt;
	-- now add our onClientGUIClick event to the button we just created&lt;br /&gt;
	addEventHandler(&amp;quot;onClientGUIClick&amp;quot;, btnLogin, clientSubmitLogin, false)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Managing the click===&lt;br /&gt;
Now that we can detect when the player clicks on the button, we need to write code to manage what happens when they do.&lt;br /&gt;
In our [[onClientGUIClick]] event handle, we told it to call the function clientSubmitLogin whenever btnLogin is clicked.&lt;br /&gt;
Therefore, we can now use the function clientSubmitLogin to control what happens when the button is clicked:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- create the function and define the 'button' and 'state' parameters&lt;br /&gt;
-- (these are passed automatically by onClientGUIClick)&lt;br /&gt;
function clientSubmitLogin(button,state)&lt;br /&gt;
	-- if our login button was clicked with the left mouse button, and the state of the mouse button is up&lt;br /&gt;
	if button == &amp;quot;left&amp;quot; and state == &amp;quot;up&amp;quot; then&lt;br /&gt;
		-- move the input focus back onto the game (allowing players to move around, open the chatbox, etc)&lt;br /&gt;
		guiSetInputEnabled(false)&lt;br /&gt;
		-- hide the window and all the components&lt;br /&gt;
		guiSetVisible(wdwLogin, false)&lt;br /&gt;
		-- hide the mouse cursor&lt;br /&gt;
		showCursor(false)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Now, when the button is clicked, the window will be hidden and all controls will be returned to the player. Next, we will tell the server to allow the player to spawn.&lt;br /&gt;
&lt;br /&gt;
===触发服务器事件===&lt;br /&gt;
客户端触发服务器端事件使用 [[triggerServerEvent]] 函数来完成.这个函数可以从客户端上触发指定的服务器端事件.同样的,服务器端触发客户端事件使用 [[triggerClientEvent]] 函数完成.因为我们要触发'''服务器端'''事件,所以在这里我们使用 [[triggerServerEvent]] 函数来调用我们'''自定义'''的服务器事件,服务器事件命名为 &amp;quot;submitLogin&amp;quot;,这个事件能够控制玩家的重生.&lt;br /&gt;
&lt;br /&gt;
'''注意,我们是在为现有的功能 'clientSubmitLogin' 编写更多的代码,并不是一个全新的功能.''' &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function clientSubmitLogin(button,state)&lt;br /&gt;
	if button == &amp;quot;left&amp;quot; and state == &amp;quot;up&amp;quot; then&lt;br /&gt;
		-- 获取账户编辑框中的账户名&lt;br /&gt;
		local username = guiGetText(edtUser)&lt;br /&gt;
		-- 获取密码编辑框中的密码&lt;br /&gt;
		local password = guiGetText(edtPass)&lt;br /&gt;
&lt;br /&gt;
		-- 如果用户名和密码都不为空时向下执行&lt;br /&gt;
		if username and password then&lt;br /&gt;
			-- 调用服务器端事件 'submitLogin' 并向服务器端传递账户名和密码&lt;br /&gt;
			triggerServerEvent(&amp;quot;submitLogin&amp;quot;, getRootElement(), username, password)&lt;br /&gt;
&lt;br /&gt;
			-- 恢复玩家的按键绑定(比如T键本地聊天),隐藏玩家的GUI画面,取消玩家鼠标的显示&lt;br /&gt;
			guiSetInputEnabled(false)&lt;br /&gt;
			guiSetVisible(wdwLogin, false)&lt;br /&gt;
			showCursor(false)&lt;br /&gt;
		else&lt;br /&gt;
			-- 用户名或密码有一个为空时,向玩家返回消息,并且不触发服务端事件&lt;br /&gt;
			-- 同时不隐藏GUI画面&lt;br /&gt;
			outputChatBox(&amp;quot;请输入用户名和密码。&amp;quot;)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===创建服务器事件===&lt;br /&gt;
'''我们现在已经编写完成我们所需的客户端代码，我们下面需要编写服务器端代码，服务器端代码需要写在 server 文件夹中的 server.lua 文件，因此我们打开 server.lua 文件，进行下面的编写。'''&lt;br /&gt;
&lt;br /&gt;
在服务器端上，我们想一下刚才所写的客户端代码，玩家一旦登陆就生成（重生）。&lt;br /&gt;
所以，现在我们需要添加'''客户端'''调用的服务器端事件（submitLogin），我们可以用 [[addEvent]] 和 [[addEventHandler]] 来完成.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- 创建一个名为 loginHandler 的功能，使用额外的两个参数 username 和 password （这是从客户端调用时传递的）&lt;br /&gt;
function loginHandler(username,password)&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- 我们添加自定义事件，命名为 submitLogin（事件名需要打英文双引号）。并允许客户端调用它（后面的true参数）。&lt;br /&gt;
addEvent(&amp;quot;submitLogin&amp;quot;,true)&lt;br /&gt;
-- 添加一个事件处理程序，在 submitLogin 事件触发时调用 loginHandler 功能。&lt;br /&gt;
addEventHandler(&amp;quot;submitLogin&amp;quot;,root,loginHandler)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===登陆===&lt;br /&gt;
现在我们已经定义好事件 'submitLogin' 以及调用时触发的功能，现在我们需要给 'loginHandler' 功能编写更多的代码以实现登陆和生成（重生）玩家。&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function loginHandler(username,password)&lt;br /&gt;
	-- 用 if 函数判断用户名和密码是否正确&lt;br /&gt;
	if username == &amp;quot;user&amp;quot; and password == &amp;quot;apple&amp;quot; then&lt;br /&gt;
		-- 判断 client 变量是否存在，以便生成（重生）玩家&lt;br /&gt;
		if (client) then&lt;br /&gt;
			spawnPlayer(client, 1959.55, -1714.46, 10)&lt;br /&gt;
			fadeCamera(client, true)&lt;br /&gt;
                        setCameraTarget(client, client)&lt;br /&gt;
			outputChatBox(&amp;quot;欢迎来到我的服务器&amp;quot;, client)&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		-- 如果用户名或密码不正确向玩家输出错误信息&lt;br /&gt;
		outputChatBox(&amp;quot;用户名或密码错误，请重新连接登陆。&amp;quot;,client)&lt;br /&gt;
        end			&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
addEvent(&amp;quot;submitLogin&amp;quot;,true)&lt;br /&gt;
addEventHandler(&amp;quot;submitLogin&amp;quot;,root,loginHandler)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
'''为了教程的目的，本教程中使用的非常简单的用户名（user）和密码（apple）。在服务器安全方面，你可以才用MySql数据库来存储用户的账户和密码以及数据信息。'''&lt;br /&gt;
&lt;br /&gt;
要注意 '''client''' 变量的使用，它是MTA用来标识触发服务端事件的'''客户端玩家'''（也就是说 client 变量就是触发（调用）服务器端事件的那个'''玩家'''）。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
最后，别忘记修改 meta.xml 文件，在&amp;lt;meta&amp;gt;&amp;lt;/meta&amp;gt;中加入下面两行，不要忘记将 gui.lua 标记为客户端属性（因为GUI是在客户端使用的），将 server.lua 标记为服务器端属性（因为 server.lua 中代码是需要在服务端中执行的）。&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;client/gui.lua&amp;quot; type=&amp;quot;client&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;server/server.lua&amp;quot; type=&amp;quot;server&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
我们现在完成了一个简单的登陆窗口，在点击登陆时会检测用户名和密码是正确，在正确的情况下生成（重生）出玩家。&lt;br /&gt;
&lt;br /&gt;
有关GUI的进一步帮助,请参考 [[:Category:GUI_Tutorials|GUI tutorials]].&lt;br /&gt;
&lt;br /&gt;
[[Category:GUI_Tutorials]]&lt;br /&gt;
[[it:Introduzione_allo_scripting_della_GUI]]&lt;br /&gt;
[[ru:Introduction to Scripting the GUI]]&lt;br /&gt;
[[es:Introducción a la Programación de GUI]]&lt;/div&gt;</summary>
		<author><name>Skyvzla</name></author>
	</entry>
	<entry>
		<id>https://wiki.multitheftauto.com/index.php?title=ZH-CN/%E8%84%9A%E6%9C%AC%E7%BC%96%E5%86%99%E4%BB%8B%E7%BB%8D_-_%E5%B8%A6%E6%9C%89%E5%9B%BE%E5%BD%A2%E7%95%8C%E9%9D%A2&amp;diff=50431</id>
		<title>ZH-CN/脚本编写介绍 - 带有图形界面</title>
		<link rel="alternate" type="text/html" href="https://wiki.multitheftauto.com/index.php?title=ZH-CN/%E8%84%9A%E6%9C%AC%E7%BC%96%E5%86%99%E4%BB%8B%E7%BB%8D_-_%E5%B8%A6%E6%9C%89%E5%9B%BE%E5%BD%A2%E7%95%8C%E9%9D%A2&amp;diff=50431"/>
		<updated>2017-02-12T06:47:15Z</updated>

		<summary type="html">&lt;p&gt;Skyvzla: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- place holder --&amp;gt;&lt;br /&gt;
在MTA:SA中有一个重要的特点，那就是可以制作自定义的GUI(Graphic User Interface)。GUI由窗口，按钮，编辑框，复选框等组成，拥有在图形环境中大部分的基础控件。它们可以在玩家正在游戏的时候显示，常常被用来代替传统命令的输入和输出。&lt;br /&gt;
&lt;br /&gt;
[[Image:AdminGUI.png|thumb|管理员后台GUI]]&lt;br /&gt;
&lt;br /&gt;
==一个做登陆窗口的教程==&lt;br /&gt;
在这个教程中我们会使用两个编辑框和一个按钮制作一个登录窗口。 窗口会在玩家加入游戏的时候出现，一旦按钮被点击，玩家将会被出生。 这个教程会继续我们在 [[Scripting Introduction|脚本介绍]] 中制作的游戏模式''（如果你已经使用了 [[Scripting Introduction|脚本介绍]]，你需要在你的代码中移除或者注释掉在 &amp;quot;joinHandler&amp;quot; 行的 [[spawnPlayer]] 函数， 我们也将会在这个教程中使用一个gui替换掉它）''。 我们也会接触一下客户端脚本的编辑。&lt;br /&gt;
&lt;br /&gt;
===画一个窗口===&lt;br /&gt;
所有的GUI只能在客户端编写。 这是一个很好的练习机会来把所有客户端脚本放在分开的目录。&lt;br /&gt;
&lt;br /&gt;
浏览到目录 /你的MTA服务端目录/mods/deathmatch/resources/myserver/，创建名为 client 和 server 两个文件夹，在 client 中创建一个名为 gui.lua 的文件 ，在 server 中创建一个名为 server.lua 的文件。&lt;br /&gt;
&lt;br /&gt;
整个 myserver 脚本文件夹中文件情况如下：&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
/myserver&lt;br /&gt;
	/meta.xml&lt;br /&gt;
	/script.lua&lt;br /&gt;
	/server&lt;br /&gt;
		/server.lua&lt;br /&gt;
	/client&lt;br /&gt;
		/gui.lua&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''注意，我们所有编写的代码必须要使用 UTF-8 编码脚本才能常执行（否则你输出的中文等会出现乱码）。'''&lt;br /&gt;
&lt;br /&gt;
现在我们打开 gui.lua文件，我们将会写一个画窗口的函数。使用[[guiCreateWindow]]来创建一个窗口：&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function createLoginWindow()&lt;br /&gt;
	-- 定义窗口的X和Y坐标&lt;br /&gt;
	local X = 0.375&lt;br /&gt;
	local Y = 0.375&lt;br /&gt;
	-- 定义窗口的宽度和高度&lt;br /&gt;
	local Width = 0.25&lt;br /&gt;
	local Height = 0.25&lt;br /&gt;
        -- 创建一个窗口，并且把它放入变量 'wdwLogin' 里&lt;br /&gt;
        -- 点击这个函数的名字可以阅读这个函数的相关信息&lt;br /&gt;
	wdwLogin = guiCreateWindow(X, Y, Width, Height, &amp;quot;请登录&amp;quot;, true)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===相对和绝对坐标===&lt;br /&gt;
注意：传给 guiCreateWindow 的最后一个参数在上面的例子中是 ''true''。这个表明了窗口的坐标和尺寸都是 '''相对的'''，也就是说他们是一个相对于屏幕大小的 ''百分比''。我来解释一下：如果屏幕最左侧是0，那么最右侧就是1，那么X坐标为0.5将代表着屏幕中央。同理，屏幕顶部和底部也是一样的，最顶部为0，最底部为1，Y坐标为0.2则代表着是屏幕高度的20%。宽度和高度也是一样的道理（宽度为0.5则意味着窗口是屏幕的一半宽）。&lt;br /&gt;
&lt;br /&gt;
另外的，也可以用 '''绝对的''' （将传入 guiCreateWindow 的最后一个参数改为 ''false'' 即可）。绝对坐标被计算为父级的左上角开始到右下角的像素总数（如果没有GUI元素指定父级，那么父级是屏幕本身）。 如果我们假设屏幕的分辨率为1920*1200，那么从屏幕左边开始为0像素，到屏幕右边为1920像素，X坐标为960代表屏幕的中点。同理，屏幕顶部为0，到屏幕底部则为1200，Y坐标为20代表着距离屏幕顶部的20个像素。宽度和高度也是相同的道理（宽度为50则意味着窗口为50个像素宽）。 ''你可以使用 [[guiGetScreenSize]] 和一点数学来计算某些绝对坐标。''&lt;br /&gt;
&lt;br /&gt;
使用相对坐标和绝对坐标的不同点非常简单：使用绝对坐标创建gui经常精确地保持着相同的像素大小和坐标，然而使用相对坐标创建gui经常是与它父级gui大小的比值。&lt;br /&gt;
&lt;br /&gt;
当你用手敲代码的时候，绝对坐标一般来说更容易维护。然而你的选择是根据你的目的而变化的。&lt;br /&gt;
&lt;br /&gt;
为了本介绍的目的，我们将使用相对坐标。&lt;br /&gt;
&lt;br /&gt;
===添加组件===&lt;br /&gt;
接下来， 我们要添加文本标签（里面写上 “帐号：” 和 “密码：”）、编辑框（为了输入你的数据）和一个登录按钮。&lt;br /&gt;
&lt;br /&gt;
我们可以使用 [[guiCreateButtonTo]] 创建按钮，创建 [[guiCreateEdit]] 创建编辑框。&lt;br /&gt;
&lt;br /&gt;
'''注意：我们现在正在给我们已经存在的 ‘createLoginWindow’函数写更多的代码。这不是一个新的函数，这个函数是用来替换你脚本中已经存在的那个。''' &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function createLoginWindow()&lt;br /&gt;
	local X = 0.375&lt;br /&gt;
	local Y = 0.375&lt;br /&gt;
	local Width = 0.25&lt;br /&gt;
	local Height = 0.25&lt;br /&gt;
	wdwLogin = guiCreateWindow(X, Y, Width, Height, &amp;quot;请登录&amp;quot;, true)&lt;br /&gt;
	&lt;br /&gt;
	-- 给第一个文本标签定义新的X和Y坐标&lt;br /&gt;
	X = 0.0825&lt;br /&gt;
	Y = 0.2&lt;br /&gt;
	-- 给第一个文本标签定义新的宽度和高度&lt;br /&gt;
	Width = 0.25&lt;br /&gt;
	Height = 0.25&lt;br /&gt;
	-- 创建第一个文本标签，注意最后一个传入的参数是 ‘wdwLogin’，这个参数是你刚刚创建的窗口（window）&lt;br /&gt;
	-- 我们在它的父级gui上方创建了这个文本标签（所以所有的坐标和大小的值都是相对于这个窗口的）&lt;br /&gt;
	guiCreateLabel(X, Y, Width, Height, &amp;quot;帐号&amp;quot;, true, wdwLogin)&lt;br /&gt;
	-- 改变Y的值，所以第二个文本标签是在第一个下方的&lt;br /&gt;
	Y = 0.5&lt;br /&gt;
	guiCreateLabel(X, Y, Width, Height, &amp;quot;密码&amp;quot;, true, wdwLogin)&lt;br /&gt;
	&lt;br /&gt;
&lt;br /&gt;
	X = 0.415&lt;br /&gt;
	Y = 0.2&lt;br /&gt;
	Width = 0.5&lt;br /&gt;
	Height = 0.15&lt;br /&gt;
	edtUser = guiCreateEdit(X, Y, Width, Height, &amp;quot;&amp;quot;, true, wdwLogin)&lt;br /&gt;
	Y = 0.5&lt;br /&gt;
	edtPass = guiCreateEdit(X, Y, Width, Height, &amp;quot;&amp;quot;, true, wdwLogin)&lt;br /&gt;
	-- 设置编辑框的最大文字长度为50&lt;br /&gt;
	guiEditSetMaxLength(edtUser, 50)&lt;br /&gt;
	guiEditSetMaxLength(edtPass, 50)&lt;br /&gt;
	&lt;br /&gt;
	X = 0.415&lt;br /&gt;
	Y = 0.7&lt;br /&gt;
	Width = 0.25&lt;br /&gt;
	Height = 0.2&lt;br /&gt;
	btnLogin = guiCreateButton(X, Y, Width, Height, &amp;quot;Log In&amp;quot;, true, wdwLogin)&lt;br /&gt;
	&lt;br /&gt;
	-- 让这个窗口不显示&lt;br /&gt;
	guiSetVisible(wdwLogin, false)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
注意：每个GUI组件都是创建为window的子级，这是当创建组件时通过指定父级元素（在这种情况下是wdwLogin）来完成的&lt;br /&gt;
&lt;br /&gt;
这是很有用的，因为这不仅意味着所有组件都是附着在window并且移动的时候会带着这些组件一起移动，而且任何对父级window的改变都会被应用到这些子级元素。例如，我们可以隐藏所有的GUI，我们只要隐藏window就行了：&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
guiSetVisible(wdwLogin, false) --隐藏我们制作的所有的GUI，这样我们才能在适当的时候显示GUI&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===使用我们编写完成的功能===&lt;br /&gt;
'''createLoginWindow''' 功能编写完成，但是它'''不会'''执行,必须要我们'''调用'''它才可以执行。建议在'''客户端启动资源时'''创建所有的GUI，当前不需要显示时可以进行隐藏（如果需要立即显示就不用隐藏），或在我们需要用到它的时候显示给玩家。因此，我们需要为GUI窗口创建一个'''事件触发器'''，事件触发器的事件为 &amp;quot;[[onClientResourceStart]]&amp;quot;：&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- 将事件处理程序添加到资源的根元素&lt;br /&gt;
-- 只有在这个资源本启动时才会触发&lt;br /&gt;
addEventHandler(&amp;quot;onClientResourceStart&amp;quot;, getResourceRootElement(), &lt;br /&gt;
	function ()&lt;br /&gt;
		createLoginWindow()&lt;br /&gt;
	end&lt;br /&gt;
)	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
因为脚本是登陆界面，所以我们要在玩家进入游戏后显示出来。&lt;br /&gt;
也可以使用 &amp;quot;[[onClientResourceStart]]&amp;quot; 事件来完成，所以我们将上面的代码进行修改，来达到我们的目的：&lt;br /&gt;
&lt;br /&gt;
'''注意,我们正在为现有的 'onClientResourceStart' 事件处理程序编写更多的代码。不是新创建的一个事件处理程序。''' &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
addEventHandler(&amp;quot;onClientResourceStart&amp;quot;, getResourceRootElement(), &lt;br /&gt;
	function ()&lt;br /&gt;
		-- 创建已经编写好的窗口以及窗口组件&lt;br /&gt;
		createLoginWindow()&lt;br /&gt;
&lt;br /&gt;
		-- 输出一个欢迎信息给玩家&lt;br /&gt;
                outputChatBox(&amp;quot;欢迎来到我的服务器，请登陆。&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
		-- 用if函数判断窗口是否创建成功，如果成功将窗口显示给玩家&lt;br /&gt;
	        if (wdwLogin ~= nil) then&lt;br /&gt;
			guiSetVisible(wdwLogin, true)&lt;br /&gt;
		else&lt;br /&gt;
			-- 如果窗口创建失败时，告诉玩家窗口创建失败&lt;br /&gt;
			outputChatBox(&amp;quot;出现错误，没有成功创建GUI画面&amp;quot;)&lt;br /&gt;
	        end &lt;br /&gt;
&lt;br /&gt;
		-- 启用玩家的鼠标显示，以便点击窗口中的组件&lt;br /&gt;
	        showCursor(true)&lt;br /&gt;
		-- 设置玩家绑定的所有按键都不生效，包括'T '键的本地发言，以保证输入的信息都输入到编辑框内&lt;br /&gt;
	        guiSetInputEnabled(true)&lt;br /&gt;
	end&lt;br /&gt;
)	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
注意,在显示窗口之前,我们使用了一个if函数来判断窗口是否创建成功。创建成功的情况下将显示出来。创建不成功时就意味着 wdwLogin 不是一个元素。服务器也不会出现错误提示。只会提示玩家窗口创建失败。&lt;br /&gt;
下一步我们将给按钮(button)创建点击后的功能&lt;br /&gt;
&lt;br /&gt;
==给按钮写一个功能==&lt;br /&gt;
既然我们已经创建了我们的GUI并且展示给了玩家看，我们需要让它运行。&lt;br /&gt;
&lt;br /&gt;
===检测点击===&lt;br /&gt;
当玩家点击GUI上任何一部分，&amp;quot;[[onClientGUIClick]]&amp;quot; 事件会因为你点击了GUI组建而被触发。这允许我们更简单地去检测我们想要使用的GUI元素上的点击。&lt;br /&gt;
举个例子，我们能够在 bthLogin 按钮上附加这个事件来抓取在这个GUI元素上的任何点击：n it:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- 附加 onClientGUIClick 到 btnLogin 上然后设置触发函数为 'clientSubmitLogin'&lt;br /&gt;
addEventHandler(&amp;quot;onClientGUIClick&amp;quot;, btnLogin, clientSubmitLogin, false)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
'''Note the final argument passed is &amp;quot;false&amp;quot;. This indicates that the event will only trigger directly on btnLogin, not if the event has propagated up or down the tree. Setting this to &amp;quot;true&amp;quot; while attaching to gui elements will mean that clicking on any element in the same branch will trigger this event.'''&lt;br /&gt;
'''注意：最后一个参数传入的是 &amp;quot;false&amp;quot; 。这表明了这个事件只会直接由 btnLogin 触发，不是当时间&lt;br /&gt;
&lt;br /&gt;
This line of code can now be added inside the createLoginWindow function. It is a common mistake to try and attach events to non-existant GUI elements, so make sure you always attach your events '''after''' the gui element (in this case, the button) has been created:&lt;br /&gt;
&lt;br /&gt;
'''注意：我们正在为目前存在的 'createLoginWindow' 函数编写更多的代码。'''&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function createLoginWindow()&lt;br /&gt;
	-- create all our GUI elements&lt;br /&gt;
	...&lt;br /&gt;
&lt;br /&gt;
	-- now add our onClientGUIClick event to the button we just created&lt;br /&gt;
	addEventHandler(&amp;quot;onClientGUIClick&amp;quot;, btnLogin, clientSubmitLogin, false)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Managing the click===&lt;br /&gt;
Now that we can detect when the player clicks on the button, we need to write code to manage what happens when they do.&lt;br /&gt;
In our [[onClientGUIClick]] event handle, we told it to call the function clientSubmitLogin whenever btnLogin is clicked.&lt;br /&gt;
Therefore, we can now use the function clientSubmitLogin to control what happens when the button is clicked:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- create the function and define the 'button' and 'state' parameters&lt;br /&gt;
-- (these are passed automatically by onClientGUIClick)&lt;br /&gt;
function clientSubmitLogin(button,state)&lt;br /&gt;
	-- if our login button was clicked with the left mouse button, and the state of the mouse button is up&lt;br /&gt;
	if button == &amp;quot;left&amp;quot; and state == &amp;quot;up&amp;quot; then&lt;br /&gt;
		-- move the input focus back onto the game (allowing players to move around, open the chatbox, etc)&lt;br /&gt;
		guiSetInputEnabled(false)&lt;br /&gt;
		-- hide the window and all the components&lt;br /&gt;
		guiSetVisible(wdwLogin, false)&lt;br /&gt;
		-- hide the mouse cursor&lt;br /&gt;
		showCursor(false)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Now, when the button is clicked, the window will be hidden and all controls will be returned to the player. Next, we will tell the server to allow the player to spawn.&lt;br /&gt;
&lt;br /&gt;
===触发服务器事件===&lt;br /&gt;
客户端触发服务器端事件使用 [[triggerServerEvent]] 函数来完成.这个函数可以从客户端上触发指定的服务器端事件.同样的,服务器端触发客户端事件使用 [[triggerClientEvent]] 函数完成.因为我们要触发'''服务器端'''事件,所以在这里我们使用 [[triggerServerEvent]] 函数来调用我们'''自定义'''的服务器事件,服务器事件命名为 &amp;quot;submitLogin&amp;quot;,这个事件能够控制玩家的重生.&lt;br /&gt;
&lt;br /&gt;
'''注意,我们是在为现有的功能 'clientSubmitLogin' 编写更多的代码,并不是一个全新的功能.''' &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function clientSubmitLogin(button,state)&lt;br /&gt;
	if button == &amp;quot;left&amp;quot; and state == &amp;quot;up&amp;quot; then&lt;br /&gt;
		-- 获取账户编辑框中的账户名&lt;br /&gt;
		local username = guiGetText(edtUser)&lt;br /&gt;
		-- 获取密码编辑框中的密码&lt;br /&gt;
		local password = guiGetText(edtPass)&lt;br /&gt;
&lt;br /&gt;
		-- 如果用户名和密码都不为空时向下执行&lt;br /&gt;
		if username and password then&lt;br /&gt;
			-- 调用服务器端事件 'submitLogin' 并向服务器端传递账户名和密码&lt;br /&gt;
			triggerServerEvent(&amp;quot;submitLogin&amp;quot;, getRootElement(), username, password)&lt;br /&gt;
&lt;br /&gt;
			-- 恢复玩家的按键绑定(比如T键本地聊天),隐藏玩家的GUI画面,取消玩家鼠标的显示&lt;br /&gt;
			guiSetInputEnabled(false)&lt;br /&gt;
			guiSetVisible(wdwLogin, false)&lt;br /&gt;
			showCursor(false)&lt;br /&gt;
		else&lt;br /&gt;
			-- 用户名或密码有一个为空时,向玩家返回消息,并且不触发服务端事件&lt;br /&gt;
			-- 同时不隐藏GUI画面&lt;br /&gt;
			outputChatBox(&amp;quot;请输入用户名和密码。&amp;quot;)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===创建服务器事件===&lt;br /&gt;
'''我们现在已经编写完成我们所需的客户端代码，我们下面需要编写服务器端代码，服务器端代码需要写在 server 文件夹中的 server.lua 文件，因此我们打开 server.lua 文件，进行下面的编写。'''&lt;br /&gt;
&lt;br /&gt;
在服务器端上，我们想一下刚才所写的客户端代码，玩家一旦登陆就生成（重生）。&lt;br /&gt;
所以，现在我们需要添加'''客户端'''调用的服务器端事件（submitLogin），我们可以用 [[addEvent]] 和 [[addEventHandler]] 来完成.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- 创建一个名为 loginHandler 的功能，使用额外的两个参数 username 和 password （这是从客户端调用时传递的）&lt;br /&gt;
function loginHandler(username,password)&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- 我们添加自定义事件，命名为 submitLogin（事件名需要打英文双引号）。并允许客户端调用它（后面的true参数）。&lt;br /&gt;
addEvent(&amp;quot;submitLogin&amp;quot;,true)&lt;br /&gt;
-- 添加一个事件处理程序，在 submitLogin 事件触发时调用 loginHandler 功能。&lt;br /&gt;
addEventHandler(&amp;quot;submitLogin&amp;quot;,root,loginHandler)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===登陆===&lt;br /&gt;
现在我们已经定义好事件 'submitLogin' 以及调用时触发的功能，现在我们需要给 'loginHandler' 功能编写更多的代码以实现登陆和生成（重生）玩家。&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function loginHandler(username,password)&lt;br /&gt;
	-- 用 if 函数判断用户名和密码是否正确&lt;br /&gt;
	if username == &amp;quot;user&amp;quot; and password == &amp;quot;apple&amp;quot; then&lt;br /&gt;
		-- 判断 client 变量是否存在，以便生成（重生）玩家&lt;br /&gt;
		if (client) then&lt;br /&gt;
			spawnPlayer(client, 1959.55, -1714.46, 10)&lt;br /&gt;
			fadeCamera(client, true)&lt;br /&gt;
                        setCameraTarget(client, client)&lt;br /&gt;
			outputChatBox(&amp;quot;欢迎来到我的服务器&amp;quot;, client)&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		-- 如果用户名或密码不正确向玩家输出错误信息&lt;br /&gt;
		outputChatBox(&amp;quot;用户名或密码错误，请重新链接登陆。&amp;quot;,client)&lt;br /&gt;
        end			&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
addEvent(&amp;quot;submitLogin&amp;quot;,true)&lt;br /&gt;
addEventHandler(&amp;quot;submitLogin&amp;quot;,root,loginHandler)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
'''为了教程的目的，本教程中使用的非常简单的用户名（user）和密码（apple）。在服务器安全方面，你可以才用MySql数据库来存储用户的账户和密码以及数据信息。'''&lt;br /&gt;
&lt;br /&gt;
要注意 '''client''' 变量的使用，它是MTA用来标识触发服务端事件的'''客户端玩家'''（也就是说 client 变量就是触发（调用）服务器端事件的那个'''玩家'''）。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
最后，别忘记修改 meta.xml 文件，在&amp;lt;meta&amp;gt;&amp;lt;/meta&amp;gt;中加入下面两行，不要忘记将 gui.lua 标记为客户端属性（因为GUI是在客户端使用的），将 server.lua 标记为服务器端属性（因为 server.lua 中代码是需要在服务端中执行的）。&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;client/gui.lua&amp;quot; type=&amp;quot;client&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;server/server.lua&amp;quot; type=&amp;quot;server&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
我们现在完成了一个简单的登陆窗口，在点击登陆时会检测用户名和密码是正确，在正确的情况下生成（重生）出玩家。&lt;br /&gt;
&lt;br /&gt;
有关GUI的进一步帮助,请参考 [[:Category:GUI_Tutorials|GUI tutorials]].&lt;br /&gt;
&lt;br /&gt;
[[Category:GUI_Tutorials]]&lt;br /&gt;
[[it:Introduzione_allo_scripting_della_GUI]]&lt;br /&gt;
[[ru:Introduction to Scripting the GUI]]&lt;br /&gt;
[[es:Introducción a la Programación de GUI]]&lt;/div&gt;</summary>
		<author><name>Skyvzla</name></author>
	</entry>
	<entry>
		<id>https://wiki.multitheftauto.com/index.php?title=ZH-CN/%E8%84%9A%E6%9C%AC%E7%BC%96%E5%86%99%E4%BB%8B%E7%BB%8D_-_%E5%B8%A6%E6%9C%89%E5%9B%BE%E5%BD%A2%E7%95%8C%E9%9D%A2&amp;diff=50430</id>
		<title>ZH-CN/脚本编写介绍 - 带有图形界面</title>
		<link rel="alternate" type="text/html" href="https://wiki.multitheftauto.com/index.php?title=ZH-CN/%E8%84%9A%E6%9C%AC%E7%BC%96%E5%86%99%E4%BB%8B%E7%BB%8D_-_%E5%B8%A6%E6%9C%89%E5%9B%BE%E5%BD%A2%E7%95%8C%E9%9D%A2&amp;diff=50430"/>
		<updated>2017-02-12T06:41:33Z</updated>

		<summary type="html">&lt;p&gt;Skyvzla: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- place holder --&amp;gt;&lt;br /&gt;
在MTA:SA中有一个重要的特点，那就是可以制作自定义的GUI(Graphic User Interface)。GUI由窗口，按钮，编辑框，复选框等组成，拥有在图形环境中大部分的基础控件。它们可以在玩家正在游戏的时候显示，常常被用来代替传统命令的输入和输出。&lt;br /&gt;
&lt;br /&gt;
[[Image:AdminGUI.png|thumb|管理员后台GUI]]&lt;br /&gt;
&lt;br /&gt;
==一个做登陆窗口的教程==&lt;br /&gt;
在这个教程中我们会使用两个编辑框和一个按钮制作一个登录窗口。 窗口会在玩家加入游戏的时候出现，一旦按钮被点击，玩家将会被出生。 这个教程会继续我们在 [[Scripting Introduction|脚本介绍]] 中制作的游戏模式''（如果你已经使用了 [[Scripting Introduction|脚本介绍]]，你需要在你的代码中移除或者注释掉在 &amp;quot;joinHandler&amp;quot; 行的 [[spawnPlayer]] 函数， 我们也将会在这个教程中使用一个gui替换掉它）''。 我们也会接触一下客户端脚本的编辑。&lt;br /&gt;
&lt;br /&gt;
===画一个窗口===&lt;br /&gt;
所有的GUI只能在客户端编写。 这是一个很好的练习机会来把所有客户端脚本放在分开的目录。&lt;br /&gt;
&lt;br /&gt;
浏览到目录 /你的MTA服务端目录/mods/deathmatch/resources/myserver/，创建名为 client 和 server 两个文件夹，在 client 中创建一个名为 gui.lua 的文件 ，在 server 中创建一个名为 server.lua 的文件。&lt;br /&gt;
&lt;br /&gt;
整个 myserver 脚本文件夹中文件情况如下：&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
/myserver&lt;br /&gt;
	/meta.xml&lt;br /&gt;
	/script.lua&lt;br /&gt;
	/server&lt;br /&gt;
		/server.lua&lt;br /&gt;
	/client&lt;br /&gt;
		/gui.lua&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''注意，我们所有编写的代码必须要使用 UTF-8 编码脚本才能常执行（否则你输出的中文等会出现乱码）。'''&lt;br /&gt;
&lt;br /&gt;
现在我们打开 gui.lua文件，我们将会写一个画窗口的函数。使用[[guiCreateWindow]]来创建一个窗口：&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function createLoginWindow()&lt;br /&gt;
	-- 定义窗口的X和Y坐标&lt;br /&gt;
	local X = 0.375&lt;br /&gt;
	local Y = 0.375&lt;br /&gt;
	-- 定义窗口的宽度和高度&lt;br /&gt;
	local Width = 0.25&lt;br /&gt;
	local Height = 0.25&lt;br /&gt;
        -- 创建一个窗口，并且把它放入变量 'wdwLogin' 里&lt;br /&gt;
        -- 点击这个函数的名字可以阅读这个函数的相关信息&lt;br /&gt;
	wdwLogin = guiCreateWindow(X, Y, Width, Height, &amp;quot;请登录&amp;quot;, true)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===相对和绝对坐标===&lt;br /&gt;
注意：传给 guiCreateWindow 的最后一个参数在上面的例子中是 ''true''。这个表明了窗口的坐标和尺寸都是 '''相对的'''，也就是说他们是一个相对于屏幕大小的 ''百分比''。我来解释一下：如果屏幕最左侧是0，那么最右侧就是1，那么X坐标为0.5将代表着屏幕中央。同理，屏幕顶部和底部也是一样的，最顶部为0，最底部为1，Y坐标为0.2则代表着是屏幕高度的20%。宽度和高度也是一样的道理（宽度为0.5则意味着窗口是屏幕的一半宽）。&lt;br /&gt;
&lt;br /&gt;
另外的，也可以用 '''绝对的''' （将传入 guiCreateWindow 的最后一个参数改为 ''false'' 即可）。绝对坐标被计算为父级的左上角开始到右下角的像素总数（如果没有GUI元素指定父级，那么父级是屏幕本身）。 如果我们假设屏幕的分辨率为1920*1200，那么从屏幕左边开始为0像素，到屏幕右边为1920像素，X坐标为960代表屏幕的中点。同理，屏幕顶部为0，到屏幕底部则为1200，Y坐标为20代表着距离屏幕顶部的20个像素。宽度和高度也是相同的道理（宽度为50则意味着窗口为50个像素宽）。 ''你可以使用 [[guiGetScreenSize]] 和一点数学来计算某些绝对坐标。''&lt;br /&gt;
&lt;br /&gt;
使用相对坐标和绝对坐标的不同点非常简单：使用绝对坐标创建gui经常精确地保持着相同的像素大小和坐标，然而使用相对坐标创建gui经常是与它父级gui大小的比值。&lt;br /&gt;
&lt;br /&gt;
当你用手敲代码的时候，绝对坐标一般来说更容易维护。然而你的选择是根据你的目的而变化的。&lt;br /&gt;
&lt;br /&gt;
为了本介绍的目的，我们将使用相对坐标。&lt;br /&gt;
&lt;br /&gt;
===添加组件===&lt;br /&gt;
接下来， 我们要添加文本标签（里面写上 “帐号：” 和 “密码：”）、编辑框（为了输入你的数据）和一个登录按钮。&lt;br /&gt;
&lt;br /&gt;
我们可以使用 [[guiCreateButtonTo]] 创建按钮，创建 [[guiCreateEdit]] 创建编辑框。&lt;br /&gt;
&lt;br /&gt;
'''注意：我们现在正在给我们已经存在的 ‘createLoginWindow’函数写更多的代码。这不是一个新的函数，这个函数是用来替换你脚本中已经存在的那个。''' &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function createLoginWindow()&lt;br /&gt;
	local X = 0.375&lt;br /&gt;
	local Y = 0.375&lt;br /&gt;
	local Width = 0.25&lt;br /&gt;
	local Height = 0.25&lt;br /&gt;
	wdwLogin = guiCreateWindow(X, Y, Width, Height, &amp;quot;请登录&amp;quot;, true)&lt;br /&gt;
	&lt;br /&gt;
	-- 给第一个文本标签定义新的X和Y坐标&lt;br /&gt;
	X = 0.0825&lt;br /&gt;
	Y = 0.2&lt;br /&gt;
	-- 给第一个文本标签定义新的宽度和高度&lt;br /&gt;
	Width = 0.25&lt;br /&gt;
	Height = 0.25&lt;br /&gt;
	-- 创建第一个文本标签，注意最后一个传入的参数是 ‘wdwLogin’，这个参数是你刚刚创建的窗口（window）&lt;br /&gt;
	-- 我们在它的父级gui上方创建了这个文本标签（所以所有的坐标和大小的值都是相对于这个窗口的）&lt;br /&gt;
	guiCreateLabel(X, Y, Width, Height, &amp;quot;帐号&amp;quot;, true, wdwLogin)&lt;br /&gt;
	-- 改变Y的值，所以第二个文本标签是在第一个下方的&lt;br /&gt;
	Y = 0.5&lt;br /&gt;
	guiCreateLabel(X, Y, Width, Height, &amp;quot;密码&amp;quot;, true, wdwLogin)&lt;br /&gt;
	&lt;br /&gt;
&lt;br /&gt;
	X = 0.415&lt;br /&gt;
	Y = 0.2&lt;br /&gt;
	Width = 0.5&lt;br /&gt;
	Height = 0.15&lt;br /&gt;
	edtUser = guiCreateEdit(X, Y, Width, Height, &amp;quot;&amp;quot;, true, wdwLogin)&lt;br /&gt;
	Y = 0.5&lt;br /&gt;
	edtPass = guiCreateEdit(X, Y, Width, Height, &amp;quot;&amp;quot;, true, wdwLogin)&lt;br /&gt;
	-- 设置编辑框的最大文字长度为50&lt;br /&gt;
	guiEditSetMaxLength(edtUser, 50)&lt;br /&gt;
	guiEditSetMaxLength(edtPass, 50)&lt;br /&gt;
	&lt;br /&gt;
	X = 0.415&lt;br /&gt;
	Y = 0.7&lt;br /&gt;
	Width = 0.25&lt;br /&gt;
	Height = 0.2&lt;br /&gt;
	btnLogin = guiCreateButton(X, Y, Width, Height, &amp;quot;Log In&amp;quot;, true, wdwLogin)&lt;br /&gt;
	&lt;br /&gt;
	-- 让这个窗口不显示&lt;br /&gt;
	guiSetVisible(wdwLogin, false)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
注意：每个GUI组件都是创建为window的子级，这是当创建组件时通过指定父级元素（在这种情况下是wdwLogin）来完成的&lt;br /&gt;
&lt;br /&gt;
这是很有用的，因为这不仅意味着所有组件都是附着在window并且移动的时候会带着这些组件一起移动，而且任何对父级window的改变都会被应用到这些子级元素。例如，我们可以隐藏所有的GUI，我们只要隐藏window就行了：&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
guiSetVisible(wdwLogin, false) --隐藏我们制作的所有的GUI，这样我们才能在适当的时候显示GUI&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===使用我们编写完成的功能===&lt;br /&gt;
'''createLoginWindow''' 功能编写完成，但是它'''不会'''执行,必须要我们'''调用'''它才可以执行。建议在'''客户端启动资源时'''创建所有的GUI，当前不需要显示时可以进行隐藏（如果需要立即显示就不用隐藏），或在我们需要用到它的时候显示给玩家。因此，我们需要为GUI窗口创建一个'''事件触发器'''，事件触发器的事件为 &amp;quot;[[onClientResourceStart]]&amp;quot;：&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- 将事件处理程序添加到资源的根元素&lt;br /&gt;
-- 只有在这个资源本启动时才会触发&lt;br /&gt;
addEventHandler(&amp;quot;onClientResourceStart&amp;quot;, getResourceRootElement(), &lt;br /&gt;
	function ()&lt;br /&gt;
		createLoginWindow()&lt;br /&gt;
	end&lt;br /&gt;
)	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
因为脚本是登陆界面，所以我们要在玩家进入游戏后显示出来。&lt;br /&gt;
也可以使用 &amp;quot;[[onClientResourceStart]]&amp;quot; 事件来完成，所以我们将上面的代码进行修改，来达到我们的目的：&lt;br /&gt;
&lt;br /&gt;
'''注意,我们正在为现有的 'onClientResourceStart' 事件处理程序编写更多的代码。不是新创建的一个事件处理程序。''' &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
addEventHandler(&amp;quot;onClientResourceStart&amp;quot;, getResourceRootElement(), &lt;br /&gt;
	function ()&lt;br /&gt;
		-- 创建已经编写好的窗口以及窗口组件&lt;br /&gt;
		createLoginWindow()&lt;br /&gt;
&lt;br /&gt;
		-- 输出一个欢迎信息给玩家&lt;br /&gt;
                outputChatBox(&amp;quot;Welcome to My MTA:SA Server, please log in.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
		-- 用if函数判断窗口是否创建成功，如果成功将窗口显示给玩家&lt;br /&gt;
	        if (wdwLogin ~= nil) then&lt;br /&gt;
			guiSetVisible(wdwLogin, true)&lt;br /&gt;
		else&lt;br /&gt;
			-- 如果窗口创建失败时，告诉玩家窗口创建失败&lt;br /&gt;
			outputChatBox(&amp;quot;An unexpected error has occurred and the log in GUI has not been created.&amp;quot;)&lt;br /&gt;
	        end &lt;br /&gt;
&lt;br /&gt;
		-- 启用玩家的鼠标显示，以便点击窗口中的组件&lt;br /&gt;
	        showCursor(true)&lt;br /&gt;
		-- 设置玩家绑定的所有按键都不生效，包括'T '键的本地发言，以保证输入的信息都输入到编辑框内&lt;br /&gt;
	        guiSetInputEnabled(true)&lt;br /&gt;
	end&lt;br /&gt;
)	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
注意,在显示窗口之前,我们使用了一个if函数来判断窗口是否创建成功。创建成功的情况下将显示出来。创建不成功时就意味着 wdwLogin 不是一个元素。服务器也不会出现错误提示。只会提示玩家窗口创建失败。&lt;br /&gt;
下一步我们将给按钮(button)创建点击后的功能&lt;br /&gt;
&lt;br /&gt;
==给按钮写一个功能==&lt;br /&gt;
既然我们已经创建了我们的GUI并且展示给了玩家看，我们需要让它运行。&lt;br /&gt;
&lt;br /&gt;
===检测点击===&lt;br /&gt;
当玩家点击GUI上任何一部分，&amp;quot;[[onClientGUIClick]]&amp;quot; 事件会因为你点击了GUI组建而被触发。这允许我们更简单地去检测我们想要使用的GUI元素上的点击。&lt;br /&gt;
举个例子，我们能够在 bthLogin 按钮上附加这个事件来抓取在这个GUI元素上的任何点击：n it:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- 附加 onClientGUIClick 到 btnLogin 上然后设置触发函数为 'clientSubmitLogin'&lt;br /&gt;
addEventHandler(&amp;quot;onClientGUIClick&amp;quot;, btnLogin, clientSubmitLogin, false)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
'''Note the final argument passed is &amp;quot;false&amp;quot;. This indicates that the event will only trigger directly on btnLogin, not if the event has propagated up or down the tree. Setting this to &amp;quot;true&amp;quot; while attaching to gui elements will mean that clicking on any element in the same branch will trigger this event.'''&lt;br /&gt;
'''注意：最后一个参数传入的是 &amp;quot;false&amp;quot; 。这表明了这个事件只会直接由 btnLogin 触发，不是当时间&lt;br /&gt;
&lt;br /&gt;
This line of code can now be added inside the createLoginWindow function. It is a common mistake to try and attach events to non-existant GUI elements, so make sure you always attach your events '''after''' the gui element (in this case, the button) has been created:&lt;br /&gt;
&lt;br /&gt;
'''注意：我们正在为目前存在的 'createLoginWindow' 函数编写更多的代码。'''&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function createLoginWindow()&lt;br /&gt;
	-- create all our GUI elements&lt;br /&gt;
	...&lt;br /&gt;
&lt;br /&gt;
	-- now add our onClientGUIClick event to the button we just created&lt;br /&gt;
	addEventHandler(&amp;quot;onClientGUIClick&amp;quot;, btnLogin, clientSubmitLogin, false)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Managing the click===&lt;br /&gt;
Now that we can detect when the player clicks on the button, we need to write code to manage what happens when they do.&lt;br /&gt;
In our [[onClientGUIClick]] event handle, we told it to call the function clientSubmitLogin whenever btnLogin is clicked.&lt;br /&gt;
Therefore, we can now use the function clientSubmitLogin to control what happens when the button is clicked:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- create the function and define the 'button' and 'state' parameters&lt;br /&gt;
-- (these are passed automatically by onClientGUIClick)&lt;br /&gt;
function clientSubmitLogin(button,state)&lt;br /&gt;
	-- if our login button was clicked with the left mouse button, and the state of the mouse button is up&lt;br /&gt;
	if button == &amp;quot;left&amp;quot; and state == &amp;quot;up&amp;quot; then&lt;br /&gt;
		-- move the input focus back onto the game (allowing players to move around, open the chatbox, etc)&lt;br /&gt;
		guiSetInputEnabled(false)&lt;br /&gt;
		-- hide the window and all the components&lt;br /&gt;
		guiSetVisible(wdwLogin, false)&lt;br /&gt;
		-- hide the mouse cursor&lt;br /&gt;
		showCursor(false)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Now, when the button is clicked, the window will be hidden and all controls will be returned to the player. Next, we will tell the server to allow the player to spawn.&lt;br /&gt;
&lt;br /&gt;
===触发服务器事件===&lt;br /&gt;
客户端触发服务器端事件使用 [[triggerServerEvent]] 函数来完成.这个函数可以从客户端上触发指定的服务器端事件.同样的,服务器端触发客户端事件使用 [[triggerClientEvent]] 函数完成.因为我们要触发'''服务器端'''事件,所以在这里我们使用 [[triggerServerEvent]] 函数来调用我们'''自定义'''的服务器事件,服务器事件命名为 &amp;quot;submitLogin&amp;quot;,这个事件能够控制玩家的重生.&lt;br /&gt;
&lt;br /&gt;
'''注意,我们是在为现有的功能 'clientSubmitLogin' 编写更多的代码,并不是一个全新的功能.''' &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function clientSubmitLogin(button,state)&lt;br /&gt;
	if button == &amp;quot;left&amp;quot; and state == &amp;quot;up&amp;quot; then&lt;br /&gt;
		-- 获取账户编辑框中的账户名&lt;br /&gt;
		local username = guiGetText(edtUser)&lt;br /&gt;
		-- 获取密码编辑框中的密码&lt;br /&gt;
		local password = guiGetText(edtPass)&lt;br /&gt;
&lt;br /&gt;
		-- 如果用户名和密码都不为空时向下执行&lt;br /&gt;
		if username and password then&lt;br /&gt;
			-- 调用服务器端事件 'submitLogin' 并向服务器端传递账户名和密码&lt;br /&gt;
			triggerServerEvent(&amp;quot;submitLogin&amp;quot;, getRootElement(), username, password)&lt;br /&gt;
&lt;br /&gt;
			-- 恢复玩家的按键绑定(比如T键本地聊天),隐藏玩家的GUI画面,取消玩家鼠标的显示&lt;br /&gt;
			guiSetInputEnabled(false)&lt;br /&gt;
			guiSetVisible(wdwLogin, false)&lt;br /&gt;
			showCursor(false)&lt;br /&gt;
		else&lt;br /&gt;
			-- 用户名或密码有一个为空时,向玩家返回消息,并且不触发服务端事件&lt;br /&gt;
			-- 同时不隐藏GUI画面&lt;br /&gt;
			outputChatBox(&amp;quot;Please enter a username and password.&amp;quot;)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===创建服务器事件===&lt;br /&gt;
'''我们现在已经编写完成我们所需的客户端代码，我们下面需要编写服务器端代码，服务器端代码需要写在 server 文件夹中的 server.lua 文件，因此我们打开 server.lua 文件，进行下面的编写。'''&lt;br /&gt;
&lt;br /&gt;
在服务器端上，我们想一下刚才所写的客户端代码，玩家一旦登陆就生成（重生）。&lt;br /&gt;
所以，现在我们需要添加'''客户端'''调用的服务器端事件（submitLogin），我们可以用 [[addEvent]] 和 [[addEventHandler]] 来完成.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- 创建一个名为 loginHandler 的功能，使用额外的两个参数 username 和 password （这是从客户端调用时传递的）&lt;br /&gt;
function loginHandler(username,password)&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- 我们添加自定义事件，命名为 submitLogin（事件名需要打英文双引号）。并允许客户端调用它（后面的true参数）。&lt;br /&gt;
addEvent(&amp;quot;submitLogin&amp;quot;,true)&lt;br /&gt;
-- 添加一个事件处理程序，在 submitLogin 事件触发时调用 loginHandler 功能。&lt;br /&gt;
addEventHandler(&amp;quot;submitLogin&amp;quot;,root,loginHandler)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===登陆===&lt;br /&gt;
现在我们已经定义好事件 'submitLogin' 以及调用时触发的功能，现在我们需要给 'loginHandler' 功能编写更多的代码以实现登陆和生成（重生）玩家。&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function loginHandler(username,password)&lt;br /&gt;
	-- 用 if 函数判断用户名和密码是否正确&lt;br /&gt;
	if username == &amp;quot;user&amp;quot; and password == &amp;quot;apple&amp;quot; then&lt;br /&gt;
		-- 判断 client 变量是否存在，以便生成（重生）玩家&lt;br /&gt;
		if (client) then&lt;br /&gt;
			spawnPlayer(client, 1959.55, -1714.46, 10)&lt;br /&gt;
			fadeCamera(client, true)&lt;br /&gt;
                        setCameraTarget(client, client)&lt;br /&gt;
			outputChatBox(&amp;quot;Welcome to My Server.&amp;quot;, client)&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		-- 如果用户名或密码不正确向玩家输出错误信息&lt;br /&gt;
		outputChatBox(&amp;quot;Invalid username and password. Please re-connect and try again.&amp;quot;,client)&lt;br /&gt;
        end			&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
addEvent(&amp;quot;submitLogin&amp;quot;,true)&lt;br /&gt;
addEventHandler(&amp;quot;submitLogin&amp;quot;,root,loginHandler)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
'''为了教程的目的，本教程中使用的非常简单的用户名（user）和密码（apple）。在服务器安全方面，你可以才用MySql数据库来存储用户的账户和密码以及数据信息。'''&lt;br /&gt;
&lt;br /&gt;
要注意 '''client''' 变量的使用，它是MTA用来标识触发服务端事件的'''客户端玩家'''（也就是说 client 变量就是触发（调用）服务器端事件的那个'''玩家'''）。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
最后，别忘记修改 meta.xml 文件，在&amp;lt;meta&amp;gt;&amp;lt;/meta&amp;gt;中加入下面两行，不要忘记将 gui.lua 标记为客户端属性（因为GUI是在客户端使用的），将 server.lua 标记为服务器端属性（因为 server.lua 中代码是需要在服务端中执行的）。&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;client/gui.lua&amp;quot; type=&amp;quot;client&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;server/server.lua&amp;quot; type=&amp;quot;server&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
我们现在完成了一个简单的登陆窗口，在点击登陆时会检测用户名和密码是正确，在正确的情况下生成（重生）出玩家。&lt;br /&gt;
&lt;br /&gt;
有关GUI的进一步帮助,请参考 [[:Category:GUI_Tutorials|GUI tutorials]].&lt;br /&gt;
&lt;br /&gt;
[[Category:GUI_Tutorials]]&lt;br /&gt;
[[it:Introduzione_allo_scripting_della_GUI]]&lt;br /&gt;
[[ru:Introduction to Scripting the GUI]]&lt;br /&gt;
[[es:Introducción a la Programación de GUI]]&lt;/div&gt;</summary>
		<author><name>Skyvzla</name></author>
	</entry>
	<entry>
		<id>https://wiki.multitheftauto.com/index.php?title=ZH-CN/%E8%84%9A%E6%9C%AC%E7%BC%96%E5%86%99%E4%BB%8B%E7%BB%8D&amp;diff=50429</id>
		<title>ZH-CN/脚本编写介绍</title>
		<link rel="alternate" type="text/html" href="https://wiki.multitheftauto.com/index.php?title=ZH-CN/%E8%84%9A%E6%9C%AC%E7%BC%96%E5%86%99%E4%BB%8B%E7%BB%8D&amp;diff=50429"/>
		<updated>2017-02-12T05:27:54Z</updated>

		<summary type="html">&lt;p&gt;Skyvzla: /* 脚本都在哪里存放着？ */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;对于 MTA 服务器而言，“资源” (Resource) 是十分重要的一个概念。一个资源本身表现为一个包含了一系列文件的文件夹或压缩文件 (zip)，外包含一个用于指示服务器如何加载资源以及资源所包含文件说明的元数据文件 (Meta File)。可把资源的概念视作是运行在操作系统之上的程序 —— 它可以被启动或停止执行，且多个资源可以同时运行在服务器上。&lt;br /&gt;
&lt;br /&gt;
所谓脚本编程，本质上即是在编写资源。资源可以定义它自身的类型为游戏模式、地图等。MTA 自带一些可供您的游戏模式使用的可选辅助用资源；例如 maplimits，一个用于限制玩家行动范围的资源；或是 deathpickups，用于创建武器拾取物以供辅助死亡竞技 (Deathmatch) 游戏模式。&lt;br /&gt;
{{Tip|您应该使用支持 Lua 格式代码的编辑器以编写脚本，以提高脚本编写的效率。我们建议使用 [http://notepad-plus.sourceforge.net/uk/site.htm Notepad++] 或 [http://luaedit.sourceforge.net/ LuaEdit]。我们也提供测试版的 [[MTASE|MTA 脚本编辑器]]（目前仍在开发中）。}}&lt;br /&gt;
&lt;br /&gt;
==创建一个脚本==&lt;br /&gt;
第一步，我们将开始一步一步学习如何编写一个能让玩家到处行走的简单脚本。&lt;br /&gt;
===脚本都在哪里存放着？===&lt;br /&gt;
首先让我们来看看脚本的文件结构。打开 MTA 服务器目录，并定位到以下路径：&lt;br /&gt;
&lt;br /&gt;
	server/mods/deathmatch/resources/&lt;br /&gt;
&lt;br /&gt;
在该目录下，可以看到一系列 MTA 自带的示例脚本压缩文件。每个压缩文件都是一个 “资源”，在服务器启动时它们会被自动解压并被加载到服务器上运行。若需创建一个新资源，只需在该目录下新建一个任意名称的文件夹。在本教程中我们以 &amp;quot;myserver&amp;quot; 资源作为演示，注意：脚本资源创建时建议使用英文命名的文件夹(英文开头可带阿拉伯数字)。&lt;br /&gt;
&lt;br /&gt;
现在请定位到以下路径：&lt;br /&gt;
&lt;br /&gt;
	server/mods/deathmatch/resources/myserver/&lt;br /&gt;
&lt;br /&gt;
===定义您的资源===&lt;br /&gt;
为了能够让服务器得知资源内所包含的数据，每个资源内都必须包含一个位于资源根目录下的 &amp;quot;meta.xml&amp;quot; 文件（本例即 &amp;quot;myserver&amp;quot; 文件夹为资源根目录）。因此我们需要新建该文件，并以记事本方式打开。&lt;br /&gt;
&lt;br /&gt;
在 &amp;quot;meta.xml&amp;quot; 文件内输入以下的代码：&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;meta&amp;gt;&lt;br /&gt;
     &amp;lt;info author=&amp;quot;YourName&amp;quot; type=&amp;quot;gamemode&amp;quot; name=&amp;quot;My Server&amp;quot; description=&amp;quot;My first MTA server&amp;quot; /&amp;gt;&lt;br /&gt;
     &amp;lt;script src=&amp;quot;script.lua&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/meta&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
在 &amp;quot;&amp;lt;info /&amp;gt;&amp;quot; 标签中，有一个用于指定资源类型的 &amp;quot;type&amp;quot; 属性。以上代码中指定了该资源类型为 &amp;quot;gamemode&amp;quot;（游戏模式），其他可选的资源类型还有常规包含文件或 &amp;quot;map&amp;quot;（地图），后文将会详细解释。目前只需要知道游戏模式是所有服务器的必不可少的核心，任意服务器都必须要有一个游戏模式。&lt;br /&gt;
&lt;br /&gt;
&amp;quot;&amp;lt;script /&amp;gt;&amp;quot; 标签指明了该资源内所包含的脚本文件，后文将会详细解释。&lt;br /&gt;
===创建一个简单的脚本===&lt;br /&gt;
注意上例代码中的 &amp;quot;&amp;lt;script /&amp;gt;&amp;quot; 标签中并没有将 .lua 文件指定在其他目录路径下，因此现在需要创建一个与 meta.xml 文件同目录下的 .lua 脚本文件；随后，请把以下代码复制到新创建的 script.lua 文件中：&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
local spawnX, spawnY, spawnZ = 1959.55, -1714.46, 10&lt;br /&gt;
function joinHandler()&lt;br /&gt;
	spawnPlayer(source, spawnX, spawnY, spawnZ)&lt;br /&gt;
	fadeCamera(source, true)&lt;br /&gt;
	setCameraTarget(source, source)&lt;br /&gt;
	outputChatBox(&amp;quot;Welcome to My Server&amp;quot;, source)&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerJoin&amp;quot;, getRootElement(), joinHandler)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
当玩家进入服务器后，脚本会将玩家刷到上例代码所指定的出生点坐标 (x, y, z)。注意必须要使用 &amp;quot;fadeCamera&amp;quot; 函数以避免屏幕全黑的情况；同样，DP2 以后的 MTA 版本中，也同样需要使用 &amp;quot;setCameraTarget&amp;quot; 函数以设置玩家摄像机的目标，否则玩家进入服务器后只会见到游戏内蓝色的天空。&lt;br /&gt;
&lt;br /&gt;
'''source''' 变量表示的是事件的触发者。由于以上代码会在玩家进入服务器时被触发，因此您需要使用该变量以确定是哪位玩家进入了服务器。因此上例代码只会影响到新进入了服务器的玩家，而非服务器内的所有玩家。&lt;br /&gt;
&lt;br /&gt;
上例代码中，我们可以看到一个 [[addEventHandler]] 函数；从中可看见 3 个参数：'onPlayerJoin'，其表明何时代码会被触发；getRootElement()，其表明了可能的事件触发者（getRootElement() 表示的是游戏内的一切事物或玩家，因此该事件可能是由游戏内的任意事物或玩家所触发的）。以及 joinHandler，其表明哪个函数在该事件 ('onPlayerJoin') 触发后会被执行。其他的代码细节会在后文中详细介绍，现在就启动服务器然后测试一下代码效果吧！&lt;br /&gt;
&lt;br /&gt;
===执行脚本===&lt;br /&gt;
只需要执行 server/ 目录下的可执行文件即可启动服务器。启动服务器后，首先可以从服务器后台上看到一系列的服务器基本信息；注意 port number（端口数）信息，在进入服务器前需要使用到该信息。随后服务器会加载所有位于 mods/deathmatch/resources 目录下的资源，最后会看到提示 &amp;quot;ready to accept connections!&amp;quot;，即表明服务器启动成功。&lt;br /&gt;
&lt;br /&gt;
在玩家进入服务器前，必须要首先加载游戏模式。在服务器后台输入指令 &amp;quot;start myserver&amp;quot; 并按下回车键发送指令。服务器便会立即启动您刚刚所创建的游戏模式，并列出该游戏模式内所有的脚本代码性错误及警告提示。现在可以启动 MTA 客户端，点击 &amp;quot;Quick Connect&amp;quot; 并输入您在服务器后台上所看到的服务器 IP 地址及端口数。如果没有错误，几秒过后玩家就会出生在 Los Santos。&lt;br /&gt;
&lt;br /&gt;
接下来，我们将会在脚本中添加一条用于在玩家附近刷出交通工具的游戏指令。您可以跳过本章节并阅读更为高级的[[Map manager|地图管理器]]教程；您也可以阅读[[图形操作界面 (GUI) 脚本编写介绍]]教程以学习如何在 MTA 下创建图形操作界面 (GUI) 及对其进行脚本编写。&lt;br /&gt;
&lt;br /&gt;
==创建一个简单的游戏指令==&lt;br /&gt;
打开先前我们所创建的 &amp;quot;script.lua&amp;quot; 文件。上文提到，我们需要创建一个可在玩家附近刷出交通工具的游戏指令。首先我们需要定义一个用于处理玩家输入指令的函数，以及一个用于创建玩家可从客户端控制台输入游戏指令的指令处理器。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- 定义一个由指令处理器所调用的函数，并提供三个参数：thePlayer, command, vehicleModel&lt;br /&gt;
function createVehicleForPlayer(thePlayer, command, vehicleModel)&lt;br /&gt;
   -- 创建交通工具的代码&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- 创建一个指令处理器&lt;br /&gt;
addCommandHandler(&amp;quot;createvehicle&amp;quot;, createVehicleForPlayer)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;quot;提示：您可以点击示例代码中的函数名以查看该 API 函数的文档。&amp;quot;&lt;br /&gt;
&lt;br /&gt;
====有关指令处理器====&lt;br /&gt;
[[addCommandHandler]] 的第一个参数表明了玩家可以从客户端输入的游戏指令（同时也表明新建了这个游戏指令）；第二个参数是该游戏指令发送到服务端后所调用的函数，本例中该函数名为 &amp;quot;createVehicleForPlayer&amp;quot;。&lt;br /&gt;
&lt;br /&gt;
如果您有一定的编程经验，您应当知道调用函数的方法是这样的：&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
functionName(参数1, 参数2, 参数3, ..)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
functionName(thePlayer, commandName, argument3, ..)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
看看上例代码，我们发现参数1 为 thePlayer，参数2 为 commandName。thePlayer 表明哪位玩家发送了该游戏指令；而 commandName 表明游戏指令字符串。因此如果玩家发送了指令 &amp;quot;/greet&amp;quot;，commandName 便是 &amp;quot;greet&amp;quot;（不包含指令前面的斜杠 &amp;quot;/&amp;quot;)。参数3 是玩家额外的输入，后文中将会提到。&lt;br /&gt;
请记住前两个参数（thePlayer 及 commandName）是必不可少的参数，但是您可以随意命名这些参数以符合您的代码风格。&lt;br /&gt;
&lt;br /&gt;
我们已经调用了 [[addCommandHandler]] 函数；而由于 ''createVehicleForPlayer'' 也同样是一个函数，它也可以以相同的方式被内部所调用。&lt;br /&gt;
&lt;br /&gt;
例如：某玩家输入了游戏指令 &amp;quot;createvehicle 468&amp;quot; 以刷出 Sanchez，指令处理器随后会调用 createVehicleForPlayer 函数，然后我们看看这个 createVehicleForPlayer 函数是如何被调用的：&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
createVehicleForPlayer(thePlayer,&amp;quot;createvehicle&amp;quot;,&amp;quot;468&amp;quot;) -- thePlayer 表示的是输入了 createvehicle 指令的玩家&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
可以看到，它提供了几个参数：输入了指令的玩家，指令字符串本身（不包括前面的斜杠 '/'）以及核心指令 createvehicle 以后的任意文本，本例中 &amp;quot;468&amp;quot; 为核心指令以后的文本，其为 Sanchez 交通工具的 ID。所有的指令处理器中，前两个参数都是一样且必须有的（参见 [[addEventHandler]] 文档）。因此，任意的指令处理器中，您都必须得要先定义这两个基本参数，再附加任意数量的附加参数。（附加参数有多少个，核心指令后的附加指令信息就有多少；本例中，createvehicle 有一个表示交通工具 ID 的附加指令信息，因此附加参数数量同为一个）&lt;br /&gt;
&lt;br /&gt;
&amp;quot;注意：必须要在定义了指令处理函数'''之后'''再创建指令处理器，否则指令处理器届时将无法找到其处理函数。&amp;quot;&lt;br /&gt;
&lt;br /&gt;
====编写函数====&lt;br /&gt;
为了完成我们所创建的函数，我们得先想想我们要在该函数内完成的一些事：&lt;br /&gt;
* 获取玩家位置以便知道在何处刷出交通工具（我们希望在玩家身边刷出）&lt;br /&gt;
* 计算最理想的交通工具刷出点（我们不希望玩家会卡在车辆内）&lt;br /&gt;
* 刷出交通工具&lt;br /&gt;
* 检查交通工具是否成功刷出，如果没有则输出错误信息&lt;br /&gt;
&lt;br /&gt;
为了实现以上的需求，我们需要使用一些 API 函数。可以在[[Script Functions|服务端函数列表]]中找到我们所需的函数。首先我们需要一个用于取得玩家位置的函数。由于玩家属于 “元素” (Element)，我们找到 '''针对元素的 API 函数'''，并找到 [[getElementPosition]] 函数。单击该函数名便可阅读该函数的文档。文档内详细说明了函数的调用语法，返回值以及其示例代码。函数调用语法告诉我们在调用函数时应该提供什么参数。&lt;br /&gt;
&lt;br /&gt;
[[getElementPosition]] 的调用语法为：&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
float, float, float getElementPosition ( element theElement )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
函数名前的三个 &amp;quot;float&amp;quot; 表示的是返回值类型。本例中表示该函数返回三个浮点数（x、y 及 z）。括号内，可以看到调用该函数时所需提供的参数。本例中只需要传递一个表示欲获取其位置的元素，在这里即玩家。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function createVehicleForPlayer(thePlayer, command, vehicleModel)&lt;br /&gt;
	-- 获取玩家位置并将信息放入 x、y 及 z 变量中&lt;br /&gt;
	-- （local 的意思是定义一个局部变量，在其作用域以外的地方无法访问，本例中即函数作用域）&lt;br /&gt;
	local x,y,z = getElementPosition(thePlayer)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
下一步，我们希望确保交通工具不会直接刷在玩家所在的位置（这样会玩家卡在车辆内），因此我们把 &amp;quot;x&amp;quot; 变量的值增加了少许，让车辆在距离玩家更东的位置被刷出。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function createVehicleForPlayer(thePlayer, command, vehicleModel)&lt;br /&gt;
	local x,y,z = getElementPosition(thePlayer) -- 获取玩家位置&lt;br /&gt;
	x = x + 5 -- x 坐标增加 5 个单位&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
现在我们需要另一个用于刷出车辆的函数。再次打开 [[Scripting Functions|服务端函数列表]]，这次因为要找的是和交通工具有关的，所以定位到 '''交通工具函数'''，并阅读 [[createVehicle]] 的文档。根据该函数的调用语法，该函数只有一个返回类型（最常见的情况），表示的是创建了的交通工具元素。同样，我们可以看到在调用语法中有一些用方括号 [ ] 括起来的参数，这类参数说明它们是可选的，调用时可以不提供。&lt;br /&gt;
&lt;br /&gt;
在我们所定义的函数中，一切需要传递给 [[createVehicle]] 函数的参数都准备好了：计算好的 &amp;quot;x, y, z&amp;quot; 变量以及通过玩家输入的游戏指令 &amp;quot;createvehicle 468&amp;quot; 所得到的交通工具 ID，可以通过 ''vehicleModel'' 参数得到该 ID。&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function createVehicleForPlayer(thePlayer, command, vehicleModel)&lt;br /&gt;
	local x,y,z = getElementPosition(thePlayer) -- 获取玩家位置&lt;br /&gt;
	x = x + 5 -- x 坐标增加 5 个单位&lt;br /&gt;
        -- 创建交通工具并将返回值存入到 ''createdVehicle'' 变量内&lt;br /&gt;
	local createdVehicle = createVehicle(tonumber(vehicleModel),x,y,z)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
当然这些代码还有改善的余地，但至少我们还可以添加一个用于检测交通工具是否成功创建的代码。阅读 [[createVehicle]] 函数文档的 '''返回值''' 部分，该部分说明：若该函数返回 ''false''，则说明交通工具创建失败。因此，我们需要检查 ''createVehicle'' 变量的值以检测交通工具是否创建成功。&lt;br /&gt;
&lt;br /&gt;
以下是完整的脚本代码：&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function createVehicleForPlayer(thePlayer, command, vehicleModel)&lt;br /&gt;
	local x,y,z = getElementPosition(thePlayer) -- 获取玩家位置&lt;br /&gt;
	x = x + 5 -- x 坐标增加 5 个单位&lt;br /&gt;
	local createdVehicle = createVehicle(tonumber(vehicleModel),x,y,z)&lt;br /&gt;
	-- 检查返回值是否为 ''false''&lt;br /&gt;
	if (createdVehicle == false) then&lt;br /&gt;
                -- 如果是，将错误信息发送给该特定玩家&lt;br /&gt;
		outputChatBox(&amp;quot;Failed to create vehicle.&amp;quot;,thePlayer)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
addCommandHandler(&amp;quot;createvehicle&amp;quot;, createVehicleForPlayer)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
上例代码中，还介绍了另一个用于发送客户端信息的 [[outputChatBox]] 函数。现在，您应该有能力可以自行阅读 API 函数文档了。若需要进一步学习高级脚本编写，请阅读[[Map manager|地图管理器]]教程。&lt;br /&gt;
&lt;br /&gt;
==必知==&lt;br /&gt;
目前为止，您已经接触到及知悉了有关资源、指令处理器及如何在文档中寻找 API 函数，但要学的还有很多。本节将粗略告诉您有关这些学习内容，您可以自行阅读相关教程。&lt;br /&gt;
===客户端及服务端脚本===&lt;br /&gt;
您可能曾经了解或是看到过所谓 “客户端及服务端脚本” (Server/Client Script) 的概念或名称了。事实上，您不仅能够编写典型的运行在服务器之上的，用于提供游戏指令处理等需求的脚本；还能够编写运行在 MTA 玩家客户端之上的脚本。之可以编写客户端脚本，原因是一些 MTA 所提供的特殊功能只能在客户端上执行（例如图形操作界面），或是在客户端上执行一些代码比在服务端上执行效率要高些。&lt;br /&gt;
&lt;br /&gt;
大部分您所制作的资源（游戏模式、地图）可能都是服务端脚本，例如本教程前面所介绍的脚本。但如果您所编写的代码无法在服务端上执行，则您可能需要令其可在客户端上运行（即编写客户端脚本）。若要开始尝试编写一个客户端脚本，您需要创建一个脚本文件（例如，把它命名为 ''client.lua''），并修改 meta.xml 如下：&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;client.lua&amp;quot; type=&amp;quot;client&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
''type'' 属性默认值为 'server'（即默认服务端脚本），因此只有在需要指定其是客户端脚本时才显式指定 ''type'' 属性。一个脚本文件一旦其 ''type'' 属性的值设置为 ''client''，则玩家连接到服务器时，该脚本文件会自动下载到玩家客户端。有关更多信息，请阅读[[客户端脚本]]教程。&lt;br /&gt;
&lt;br /&gt;
===更复杂的资源===&lt;br /&gt;
上一节简单介绍了如何添加一个客户端脚本，但其实您什么都可以做到。正如本教程开头提到过，资源可以是任何类型的游戏模块。这些资源本身做什么或提供什么功能，便决定了它们的类型是什么。首先我们来分析一下下面的这些资源：&lt;br /&gt;
&lt;br /&gt;
====第一个示例资源 —— 辅助类资源====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
/admin_commands&lt;br /&gt;
	/meta.xml&lt;br /&gt;
	/commands.lua&lt;br /&gt;
	/client.lua&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;meta&amp;gt;&lt;br /&gt;
	&amp;lt;info author=&amp;quot;Someguy&amp;quot; description=&amp;quot;admin commands&amp;quot; /&amp;gt;&lt;br /&gt;
	&amp;lt;script src=&amp;quot;commands.lua&amp;quot; /&amp;gt;&lt;br /&gt;
	&amp;lt;script src=&amp;quot;client.lua&amp;quot; type=&amp;quot;client&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/meta&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* ''commands.lua'' 提供一些管理员指令，例如封禁玩家、玩家禁言等&lt;br /&gt;
* ''client.lua'' 提供图形操作界面以简化玩家操作&lt;br /&gt;
&lt;br /&gt;
该示例资源可以随时被使用（也可以设置为随服务器而自动启动），原因是该资源对于整个游戏流程而言有一定的帮助（管理服务器）且不会影响到正常的游戏过程。&lt;br /&gt;
&lt;br /&gt;
====第二个示例资源 —— 游戏模式====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
/counterstrike&lt;br /&gt;
	/meta.xml&lt;br /&gt;
	/counterstrike.lua&lt;br /&gt;
	/buymenu.lua&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;meta&amp;gt;&lt;br /&gt;
	&amp;lt;info author=&amp;quot;Someguy&amp;quot; description=&amp;quot;Counterstrike remake&amp;quot; type=&amp;quot;gamemode&amp;quot; /&amp;gt;&lt;br /&gt;
	&amp;lt;script src=&amp;quot;counterstrike.lua&amp;quot; /&amp;gt;&lt;br /&gt;
	&amp;lt;script src=&amp;quot;buymenu.lua&amp;quot; type=&amp;quot;client&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/meta&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* ''counterstrike.lua'' 包含有类似于以下功能的实现：&lt;br /&gt;
** 允许玩家选择其团队并刷出玩家&lt;br /&gt;
** 把武器、目标及游戏指示提供给玩家&lt;br /&gt;
** 规定游戏规则。例如：一盘游戏的时间、玩家死后会发生什么等&lt;br /&gt;
** 等等等等...&lt;br /&gt;
* ''buymenu.lua'' 是一个客户端脚本，其用于创建武器购买菜单（使用到了图形操作界面，因此必须写为客户端脚本）&lt;br /&gt;
&lt;br /&gt;
该示例资源便是一个游戏模式，因为它不但影响到了游戏过程，还实际规定了''游戏规则''。该资源的 ''type'' 属性表明了该资源是运行在[[地图管理器]]上的，但 QA 团队编写的另一个资源可以用于帮助您管理游戏模式以及地图加载。强烈建议您在该辅助资源的基础上编写您的游戏模式。&lt;br /&gt;
&lt;br /&gt;
这可能也说明了游戏模式在没有地图的情况下无法运行。游戏模式本身就应该包含有至少一个地图。下一个示例将会介绍地图。&lt;br /&gt;
&lt;br /&gt;
====第三个示例 —— 地图====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
/cs-airport&lt;br /&gt;
	/meta.xml&lt;br /&gt;
	/airport.map&lt;br /&gt;
	/airport.lua&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;meta&amp;gt;&lt;br /&gt;
	&amp;lt;info author=&amp;quot;Someguy&amp;quot; description=&amp;quot;Counterstrike airport map&amp;quot; type=&amp;quot;map&amp;quot; gamemodes=&amp;quot;counterstrike&amp;quot; /&amp;gt;&lt;br /&gt;
	&amp;lt;map src=&amp;quot;airport.map&amp;quot; /&amp;gt;&lt;br /&gt;
	&amp;lt;script src=&amp;quot;airport.lua&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/meta&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* XML 格式的 ''airport.map'' 文件向游戏模式提供了有关该地图的信息，包括：&lt;br /&gt;
** 玩家的出生点、出生时配备的武器，以及玩家所属的团队&lt;br /&gt;
** 玩家的目标&lt;br /&gt;
** 天气、世界时间、时间限制&lt;br /&gt;
** 提供交通工具&lt;br /&gt;
* ''airport.lua'' 可能包含了一些辅助地图自身的功能：&lt;br /&gt;
** 当特定的事情发生时打开特定的门或使某物爆炸&lt;br /&gt;
** 创建或移动一些自定义对象，或是控制通过 .map 文件所创建的对象&lt;br /&gt;
** 等等等等...&lt;br /&gt;
&lt;br /&gt;
正如您所见的，''type'' 属性改为了 'map'，告诉[[地图管理器]]该资源为地图；而 ''gamemodes'' 属性指明了该地图只有在什么游戏模式下才能够运行，在本例中所指定的游戏模式即上面所创建的。&lt;br /&gt;
地图资源内也可以包含脚本。当然，地图并不一定需要包含脚本，但是这可以在游戏模式所设计的游戏规则以外再允许地图创造者设计这些地图自己的游戏规则。&lt;br /&gt;
&lt;br /&gt;
''airport.map'' 文件内容可能如下：&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;map mode=&amp;quot;deathmatch&amp;quot; version=&amp;quot;1.0&amp;quot;&amp;gt;&lt;br /&gt;
	&amp;lt;terrorists&amp;gt;&lt;br /&gt;
		&amp;lt;spawnpoint posX=&amp;quot;2332.23&amp;quot; posY=&amp;quot;-12232.33&amp;quot; posZ=&amp;quot;4.42223&amp;quot; skins=&amp;quot;23-40&amp;quot; /&amp;gt;&lt;br /&gt;
	&amp;lt;/terrorists&amp;gt;&lt;br /&gt;
	&amp;lt;counterterrorists&amp;gt;&lt;br /&gt;
		&amp;lt;spawnpoint posX=&amp;quot;2334.23443&amp;quot; posY=&amp;quot;-12300.233&amp;quot; posZ=&amp;quot;10.2344&amp;quot; skins=&amp;quot;40-50&amp;quot; /&amp;gt;&lt;br /&gt;
	&amp;lt;/counterterrorists&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	&amp;lt;bomb posX=&amp;quot;23342.23&amp;quot; posY=&amp;quot;&amp;quot; posZ=&amp;quot;&amp;quot; /&amp;gt;&lt;br /&gt;
	&lt;br /&gt;
	&amp;lt;vehicle posX=&amp;quot;&amp;quot; posY=&amp;quot;&amp;quot; posZ=&amp;quot;&amp;quot; model=&amp;quot;602&amp;quot; /&amp;gt;	&lt;br /&gt;
	&amp;lt;vehicle posX=&amp;quot;&amp;quot; posY=&amp;quot;&amp;quot; posZ=&amp;quot;&amp;quot; model=&amp;quot;603&amp;quot; /&amp;gt;	&lt;br /&gt;
&amp;lt;/map&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
当游戏模式及地图同时被加载，地图管理器会自动启动地图资源，随后游戏模式资源会开始读取地图信息；当切换到其他地图时，当前的地图资源会被停止，然后轮到下一张地图资源被加载。请阅读[[编写游戏模式]]教程以学习更深入的地图资源管理模式。&lt;br /&gt;
&lt;br /&gt;
===事件===&lt;br /&gt;
MTA 会以触发事件的形式告知脚本发生了什么。例如，当任意玩家死亡时，[[onPlayerWasted]] 事件会被触发。为了能够及时地在任意玩家死亡时能够执行一些操作，您需要自定义一个用于处理该类型事件的事件处理器。&lt;br /&gt;
&lt;br /&gt;
下列示例代码会在任意玩家死后，向所有玩家发送该玩家死亡的信息：&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function playerDied(totalAmmo, killer, killerWeapon, bodypart)&lt;br /&gt;
	outputChatBox(getPlayerName(source)..&amp;quot; died!&amp;quot;)&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerWasted&amp;quot;,getRootElement(),playerDied)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
事件文档列举了不同事件触发后将会传递到事件处理函数的不同参数。请留意 ''source'' 变量，虽然其并非是参数，但每个处理函数都隐式包含有该变量。在不同的事件中，该变量表示的概念不同；例如玩家事件的处理函数中，该变量表示触发了事件的玩家元素。&lt;br /&gt;
&lt;br /&gt;
==然后呢？==&lt;br /&gt;
现在，您应该已经大概了解了 MTA 脚本编写的一些基础知识以及如何阅读文档了。[[首页]]提供有更多的信息、教程以及参考链接。您可以随时阅读这些文章以更深入地学习 MTA 脚本编写。&lt;br /&gt;
{{note|强烈建议您开始阅读[[调试脚本]]教程。编写脚本时，调试是十分有必要的；我们也建议您参考[[预定义变量列表]]中的信息以帮助您更高效率地编写 MTA 脚本。}}&lt;br /&gt;
'''相关参考:'''&lt;br /&gt;
&lt;br /&gt;
* [[Advanced Topics]]&lt;br /&gt;
&lt;br /&gt;
[[en:Scripting Introduction]]&lt;/div&gt;</summary>
		<author><name>Skyvzla</name></author>
	</entry>
	<entry>
		<id>https://wiki.multitheftauto.com/index.php?title=ZH-CN/%E8%84%9A%E6%9C%AC%E7%BC%96%E5%86%99%E4%BB%8B%E7%BB%8D_-_%E5%B8%A6%E6%9C%89%E5%9B%BE%E5%BD%A2%E7%95%8C%E9%9D%A2&amp;diff=50428</id>
		<title>ZH-CN/脚本编写介绍 - 带有图形界面</title>
		<link rel="alternate" type="text/html" href="https://wiki.multitheftauto.com/index.php?title=ZH-CN/%E8%84%9A%E6%9C%AC%E7%BC%96%E5%86%99%E4%BB%8B%E7%BB%8D_-_%E5%B8%A6%E6%9C%89%E5%9B%BE%E5%BD%A2%E7%95%8C%E9%9D%A2&amp;diff=50428"/>
		<updated>2017-02-12T05:11:09Z</updated>

		<summary type="html">&lt;p&gt;Skyvzla: /* Creating the serverside event */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- place holder --&amp;gt;&lt;br /&gt;
在MTA:SA中有一个重要的特点，那就是可以制作自定义的GUI(Graphic User Interface)。GUI由窗口，按钮，编辑框，复选框等组成，拥有在图形环境中大部分的基础控件。它们可以在玩家正在游戏的时候显示，常常被用来代替传统命令的输入和输出。&lt;br /&gt;
&lt;br /&gt;
[[Image:AdminGUI.png|thumb|管理员后台GUI]]&lt;br /&gt;
&lt;br /&gt;
==一个做登陆窗口的教程==&lt;br /&gt;
在这个教程中我们会使用两个编辑框和一个按钮制作一个登录窗口。 窗口会在玩家加入游戏的时候出现，一旦按钮被点击，玩家将会被出生。 这个教程会继续我们在 [[Scripting Introduction|脚本介绍]] 中制作的游戏模式''（如果你已经使用了 [[Scripting Introduction|脚本介绍]]，你需要在你的代码中移除或者注释掉在 &amp;quot;joinHandler&amp;quot; 行的 [[spawnPlayer]] 函数， 我们也将会在这个教程中使用一个gui替换掉它）''。 我们也会接触一下客户端脚本的编辑.。&lt;br /&gt;
&lt;br /&gt;
===画一个窗口===&lt;br /&gt;
所有的GUI只能在客户端制作。 这是一个很好的练习机会来把所有客户端脚本放在分开的目录。&lt;br /&gt;
&lt;br /&gt;
浏览到目录 /你的MTA服务端目录/mods/deathmatch/resources/myserver/ , 然后创建一个名为 &amp;quot;client&amp;quot; 的目录。 在 /client/ 目录下, 创建一个文件，文件名为 &amp;quot;gui.lua&amp;quot;.。&lt;br /&gt;
&lt;br /&gt;
在这个文件里，我们将会写一个画窗口的函数。使用[[guiCreateWindow]]来创建一个窗口：&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function createLoginWindow()&lt;br /&gt;
	-- 定义窗口的X和Y坐标&lt;br /&gt;
	local X = 0.375&lt;br /&gt;
	local Y = 0.375&lt;br /&gt;
	-- 定义窗口的宽度和高度&lt;br /&gt;
	local Width = 0.25&lt;br /&gt;
	local Height = 0.25&lt;br /&gt;
        -- 创建一个窗口，并且把它放入变量 'wdwLogin' 里&lt;br /&gt;
        -- 点击这个函数的名字可以阅读这个函数的相关信息&lt;br /&gt;
	wdwLogin = guiCreateWindow(X, Y, Width, Height, &amp;quot;请登录&amp;quot;, true)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===相对和绝对坐标===&lt;br /&gt;
注意：传给 guiCreateWindow 的最后一个参数在上面的例子中是 ''true''。这个表明了窗口的坐标和尺寸都是 '''相对的'''，也就是说他们是一个相对于屏幕大小的 ''百分比''。我来解释一下：如果屏幕最左侧是0，那么最右侧就是1，那么X坐标为0.5将代表着屏幕中央。同理，屏幕顶部和底部也是一样的，最顶部为0，最底部为1，Y坐标为0.2则代表着是屏幕高度的20%。宽度和高度也是一样的道理（宽度为0.5则意味着窗口是屏幕的一半宽）。&lt;br /&gt;
&lt;br /&gt;
另外的，也可以用 '''绝对的''' （将传入 guiCreateWindow 的最后一个参数改为 ''false'' 即可）。绝对坐标被计算为父级的左上角开始到右下角的像素总数（如果没有GUI元素指定父级，那么父级是屏幕本身）。 如果我们假设屏幕的分辨率为1920*1200，那么从屏幕左边开始为0像素，到屏幕右边为1920像素，X坐标为960代表屏幕的中点。同理，屏幕顶部为0，到屏幕底部则为1200，Y坐标为20代表着距离屏幕顶部的20个像素。宽度和高度也是相同的道理（宽度为50则意味着窗口为50个像素宽）。 ''你可以使用 [[guiGetScreenSize]] 和一点数学来计算某些绝对坐标。''&lt;br /&gt;
&lt;br /&gt;
使用相对坐标和绝对坐标的不同点非常简单：使用绝对坐标创建gui经常精确地保持着相同的像素大小和坐标，然而使用相对坐标创建gui经常是与它父级gui大小的比值。&lt;br /&gt;
&lt;br /&gt;
当你用手敲代码的时候，绝对坐标一般来说更容易维护。然而你的选择是根据你的目的而变化的。&lt;br /&gt;
&lt;br /&gt;
为了本介绍的目的，我们将使用相对坐标。&lt;br /&gt;
&lt;br /&gt;
===添加组件===&lt;br /&gt;
接下来， 我们要添加文本标签（里面写上 “帐号：” 和 “密码：”）、编辑框（为了输入你的数据）和一个登录按钮。&lt;br /&gt;
&lt;br /&gt;
我们可以使用 [[guiCreateButtonTo]] 创建按钮，创建 [[guiCreateEdit]] 创建编辑框。&lt;br /&gt;
&lt;br /&gt;
'''注意：我们现在正在给我们已经存在的 ‘createLoginWindow’函数写更多的代码。这不是一个新的函数，这个函数是用来替换你脚本中已经存在的那个。''' &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function createLoginWindow()&lt;br /&gt;
	local X = 0.375&lt;br /&gt;
	local Y = 0.375&lt;br /&gt;
	local Width = 0.25&lt;br /&gt;
	local Height = 0.25&lt;br /&gt;
	wdwLogin = guiCreateWindow(X, Y, Width, Height, &amp;quot;请登录&amp;quot;, true)&lt;br /&gt;
	&lt;br /&gt;
	-- 给第一个文本标签定义新的X和Y坐标&lt;br /&gt;
	X = 0.0825&lt;br /&gt;
	Y = 0.2&lt;br /&gt;
	-- 给第一个文本标签定义新的宽度和高度&lt;br /&gt;
	Width = 0.25&lt;br /&gt;
	Height = 0.25&lt;br /&gt;
	-- 创建第一个文本标签，注意最后一个传入的参数是 ‘wdwLogin’，这个参数是你刚刚创建的窗口（window）&lt;br /&gt;
	-- 我们在它的父级gui上方创建了这个文本标签（所以所有的坐标和大小的值都是相对于这个窗口的）&lt;br /&gt;
	guiCreateLabel(X, Y, Width, Height, &amp;quot;帐号&amp;quot;, true, wdwLogin)&lt;br /&gt;
	-- 改变Y的值，所以第二个文本标签是在第一个下方的&lt;br /&gt;
	Y = 0.5&lt;br /&gt;
	guiCreateLabel(X, Y, Width, Height, &amp;quot;密码&amp;quot;, true, wdwLogin)&lt;br /&gt;
	&lt;br /&gt;
&lt;br /&gt;
	X = 0.415&lt;br /&gt;
	Y = 0.2&lt;br /&gt;
	Width = 0.5&lt;br /&gt;
	Height = 0.15&lt;br /&gt;
	edtUser = guiCreateEdit(X, Y, Width, Height, &amp;quot;&amp;quot;, true, wdwLogin)&lt;br /&gt;
	Y = 0.5&lt;br /&gt;
	edtPass = guiCreateEdit(X, Y, Width, Height, &amp;quot;&amp;quot;, true, wdwLogin)&lt;br /&gt;
	-- 设置编辑框的最大文字长度为50&lt;br /&gt;
	guiEditSetMaxLength(edtUser, 50)&lt;br /&gt;
	guiEditSetMaxLength(edtPass, 50)&lt;br /&gt;
	&lt;br /&gt;
	X = 0.415&lt;br /&gt;
	Y = 0.7&lt;br /&gt;
	Width = 0.25&lt;br /&gt;
	Height = 0.2&lt;br /&gt;
	btnLogin = guiCreateButton(X, Y, Width, Height, &amp;quot;Log In&amp;quot;, true, wdwLogin)&lt;br /&gt;
	&lt;br /&gt;
	-- 让这个窗口不显示&lt;br /&gt;
	guiSetVisible(wdwLogin, false)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
注意：每个GUI组件都是创建为window的子级，这是当创建组件时通过指定父级元素（在这种情况下是wdwLogin）来完成的&lt;br /&gt;
&lt;br /&gt;
这是很有用的，因为这不仅意味着所有组件都是附着在window并且移动的时候会带着这些组件一起移动，而且任何对父级window的改变都会被应用到这些子级元素。例如，我们可以隐藏所有的GUI，我们只要隐藏window就行了：&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
guiSetVisible(wdwLogin, false) --隐藏我们制作的所有的GUI，这样我们才能在适当的时候显示GUI&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===使用我们编写完成的功能===&lt;br /&gt;
'''createLoginWindow''' 功能编写完成，但是它'''不会'''执行,必须要我们'''调用'''它才可以执行。建议在'''客户端启动资源时'''创建所有的GUI，当前不需要显示时可以进行隐藏（如果需要立即显示就不用隐藏），或在我们需要用到它的时候显示给玩家。因此，我们需要为GUI窗口创建一个'''事件触发器'''，事件触发器的事件为 &amp;quot;[[onClientResourceStart]]&amp;quot;：&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- 将事件处理程序添加到资源的根元素&lt;br /&gt;
-- 只有在这个资源本启动时才会触发&lt;br /&gt;
addEventHandler(&amp;quot;onClientResourceStart&amp;quot;, getResourceRootElement(), &lt;br /&gt;
	function ()&lt;br /&gt;
		createLoginWindow()&lt;br /&gt;
	end&lt;br /&gt;
)	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
因为脚本是登陆界面，所以我们要在玩家进入游戏后显示出来。&lt;br /&gt;
也可以使用 &amp;quot;[[onClientResourceStart]]&amp;quot; 事件来完成，所以我们将上面的代码进行修改，来达到我们的目的：&lt;br /&gt;
&lt;br /&gt;
'''注意,我们正在为现有的 'onClientResourceStart' 事件处理程序编写更多的代码。不是新创建的一个事件处理程序。''' &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
addEventHandler(&amp;quot;onClientResourceStart&amp;quot;, getResourceRootElement(), &lt;br /&gt;
	function ()&lt;br /&gt;
		-- 创建已经编写好的窗口以及窗口组件&lt;br /&gt;
		createLoginWindow()&lt;br /&gt;
&lt;br /&gt;
		-- 输出一个欢迎信息给玩家&lt;br /&gt;
                outputChatBox(&amp;quot;Welcome to My MTA:SA Server, please log in.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
		-- 用if函数判断窗口是否创建成功，如果成功将窗口显示给玩家&lt;br /&gt;
	        if (wdwLogin ~= nil) then&lt;br /&gt;
			guiSetVisible(wdwLogin, true)&lt;br /&gt;
		else&lt;br /&gt;
			-- 如果窗口创建失败时，告诉玩家窗口创建失败&lt;br /&gt;
			outputChatBox(&amp;quot;An unexpected error has occurred and the log in GUI has not been created.&amp;quot;)&lt;br /&gt;
	        end &lt;br /&gt;
&lt;br /&gt;
		-- 启用玩家的鼠标显示，以便点击窗口中的组件&lt;br /&gt;
	        showCursor(true)&lt;br /&gt;
		-- 设置玩家绑定的所有按键都不生效，包括'T '键的本地发言，以保证输入的信息都输入到编辑框内&lt;br /&gt;
	        guiSetInputEnabled(true)&lt;br /&gt;
	end&lt;br /&gt;
)	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
注意,在显示窗口之前,我们使用了一个if函数来判断窗口是否创建成功。创建成功的情况下将显示出来。创建不成功时就意味着 wdwLogin 不是一个元素。服务器也不会出现错误提示。只会提示玩家窗口创建失败。&lt;br /&gt;
下一步我们将给按钮(button)创建点击后的功能&lt;br /&gt;
&lt;br /&gt;
==给按钮写一个功能==&lt;br /&gt;
既然我们已经创建了我们的GUI并且展示给了玩家看，我们需要让它运行。&lt;br /&gt;
&lt;br /&gt;
===检测点击===&lt;br /&gt;
当玩家点击GUI上任何一部分，&amp;quot;[[onClientGUIClick]]&amp;quot; 事件会因为你点击了GUI组建而被触发。这允许我们更简单地去检测我们想要使用的GUI元素上的点击。&lt;br /&gt;
举个例子，我们能够在 bthLogin 按钮上附加这个事件来抓取在这个GUI元素上的任何点击：n it:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- 附加 onClientGUIClick 到 btnLogin 上然后设置触发函数为 'clientSubmitLogin'&lt;br /&gt;
addEventHandler(&amp;quot;onClientGUIClick&amp;quot;, btnLogin, clientSubmitLogin, false)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
'''Note the final argument passed is &amp;quot;false&amp;quot;. This indicates that the event will only trigger directly on btnLogin, not if the event has propagated up or down the tree. Setting this to &amp;quot;true&amp;quot; while attaching to gui elements will mean that clicking on any element in the same branch will trigger this event.'''&lt;br /&gt;
'''注意：最后一个参数传入的是 &amp;quot;false&amp;quot; 。这表明了这个事件只会直接由 btnLogin 触发，不是当时间&lt;br /&gt;
&lt;br /&gt;
This line of code can now be added inside the createLoginWindow function. It is a common mistake to try and attach events to non-existant GUI elements, so make sure you always attach your events '''after''' the gui element (in this case, the button) has been created:&lt;br /&gt;
&lt;br /&gt;
'''注意：我们正在为目前存在的 'createLoginWindow' 函数编写更多的代码。'''&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function createLoginWindow()&lt;br /&gt;
	-- create all our GUI elements&lt;br /&gt;
	...&lt;br /&gt;
&lt;br /&gt;
	-- now add our onClientGUIClick event to the button we just created&lt;br /&gt;
	addEventHandler(&amp;quot;onClientGUIClick&amp;quot;, btnLogin, clientSubmitLogin, false)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Managing the click===&lt;br /&gt;
Now that we can detect when the player clicks on the button, we need to write code to manage what happens when they do.&lt;br /&gt;
In our [[onClientGUIClick]] event handle, we told it to call the function clientSubmitLogin whenever btnLogin is clicked.&lt;br /&gt;
Therefore, we can now use the function clientSubmitLogin to control what happens when the button is clicked:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- create the function and define the 'button' and 'state' parameters&lt;br /&gt;
-- (these are passed automatically by onClientGUIClick)&lt;br /&gt;
function clientSubmitLogin(button,state)&lt;br /&gt;
	-- if our login button was clicked with the left mouse button, and the state of the mouse button is up&lt;br /&gt;
	if button == &amp;quot;left&amp;quot; and state == &amp;quot;up&amp;quot; then&lt;br /&gt;
		-- move the input focus back onto the game (allowing players to move around, open the chatbox, etc)&lt;br /&gt;
		guiSetInputEnabled(false)&lt;br /&gt;
		-- hide the window and all the components&lt;br /&gt;
		guiSetVisible(wdwLogin, false)&lt;br /&gt;
		-- hide the mouse cursor&lt;br /&gt;
		showCursor(false)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Now, when the button is clicked, the window will be hidden and all controls will be returned to the player. Next, we will tell the server to allow the player to spawn.&lt;br /&gt;
&lt;br /&gt;
===触发服务器事件===&lt;br /&gt;
客户端触发服务器端事件使用 [[triggerServerEvent]] 函数来完成.这个函数可以从客户端上触发指定的服务器端事件.同样的,服务器端触发客户端事件使用 [[triggerClientEvent]] 函数完成.因为我们要触发'''服务器端'''事件,所以在这里我们使用 [[triggerServerEvent]] 函数来调用我们'''自定义'''的服务器事件,服务器事件命名为 &amp;quot;submitLogin&amp;quot;,这个事件能够控制玩家的重生.&lt;br /&gt;
&lt;br /&gt;
'''注意,我们是在为现有的功能 'clientSubmitLogin' 编写更多的代码,并不是一个全新的功能.''' &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function clientSubmitLogin(button,state)&lt;br /&gt;
	if button == &amp;quot;left&amp;quot; and state == &amp;quot;up&amp;quot; then&lt;br /&gt;
		-- 获取账户编辑框中的账户名&lt;br /&gt;
		local username = guiGetText(edtUser)&lt;br /&gt;
		-- 获取密码编辑框中的密码&lt;br /&gt;
		local password = guiGetText(edtPass)&lt;br /&gt;
&lt;br /&gt;
		-- 如果用户名和密码都不为空时向下执行&lt;br /&gt;
		if username and password then&lt;br /&gt;
			-- 调用服务器端事件 'submitLogin' 并向服务器端传递账户名和密码&lt;br /&gt;
			triggerServerEvent(&amp;quot;submitLogin&amp;quot;, getRootElement(), username, password)&lt;br /&gt;
&lt;br /&gt;
			-- 恢复玩家的按键绑定(比如T键本地聊天),隐藏玩家的GUI画面,取消玩家鼠标的显示&lt;br /&gt;
			guiSetInputEnabled(false)&lt;br /&gt;
			guiSetVisible(wdwLogin, false)&lt;br /&gt;
			showCursor(false)&lt;br /&gt;
		else&lt;br /&gt;
			-- 用户名或密码有一个为空时,向玩家返回消息,并且不触发服务端事件&lt;br /&gt;
			-- 同时不隐藏GUI画面&lt;br /&gt;
			outputChatBox(&amp;quot;Please enter a username and password.&amp;quot;)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===创建服务器事件===&lt;br /&gt;
At this point we now have all the code needed on the client side, so open up your serverside 'script.lua' file (from the [[Scripting Introduction|Introduction to Scripting]]) or another suitable serverside file to work with.&lt;br /&gt;
&lt;br /&gt;
On the server side, recall that we are spawning the player as soon as they login.&lt;br /&gt;
So, first of all, we will need to define the custom event that we used before on the client. This can be done using [[addEvent]] and [[addEventHandler]].&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- create our loginHandler function, with username and password parameters (passed from the client gui)&lt;br /&gt;
function loginHandler(username,password)&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- define our custom event, and allow it to be triggered from the client ('true')&lt;br /&gt;
addEvent(&amp;quot;submitLogin&amp;quot;,true)&lt;br /&gt;
-- add an event handler so that when submitLogin is triggered, the function loginHandler is called&lt;br /&gt;
addEventHandler(&amp;quot;submitLogin&amp;quot;,root,loginHandler)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Logging in===&lt;br /&gt;
Now we have a function that is called through the custom event 'submitLogin', we can start to work on logging in and spawning the player, using our 'loginHandler' function:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function loginHandler(username,password)&lt;br /&gt;
	-- check that the username and password are correct&lt;br /&gt;
	if username == &amp;quot;user&amp;quot; and password == &amp;quot;apple&amp;quot; then&lt;br /&gt;
		-- the player has successfully logged in, so spawn them&lt;br /&gt;
		if (client) then&lt;br /&gt;
			spawnPlayer(client, 1959.55, -1714.46, 10)&lt;br /&gt;
			fadeCamera(client, true)&lt;br /&gt;
                        setCameraTarget(client, client)&lt;br /&gt;
			outputChatBox(&amp;quot;Welcome to My Server.&amp;quot;, client)&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		-- if the username or password are not correct, output a message to the player&lt;br /&gt;
		outputChatBox(&amp;quot;Invalid username and password. Please re-connect and try again.&amp;quot;,client)&lt;br /&gt;
        end			&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
addEvent(&amp;quot;submitLogin&amp;quot;,true)&lt;br /&gt;
addEventHandler(&amp;quot;submitLogin&amp;quot;,root,loginHandler)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
'''For the purposes of this tutorial, a very basic username and password system is shown. For a more comprehensive alternative, you can use the Account System or a MySQL database.'''&lt;br /&gt;
&lt;br /&gt;
Also note the use of the variable &amp;quot;client&amp;quot;, it's an internal variable used by MTA to identify the player who triggered the event. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Finally, do not forget to include the new gui.lua file in the meta.xml of the main resource, and label it as a client script:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;client/gui.lua&amp;quot; type=&amp;quot;client&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
At this point, we now have a basic login window that checks the player's username and password when the login button is clicked. If they are correct, the player is automatically spawned.&lt;br /&gt;
&lt;br /&gt;
有关GUI的进一步帮助,请参考 [[:Category:GUI_Tutorials|GUI tutorials]].&lt;br /&gt;
&lt;br /&gt;
[[Category:GUI_Tutorials]]&lt;br /&gt;
[[it:Introduzione_allo_scripting_della_GUI]]&lt;br /&gt;
[[ru:Introduction to Scripting the GUI]]&lt;br /&gt;
[[es:Introducción a la Programación de GUI]]&lt;/div&gt;</summary>
		<author><name>Skyvzla</name></author>
	</entry>
	<entry>
		<id>https://wiki.multitheftauto.com/index.php?title=ZH-CN/%E8%84%9A%E6%9C%AC%E7%BC%96%E5%86%99%E4%BB%8B%E7%BB%8D_-_%E5%B8%A6%E6%9C%89%E5%9B%BE%E5%BD%A2%E7%95%8C%E9%9D%A2&amp;diff=50427</id>
		<title>ZH-CN/脚本编写介绍 - 带有图形界面</title>
		<link rel="alternate" type="text/html" href="https://wiki.multitheftauto.com/index.php?title=ZH-CN/%E8%84%9A%E6%9C%AC%E7%BC%96%E5%86%99%E4%BB%8B%E7%BB%8D_-_%E5%B8%A6%E6%9C%89%E5%9B%BE%E5%BD%A2%E7%95%8C%E9%9D%A2&amp;diff=50427"/>
		<updated>2017-02-12T05:10:03Z</updated>

		<summary type="html">&lt;p&gt;Skyvzla: /* 触发服务器事件 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- place holder --&amp;gt;&lt;br /&gt;
在MTA:SA中有一个重要的特点，那就是可以制作自定义的GUI(Graphic User Interface)。GUI由窗口，按钮，编辑框，复选框等组成，拥有在图形环境中大部分的基础控件。它们可以在玩家正在游戏的时候显示，常常被用来代替传统命令的输入和输出。&lt;br /&gt;
&lt;br /&gt;
[[Image:AdminGUI.png|thumb|管理员后台GUI]]&lt;br /&gt;
&lt;br /&gt;
==一个做登陆窗口的教程==&lt;br /&gt;
在这个教程中我们会使用两个编辑框和一个按钮制作一个登录窗口。 窗口会在玩家加入游戏的时候出现，一旦按钮被点击，玩家将会被出生。 这个教程会继续我们在 [[Scripting Introduction|脚本介绍]] 中制作的游戏模式''（如果你已经使用了 [[Scripting Introduction|脚本介绍]]，你需要在你的代码中移除或者注释掉在 &amp;quot;joinHandler&amp;quot; 行的 [[spawnPlayer]] 函数， 我们也将会在这个教程中使用一个gui替换掉它）''。 我们也会接触一下客户端脚本的编辑.。&lt;br /&gt;
&lt;br /&gt;
===画一个窗口===&lt;br /&gt;
所有的GUI只能在客户端制作。 这是一个很好的练习机会来把所有客户端脚本放在分开的目录。&lt;br /&gt;
&lt;br /&gt;
浏览到目录 /你的MTA服务端目录/mods/deathmatch/resources/myserver/ , 然后创建一个名为 &amp;quot;client&amp;quot; 的目录。 在 /client/ 目录下, 创建一个文件，文件名为 &amp;quot;gui.lua&amp;quot;.。&lt;br /&gt;
&lt;br /&gt;
在这个文件里，我们将会写一个画窗口的函数。使用[[guiCreateWindow]]来创建一个窗口：&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function createLoginWindow()&lt;br /&gt;
	-- 定义窗口的X和Y坐标&lt;br /&gt;
	local X = 0.375&lt;br /&gt;
	local Y = 0.375&lt;br /&gt;
	-- 定义窗口的宽度和高度&lt;br /&gt;
	local Width = 0.25&lt;br /&gt;
	local Height = 0.25&lt;br /&gt;
        -- 创建一个窗口，并且把它放入变量 'wdwLogin' 里&lt;br /&gt;
        -- 点击这个函数的名字可以阅读这个函数的相关信息&lt;br /&gt;
	wdwLogin = guiCreateWindow(X, Y, Width, Height, &amp;quot;请登录&amp;quot;, true)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===相对和绝对坐标===&lt;br /&gt;
注意：传给 guiCreateWindow 的最后一个参数在上面的例子中是 ''true''。这个表明了窗口的坐标和尺寸都是 '''相对的'''，也就是说他们是一个相对于屏幕大小的 ''百分比''。我来解释一下：如果屏幕最左侧是0，那么最右侧就是1，那么X坐标为0.5将代表着屏幕中央。同理，屏幕顶部和底部也是一样的，最顶部为0，最底部为1，Y坐标为0.2则代表着是屏幕高度的20%。宽度和高度也是一样的道理（宽度为0.5则意味着窗口是屏幕的一半宽）。&lt;br /&gt;
&lt;br /&gt;
另外的，也可以用 '''绝对的''' （将传入 guiCreateWindow 的最后一个参数改为 ''false'' 即可）。绝对坐标被计算为父级的左上角开始到右下角的像素总数（如果没有GUI元素指定父级，那么父级是屏幕本身）。 如果我们假设屏幕的分辨率为1920*1200，那么从屏幕左边开始为0像素，到屏幕右边为1920像素，X坐标为960代表屏幕的中点。同理，屏幕顶部为0，到屏幕底部则为1200，Y坐标为20代表着距离屏幕顶部的20个像素。宽度和高度也是相同的道理（宽度为50则意味着窗口为50个像素宽）。 ''你可以使用 [[guiGetScreenSize]] 和一点数学来计算某些绝对坐标。''&lt;br /&gt;
&lt;br /&gt;
使用相对坐标和绝对坐标的不同点非常简单：使用绝对坐标创建gui经常精确地保持着相同的像素大小和坐标，然而使用相对坐标创建gui经常是与它父级gui大小的比值。&lt;br /&gt;
&lt;br /&gt;
当你用手敲代码的时候，绝对坐标一般来说更容易维护。然而你的选择是根据你的目的而变化的。&lt;br /&gt;
&lt;br /&gt;
为了本介绍的目的，我们将使用相对坐标。&lt;br /&gt;
&lt;br /&gt;
===添加组件===&lt;br /&gt;
接下来， 我们要添加文本标签（里面写上 “帐号：” 和 “密码：”）、编辑框（为了输入你的数据）和一个登录按钮。&lt;br /&gt;
&lt;br /&gt;
我们可以使用 [[guiCreateButtonTo]] 创建按钮，创建 [[guiCreateEdit]] 创建编辑框。&lt;br /&gt;
&lt;br /&gt;
'''注意：我们现在正在给我们已经存在的 ‘createLoginWindow’函数写更多的代码。这不是一个新的函数，这个函数是用来替换你脚本中已经存在的那个。''' &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function createLoginWindow()&lt;br /&gt;
	local X = 0.375&lt;br /&gt;
	local Y = 0.375&lt;br /&gt;
	local Width = 0.25&lt;br /&gt;
	local Height = 0.25&lt;br /&gt;
	wdwLogin = guiCreateWindow(X, Y, Width, Height, &amp;quot;请登录&amp;quot;, true)&lt;br /&gt;
	&lt;br /&gt;
	-- 给第一个文本标签定义新的X和Y坐标&lt;br /&gt;
	X = 0.0825&lt;br /&gt;
	Y = 0.2&lt;br /&gt;
	-- 给第一个文本标签定义新的宽度和高度&lt;br /&gt;
	Width = 0.25&lt;br /&gt;
	Height = 0.25&lt;br /&gt;
	-- 创建第一个文本标签，注意最后一个传入的参数是 ‘wdwLogin’，这个参数是你刚刚创建的窗口（window）&lt;br /&gt;
	-- 我们在它的父级gui上方创建了这个文本标签（所以所有的坐标和大小的值都是相对于这个窗口的）&lt;br /&gt;
	guiCreateLabel(X, Y, Width, Height, &amp;quot;帐号&amp;quot;, true, wdwLogin)&lt;br /&gt;
	-- 改变Y的值，所以第二个文本标签是在第一个下方的&lt;br /&gt;
	Y = 0.5&lt;br /&gt;
	guiCreateLabel(X, Y, Width, Height, &amp;quot;密码&amp;quot;, true, wdwLogin)&lt;br /&gt;
	&lt;br /&gt;
&lt;br /&gt;
	X = 0.415&lt;br /&gt;
	Y = 0.2&lt;br /&gt;
	Width = 0.5&lt;br /&gt;
	Height = 0.15&lt;br /&gt;
	edtUser = guiCreateEdit(X, Y, Width, Height, &amp;quot;&amp;quot;, true, wdwLogin)&lt;br /&gt;
	Y = 0.5&lt;br /&gt;
	edtPass = guiCreateEdit(X, Y, Width, Height, &amp;quot;&amp;quot;, true, wdwLogin)&lt;br /&gt;
	-- 设置编辑框的最大文字长度为50&lt;br /&gt;
	guiEditSetMaxLength(edtUser, 50)&lt;br /&gt;
	guiEditSetMaxLength(edtPass, 50)&lt;br /&gt;
	&lt;br /&gt;
	X = 0.415&lt;br /&gt;
	Y = 0.7&lt;br /&gt;
	Width = 0.25&lt;br /&gt;
	Height = 0.2&lt;br /&gt;
	btnLogin = guiCreateButton(X, Y, Width, Height, &amp;quot;Log In&amp;quot;, true, wdwLogin)&lt;br /&gt;
	&lt;br /&gt;
	-- 让这个窗口不显示&lt;br /&gt;
	guiSetVisible(wdwLogin, false)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
注意：每个GUI组件都是创建为window的子级，这是当创建组件时通过指定父级元素（在这种情况下是wdwLogin）来完成的&lt;br /&gt;
&lt;br /&gt;
这是很有用的，因为这不仅意味着所有组件都是附着在window并且移动的时候会带着这些组件一起移动，而且任何对父级window的改变都会被应用到这些子级元素。例如，我们可以隐藏所有的GUI，我们只要隐藏window就行了：&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
guiSetVisible(wdwLogin, false) --隐藏我们制作的所有的GUI，这样我们才能在适当的时候显示GUI&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===使用我们编写完成的功能===&lt;br /&gt;
'''createLoginWindow''' 功能编写完成，但是它'''不会'''执行,必须要我们'''调用'''它才可以执行。建议在'''客户端启动资源时'''创建所有的GUI，当前不需要显示时可以进行隐藏（如果需要立即显示就不用隐藏），或在我们需要用到它的时候显示给玩家。因此，我们需要为GUI窗口创建一个'''事件触发器'''，事件触发器的事件为 &amp;quot;[[onClientResourceStart]]&amp;quot;：&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- 将事件处理程序添加到资源的根元素&lt;br /&gt;
-- 只有在这个资源本启动时才会触发&lt;br /&gt;
addEventHandler(&amp;quot;onClientResourceStart&amp;quot;, getResourceRootElement(), &lt;br /&gt;
	function ()&lt;br /&gt;
		createLoginWindow()&lt;br /&gt;
	end&lt;br /&gt;
)	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
因为脚本是登陆界面，所以我们要在玩家进入游戏后显示出来。&lt;br /&gt;
也可以使用 &amp;quot;[[onClientResourceStart]]&amp;quot; 事件来完成，所以我们将上面的代码进行修改，来达到我们的目的：&lt;br /&gt;
&lt;br /&gt;
'''注意,我们正在为现有的 'onClientResourceStart' 事件处理程序编写更多的代码。不是新创建的一个事件处理程序。''' &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
addEventHandler(&amp;quot;onClientResourceStart&amp;quot;, getResourceRootElement(), &lt;br /&gt;
	function ()&lt;br /&gt;
		-- 创建已经编写好的窗口以及窗口组件&lt;br /&gt;
		createLoginWindow()&lt;br /&gt;
&lt;br /&gt;
		-- 输出一个欢迎信息给玩家&lt;br /&gt;
                outputChatBox(&amp;quot;Welcome to My MTA:SA Server, please log in.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
		-- 用if函数判断窗口是否创建成功，如果成功将窗口显示给玩家&lt;br /&gt;
	        if (wdwLogin ~= nil) then&lt;br /&gt;
			guiSetVisible(wdwLogin, true)&lt;br /&gt;
		else&lt;br /&gt;
			-- 如果窗口创建失败时，告诉玩家窗口创建失败&lt;br /&gt;
			outputChatBox(&amp;quot;An unexpected error has occurred and the log in GUI has not been created.&amp;quot;)&lt;br /&gt;
	        end &lt;br /&gt;
&lt;br /&gt;
		-- 启用玩家的鼠标显示，以便点击窗口中的组件&lt;br /&gt;
	        showCursor(true)&lt;br /&gt;
		-- 设置玩家绑定的所有按键都不生效，包括'T '键的本地发言，以保证输入的信息都输入到编辑框内&lt;br /&gt;
	        guiSetInputEnabled(true)&lt;br /&gt;
	end&lt;br /&gt;
)	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
注意,在显示窗口之前,我们使用了一个if函数来判断窗口是否创建成功。创建成功的情况下将显示出来。创建不成功时就意味着 wdwLogin 不是一个元素。服务器也不会出现错误提示。只会提示玩家窗口创建失败。&lt;br /&gt;
下一步我们将给按钮(button)创建点击后的功能&lt;br /&gt;
&lt;br /&gt;
==给按钮写一个功能==&lt;br /&gt;
既然我们已经创建了我们的GUI并且展示给了玩家看，我们需要让它运行。&lt;br /&gt;
&lt;br /&gt;
===检测点击===&lt;br /&gt;
当玩家点击GUI上任何一部分，&amp;quot;[[onClientGUIClick]]&amp;quot; 事件会因为你点击了GUI组建而被触发。这允许我们更简单地去检测我们想要使用的GUI元素上的点击。&lt;br /&gt;
举个例子，我们能够在 bthLogin 按钮上附加这个事件来抓取在这个GUI元素上的任何点击：n it:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- 附加 onClientGUIClick 到 btnLogin 上然后设置触发函数为 'clientSubmitLogin'&lt;br /&gt;
addEventHandler(&amp;quot;onClientGUIClick&amp;quot;, btnLogin, clientSubmitLogin, false)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
'''Note the final argument passed is &amp;quot;false&amp;quot;. This indicates that the event will only trigger directly on btnLogin, not if the event has propagated up or down the tree. Setting this to &amp;quot;true&amp;quot; while attaching to gui elements will mean that clicking on any element in the same branch will trigger this event.'''&lt;br /&gt;
'''注意：最后一个参数传入的是 &amp;quot;false&amp;quot; 。这表明了这个事件只会直接由 btnLogin 触发，不是当时间&lt;br /&gt;
&lt;br /&gt;
This line of code can now be added inside the createLoginWindow function. It is a common mistake to try and attach events to non-existant GUI elements, so make sure you always attach your events '''after''' the gui element (in this case, the button) has been created:&lt;br /&gt;
&lt;br /&gt;
'''注意：我们正在为目前存在的 'createLoginWindow' 函数编写更多的代码。'''&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function createLoginWindow()&lt;br /&gt;
	-- create all our GUI elements&lt;br /&gt;
	...&lt;br /&gt;
&lt;br /&gt;
	-- now add our onClientGUIClick event to the button we just created&lt;br /&gt;
	addEventHandler(&amp;quot;onClientGUIClick&amp;quot;, btnLogin, clientSubmitLogin, false)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Managing the click===&lt;br /&gt;
Now that we can detect when the player clicks on the button, we need to write code to manage what happens when they do.&lt;br /&gt;
In our [[onClientGUIClick]] event handle, we told it to call the function clientSubmitLogin whenever btnLogin is clicked.&lt;br /&gt;
Therefore, we can now use the function clientSubmitLogin to control what happens when the button is clicked:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- create the function and define the 'button' and 'state' parameters&lt;br /&gt;
-- (these are passed automatically by onClientGUIClick)&lt;br /&gt;
function clientSubmitLogin(button,state)&lt;br /&gt;
	-- if our login button was clicked with the left mouse button, and the state of the mouse button is up&lt;br /&gt;
	if button == &amp;quot;left&amp;quot; and state == &amp;quot;up&amp;quot; then&lt;br /&gt;
		-- move the input focus back onto the game (allowing players to move around, open the chatbox, etc)&lt;br /&gt;
		guiSetInputEnabled(false)&lt;br /&gt;
		-- hide the window and all the components&lt;br /&gt;
		guiSetVisible(wdwLogin, false)&lt;br /&gt;
		-- hide the mouse cursor&lt;br /&gt;
		showCursor(false)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Now, when the button is clicked, the window will be hidden and all controls will be returned to the player. Next, we will tell the server to allow the player to spawn.&lt;br /&gt;
&lt;br /&gt;
===触发服务器事件===&lt;br /&gt;
客户端触发服务器端事件使用 [[triggerServerEvent]] 函数来完成.这个函数可以从客户端上触发指定的服务器端事件.同样的,服务器端触发客户端事件使用 [[triggerClientEvent]] 函数完成.因为我们要触发'''服务器端'''事件,所以在这里我们使用 [[triggerServerEvent]] 函数来调用我们'''自定义'''的服务器事件,服务器事件命名为 &amp;quot;submitLogin&amp;quot;,这个事件能够控制玩家的重生.&lt;br /&gt;
&lt;br /&gt;
'''注意,我们是在为现有的功能 'clientSubmitLogin' 编写更多的代码,并不是一个全新的功能.''' &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function clientSubmitLogin(button,state)&lt;br /&gt;
	if button == &amp;quot;left&amp;quot; and state == &amp;quot;up&amp;quot; then&lt;br /&gt;
		-- 获取账户编辑框中的账户名&lt;br /&gt;
		local username = guiGetText(edtUser)&lt;br /&gt;
		-- 获取密码编辑框中的密码&lt;br /&gt;
		local password = guiGetText(edtPass)&lt;br /&gt;
&lt;br /&gt;
		-- 如果用户名和密码都不为空时向下执行&lt;br /&gt;
		if username and password then&lt;br /&gt;
			-- 调用服务器端事件 'submitLogin' 并向服务器端传递账户名和密码&lt;br /&gt;
			triggerServerEvent(&amp;quot;submitLogin&amp;quot;, getRootElement(), username, password)&lt;br /&gt;
&lt;br /&gt;
			-- 恢复玩家的按键绑定(比如T键本地聊天),隐藏玩家的GUI画面,取消玩家鼠标的显示&lt;br /&gt;
			guiSetInputEnabled(false)&lt;br /&gt;
			guiSetVisible(wdwLogin, false)&lt;br /&gt;
			showCursor(false)&lt;br /&gt;
		else&lt;br /&gt;
			-- 用户名或密码有一个为空时,向玩家返回消息,并且不触发服务端事件&lt;br /&gt;
			-- 同时不隐藏GUI画面&lt;br /&gt;
			outputChatBox(&amp;quot;Please enter a username and password.&amp;quot;)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Creating the serverside event===&lt;br /&gt;
At this point we now have all the code needed on the client side, so open up your serverside 'script.lua' file (from the [[Scripting Introduction|Introduction to Scripting]]) or another suitable serverside file to work with.&lt;br /&gt;
&lt;br /&gt;
On the server side, recall that we are spawning the player as soon as they login.&lt;br /&gt;
So, first of all, we will need to define the custom event that we used before on the client. This can be done using [[addEvent]] and [[addEventHandler]].&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- create our loginHandler function, with username and password parameters (passed from the client gui)&lt;br /&gt;
function loginHandler(username,password)&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- define our custom event, and allow it to be triggered from the client ('true')&lt;br /&gt;
addEvent(&amp;quot;submitLogin&amp;quot;,true)&lt;br /&gt;
-- add an event handler so that when submitLogin is triggered, the function loginHandler is called&lt;br /&gt;
addEventHandler(&amp;quot;submitLogin&amp;quot;,root,loginHandler)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Logging in===&lt;br /&gt;
Now we have a function that is called through the custom event 'submitLogin', we can start to work on logging in and spawning the player, using our 'loginHandler' function:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function loginHandler(username,password)&lt;br /&gt;
	-- check that the username and password are correct&lt;br /&gt;
	if username == &amp;quot;user&amp;quot; and password == &amp;quot;apple&amp;quot; then&lt;br /&gt;
		-- the player has successfully logged in, so spawn them&lt;br /&gt;
		if (client) then&lt;br /&gt;
			spawnPlayer(client, 1959.55, -1714.46, 10)&lt;br /&gt;
			fadeCamera(client, true)&lt;br /&gt;
                        setCameraTarget(client, client)&lt;br /&gt;
			outputChatBox(&amp;quot;Welcome to My Server.&amp;quot;, client)&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		-- if the username or password are not correct, output a message to the player&lt;br /&gt;
		outputChatBox(&amp;quot;Invalid username and password. Please re-connect and try again.&amp;quot;,client)&lt;br /&gt;
        end			&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
addEvent(&amp;quot;submitLogin&amp;quot;,true)&lt;br /&gt;
addEventHandler(&amp;quot;submitLogin&amp;quot;,root,loginHandler)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
'''For the purposes of this tutorial, a very basic username and password system is shown. For a more comprehensive alternative, you can use the Account System or a MySQL database.'''&lt;br /&gt;
&lt;br /&gt;
Also note the use of the variable &amp;quot;client&amp;quot;, it's an internal variable used by MTA to identify the player who triggered the event. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Finally, do not forget to include the new gui.lua file in the meta.xml of the main resource, and label it as a client script:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;client/gui.lua&amp;quot; type=&amp;quot;client&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
At this point, we now have a basic login window that checks the player's username and password when the login button is clicked. If they are correct, the player is automatically spawned.&lt;br /&gt;
&lt;br /&gt;
有关GUI的进一步帮助,请参考 [[:Category:GUI_Tutorials|GUI tutorials]].&lt;br /&gt;
&lt;br /&gt;
[[Category:GUI_Tutorials]]&lt;br /&gt;
[[it:Introduzione_allo_scripting_della_GUI]]&lt;br /&gt;
[[ru:Introduction to Scripting the GUI]]&lt;br /&gt;
[[es:Introducción a la Programación de GUI]]&lt;/div&gt;</summary>
		<author><name>Skyvzla</name></author>
	</entry>
	<entry>
		<id>https://wiki.multitheftauto.com/index.php?title=SpawnPlayer&amp;diff=50413</id>
		<title>SpawnPlayer</title>
		<link rel="alternate" type="text/html" href="https://wiki.multitheftauto.com/index.php?title=SpawnPlayer&amp;diff=50413"/>
		<updated>2017-02-10T14:34:03Z</updated>

		<summary type="html">&lt;p&gt;Skyvzla: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Server function}}&lt;br /&gt;
__NOTOC__&lt;br /&gt;
This function spawns the player at an arbitary point on the map.&amp;lt;br&amp;gt;&lt;br /&gt;
{{Note|[[setCameraTarget]] must be used to focus on the player. Also, all players have their camera initially faded out after connect. To ensure that the camera is faded in, please do a [[fadeCamera]] after.}}&lt;br /&gt;
&lt;br /&gt;
==Syntax==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
bool spawnPlayer ( player thePlayer, float x, float y, float z, [ int rotation = 0, int skinID = 0, int interior = 0, int dimension = 0, team theTeam = getPlayerTeam(thePlayer) ] )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
{{OOP||[[player]]:spawn||}}&lt;br /&gt;
===Required Arguments===&lt;br /&gt;
*'''thePlayer:''' The player you want to spawn.&lt;br /&gt;
*'''x:''' The x co-ordinate to spawn the player at.&lt;br /&gt;
*'''y:''' The y co-ordinate to spawn the player at.&lt;br /&gt;
*'''z:''' The z co-ordinate to spawn the player at.&lt;br /&gt;
&lt;br /&gt;
===Optional Arguments===&lt;br /&gt;
*'''rotation:''' rotation of the player on spawn.&lt;br /&gt;
*'''skinID:''' player's skin on spawn. [[Character Skins]]&lt;br /&gt;
*'''interior:''' interior the player will spawn into. [[Interior IDs]]&lt;br /&gt;
*'''dimension:''' The ID of the [[dimension]] that the player should be in.&lt;br /&gt;
*'''theTeam:''' the team the player will join.&lt;br /&gt;
&lt;br /&gt;
===Returns===&lt;br /&gt;
Returns ''true'' if the player was spawned successfully, ''false'' otherwise.&lt;br /&gt;
&lt;br /&gt;
==Example==  &lt;br /&gt;
This example spawns all the players in the middle of the game map.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- Get a table of all the players&lt;br /&gt;
players = getElementsByType ( &amp;quot;player&amp;quot; )&lt;br /&gt;
-- Go through every player&lt;br /&gt;
for playerKey, playerValue in ipairs(players) do&lt;br /&gt;
	-- Spawn them at the desired coordinates&lt;br /&gt;
	spawnPlayer ( playerValue, 0.0, 0.0, 5.0, 90.0, 0 )&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This example spawns a player when he logs in.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
spawnTeam = createTeam (&amp;quot;Teamname&amp;quot;, 255, 0, 0) -- Create team to spawn.&lt;br /&gt;
function spawnOnLogin (prevA, curA )&lt;br /&gt;
	outputChatBox (&amp;quot;Welcome to ...&amp;quot;, source, 255, 0, 0, false)&lt;br /&gt;
	spawnPlayer (source, 0, 0, 5, 0, math.random (0,288), 0, 0, spawnTeam) -- spawns player with random skin&lt;br /&gt;
	fadeCamera (source, true)&lt;br /&gt;
	setCameraTarget (source, source)&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerLogin&amp;quot;, getRootElement(), spawnOnLogin)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
{{Player_functions}}&lt;br /&gt;
[[ru:spawnPlayer]]&lt;/div&gt;</summary>
		<author><name>Skyvzla</name></author>
	</entry>
	<entry>
		<id>https://wiki.multitheftauto.com/index.php?title=SpawnPlayer&amp;diff=50412</id>
		<title>SpawnPlayer</title>
		<link rel="alternate" type="text/html" href="https://wiki.multitheftauto.com/index.php?title=SpawnPlayer&amp;diff=50412"/>
		<updated>2017-02-10T14:24:05Z</updated>

		<summary type="html">&lt;p&gt;Skyvzla: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Server function}}&lt;br /&gt;
__NOTOC__&lt;br /&gt;
此函数是在地图上生成玩家(即刷新或者是重生玩家).&lt;br /&gt;
{{Note|[[setCameraTarget]] must be used to focus on the player. Also, all players have their camera initially faded out after connect. To ensure that the camera is faded in, please do a [[fadeCamera]] after.}}&lt;br /&gt;
&lt;br /&gt;
==Syntax==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
bool spawnPlayer ( player thePlayer, float x, float y, float z, [ int rotation = 0, int skinID = 0, int interior = 0, int dimension = 0, team theTeam = getPlayerTeam(thePlayer) ] )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
{{OOP||[[player]]:spawn||}}&lt;br /&gt;
===必须参数===&lt;br /&gt;
*'''thePlayer:''' 你想生成(复活)的玩家.&lt;br /&gt;
*'''x:''' 生成(复活)时所在的 x 坐标.&lt;br /&gt;
*'''y:''' 生成(复活)时所在的 y 坐标.&lt;br /&gt;
*'''z:''' 生成(复活)时所在的 z 坐标.&lt;br /&gt;
&lt;br /&gt;
===可选参数===&lt;br /&gt;
*'''rotation:''' rotation of the player on spawn.&lt;br /&gt;
*'''skinID:''' 生成(重生)玩家的皮肤ID. [[Character Skins]]&lt;br /&gt;
*'''interior:''' interior the player will spawn into. [[Interior IDs]]&lt;br /&gt;
*'''dimension:''' The ID of the [[dimension]] that the player should be in.&lt;br /&gt;
*'''theTeam:''' 玩家将加入的团队.&lt;br /&gt;
&lt;br /&gt;
===返回===&lt;br /&gt;
如果玩家生成(重生)成功,则返回'true',否则返回false.&lt;br /&gt;
&lt;br /&gt;
==例子== &lt;br /&gt;
这个例子生成(重生)服务器内所有的玩家.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- 获取所有玩家列表&lt;br /&gt;
players = getElementsByType ( &amp;quot;player&amp;quot; )&lt;br /&gt;
-- 通过遍历数组来获获取每一个玩家&lt;br /&gt;
for playerKey, playerValue in ipairs(players) do&lt;br /&gt;
	-- Spawn them at the desired coordinates&lt;br /&gt;
	spawnPlayer ( playerValue, 0.0, 0.0, 5.0, 90.0, 0 )&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
此例演示在玩家登陆时生成(重生)一个玩家.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
spawnTeam = createTeam (&amp;quot;Teamname&amp;quot;, 255, 0, 0) -- 创建一个名为&amp;quot;Teamname&amp;quot;的团队.&lt;br /&gt;
function spawnOnLogin (prevA, curA )&lt;br /&gt;
	outputChatBox (&amp;quot;Welcome to ...&amp;quot;, source, 255, 0, 0, false)&lt;br /&gt;
	spawnPlayer (source, 0, 0, 5, 0, math.random (0,288), 0, 0, spawnTeam) -- 生成(重生)玩家并使玩家皮肤ID数从0到288随机一个数&lt;br /&gt;
	fadeCamera (source, true)&lt;br /&gt;
	setCameraTarget (source, source)&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerLogin&amp;quot;, getRootElement(), spawnOnLogin)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
{{Player_functions}}&lt;br /&gt;
[[ru:spawnPlayer]]&lt;/div&gt;</summary>
		<author><name>Skyvzla</name></author>
	</entry>
	<entry>
		<id>https://wiki.multitheftauto.com/index.php?title=SpawnPlayer&amp;diff=50411</id>
		<title>SpawnPlayer</title>
		<link rel="alternate" type="text/html" href="https://wiki.multitheftauto.com/index.php?title=SpawnPlayer&amp;diff=50411"/>
		<updated>2017-02-10T14:21:42Z</updated>

		<summary type="html">&lt;p&gt;Skyvzla: Undo revision 50410 by Skyvzla (talk)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
{{Server Functions}}&lt;br /&gt;
此函数是在地图上生成玩家（即刷新或者是重生玩家）.&amp;lt;br&amp;gt;&lt;br /&gt;
{{Note|[[setCameraTarget]] must be used to focus on the player. Also, all players have their camera initially faded out after connect. To ensure that the camera is faded in, please do a [[fadeCamera]] after.}}&lt;br /&gt;
&lt;br /&gt;
==Syntax==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
bool spawnPlayer ( player thePlayer, float x, float y, float z, [ int rotation = 0, int skinID = 0, int interior = 0, int dimension = 0, team theTeam = getPlayerTeam(thePlayer) ] )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
{{OOP||[[player]]:spawn||}}&lt;br /&gt;
===必须参数===&lt;br /&gt;
*'''thePlayer:''' 你想生成(复活)的玩家.&lt;br /&gt;
*'''x:''' 生成(复活)时所在的 x 坐标.&lt;br /&gt;
*'''y:''' 生成(复活)时所在的 y 坐标.&lt;br /&gt;
*'''z:''' 生成(复活)时所在的 z 坐标.&lt;br /&gt;
&lt;br /&gt;
===可选参数===&lt;br /&gt;
*'''rotation:''' rotation of the player on spawn.&lt;br /&gt;
*'''skinID:''' 生成(重生)玩家的皮肤ID. [[Character Skins]]&lt;br /&gt;
*'''interior:''' interior the player will spawn into. [[Interior IDs]]&lt;br /&gt;
*'''dimension:''' The ID of the [[dimension]] that the player should be in.&lt;br /&gt;
*'''theTeam:''' 玩家将加入的团队.&lt;br /&gt;
&lt;br /&gt;
===返回===&lt;br /&gt;
如果玩家生成(重生)成功,则返回'true',否则返回false.&lt;br /&gt;
&lt;br /&gt;
==例子== &lt;br /&gt;
这个例子生成(重生)服务器内所有的玩家.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- 获取所有玩家列表&lt;br /&gt;
players = getElementsByType ( &amp;quot;player&amp;quot; )&lt;br /&gt;
-- 通过遍历数组来获获取每一个玩家&lt;br /&gt;
for playerKey, playerValue in ipairs(players) do&lt;br /&gt;
	-- Spawn them at the desired coordinates&lt;br /&gt;
	spawnPlayer ( playerValue, 0.0, 0.0, 5.0, 90.0, 0 )&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
此例演示在玩家登陆时生成(重生)一个玩家.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
spawnTeam = createTeam (&amp;quot;Teamname&amp;quot;, 255, 0, 0) -- 创建一个名为&amp;quot;Teamname&amp;quot;的团队.&lt;br /&gt;
function spawnOnLogin (prevA, curA )&lt;br /&gt;
	outputChatBox (&amp;quot;Welcome to ...&amp;quot;, source, 255, 0, 0, false)&lt;br /&gt;
	spawnPlayer (source, 0, 0, 5, 0, math.random (0,288), 0, 0, spawnTeam) -- 生成(重生)玩家并使玩家皮肤ID数从0到288随机一个数&lt;br /&gt;
	fadeCamera (source, true)&lt;br /&gt;
	setCameraTarget (source, source)&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerLogin&amp;quot;, getRootElement(), spawnOnLogin)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
{{Player_functions}}&lt;br /&gt;
[[ru:spawnPlayer]]&lt;/div&gt;</summary>
		<author><name>Skyvzla</name></author>
	</entry>
	<entry>
		<id>https://wiki.multitheftauto.com/index.php?title=SpawnPlayer&amp;diff=50410</id>
		<title>SpawnPlayer</title>
		<link rel="alternate" type="text/html" href="https://wiki.multitheftauto.com/index.php?title=SpawnPlayer&amp;diff=50410"/>
		<updated>2017-02-10T14:15:49Z</updated>

		<summary type="html">&lt;p&gt;Skyvzla: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
{{Server Functions}}&lt;br /&gt;
此函数是在地图上生成玩家（即刷新或者是重生玩家）.&amp;lt;br&amp;gt;&lt;br /&gt;
{{注意|[[setCameraTarget]] must be used to focus on the player. Also, all players have their camera initially faded out after connect. To ensure that the camera is faded in, please do a [[fadeCamera]] after.}}&lt;br /&gt;
&lt;br /&gt;
==Syntax==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
bool spawnPlayer ( player thePlayer, float x, float y, float z, [ int rotation = 0, int skinID = 0, int interior = 0, int dimension = 0, team theTeam = getPlayerTeam(thePlayer) ] )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
{{OOP||[[player]]:spawn||}}&lt;br /&gt;
===必须参数===&lt;br /&gt;
*'''thePlayer:''' 你想生成(复活)的玩家.&lt;br /&gt;
*'''x:''' 生成(复活)时所在的 x 坐标.&lt;br /&gt;
*'''y:''' 生成(复活)时所在的 y 坐标.&lt;br /&gt;
*'''z:''' 生成(复活)时所在的 z 坐标.&lt;br /&gt;
&lt;br /&gt;
===可选参数===&lt;br /&gt;
*'''rotation:''' rotation of the player on spawn.&lt;br /&gt;
*'''skinID:''' 生成(重生)玩家的皮肤ID. [[Character Skins]]&lt;br /&gt;
*'''interior:''' interior the player will spawn into. [[Interior IDs]]&lt;br /&gt;
*'''dimension:''' The ID of the [[dimension]] that the player should be in.&lt;br /&gt;
*'''theTeam:''' 玩家将加入的团队.&lt;br /&gt;
&lt;br /&gt;
===返回===&lt;br /&gt;
如果玩家生成(重生)成功,则返回'true',否则返回false.&lt;br /&gt;
&lt;br /&gt;
==例子== &lt;br /&gt;
这个例子生成(重生)服务器内所有的玩家.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- 获取所有玩家列表&lt;br /&gt;
players = getElementsByType ( &amp;quot;player&amp;quot; )&lt;br /&gt;
-- 通过遍历数组来获获取每一个玩家&lt;br /&gt;
for playerKey, playerValue in ipairs(players) do&lt;br /&gt;
	-- Spawn them at the desired coordinates&lt;br /&gt;
	spawnPlayer ( playerValue, 0.0, 0.0, 5.0, 90.0, 0 )&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
此例演示在玩家登陆时生成(重生)一个玩家.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
spawnTeam = createTeam (&amp;quot;Teamname&amp;quot;, 255, 0, 0) -- 创建一个名为&amp;quot;Teamname&amp;quot;的团队.&lt;br /&gt;
function spawnOnLogin (prevA, curA )&lt;br /&gt;
	outputChatBox (&amp;quot;Welcome to ...&amp;quot;, source, 255, 0, 0, false)&lt;br /&gt;
	spawnPlayer (source, 0, 0, 5, 0, math.random (0,288), 0, 0, spawnTeam) -- 生成(重生)玩家并使玩家皮肤ID数从0到288随机一个数&lt;br /&gt;
	fadeCamera (source, true)&lt;br /&gt;
	setCameraTarget (source, source)&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerLogin&amp;quot;, getRootElement(), spawnOnLogin)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
{{Player_functions}}&lt;br /&gt;
[[ru:spawnPlayer]]&lt;/div&gt;</summary>
		<author><name>Skyvzla</name></author>
	</entry>
	<entry>
		<id>https://wiki.multitheftauto.com/index.php?title=SpawnPlayer&amp;diff=50409</id>
		<title>SpawnPlayer</title>
		<link rel="alternate" type="text/html" href="https://wiki.multitheftauto.com/index.php?title=SpawnPlayer&amp;diff=50409"/>
		<updated>2017-02-10T14:15:15Z</updated>

		<summary type="html">&lt;p&gt;Skyvzla: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;__NOTOC__&lt;br /&gt;
{{服务器函数}}&lt;br /&gt;
此函数是在地图上生成玩家（即刷新或者是重生玩家）.&amp;lt;br&amp;gt;&lt;br /&gt;
{{注意|[[setCameraTarget]] must be used to focus on the player. Also, all players have their camera initially faded out after connect. To ensure that the camera is faded in, please do a [[fadeCamera]] after.}}&lt;br /&gt;
&lt;br /&gt;
==Syntax==&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
bool spawnPlayer ( player thePlayer, float x, float y, float z, [ int rotation = 0, int skinID = 0, int interior = 0, int dimension = 0, team theTeam = getPlayerTeam(thePlayer) ] )&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
{{OOP||[[player]]:spawn||}}&lt;br /&gt;
===必须参数===&lt;br /&gt;
*'''thePlayer:''' 你想生成(复活)的玩家.&lt;br /&gt;
*'''x:''' 生成(复活)时所在的 x 坐标.&lt;br /&gt;
*'''y:''' 生成(复活)时所在的 y 坐标.&lt;br /&gt;
*'''z:''' 生成(复活)时所在的 z 坐标.&lt;br /&gt;
&lt;br /&gt;
===可选参数===&lt;br /&gt;
*'''rotation:''' rotation of the player on spawn.&lt;br /&gt;
*'''skinID:''' 生成(重生)玩家的皮肤ID. [[Character Skins]]&lt;br /&gt;
*'''interior:''' interior the player will spawn into. [[Interior IDs]]&lt;br /&gt;
*'''dimension:''' The ID of the [[dimension]] that the player should be in.&lt;br /&gt;
*'''theTeam:''' 玩家将加入的团队.&lt;br /&gt;
&lt;br /&gt;
===返回===&lt;br /&gt;
如果玩家生成(重生)成功,则返回'true',否则返回false.&lt;br /&gt;
&lt;br /&gt;
==例子== &lt;br /&gt;
这个例子生成(重生)服务器内所有的玩家.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- 获取所有玩家列表&lt;br /&gt;
players = getElementsByType ( &amp;quot;player&amp;quot; )&lt;br /&gt;
-- 通过遍历数组来获获取每一个玩家&lt;br /&gt;
for playerKey, playerValue in ipairs(players) do&lt;br /&gt;
	-- Spawn them at the desired coordinates&lt;br /&gt;
	spawnPlayer ( playerValue, 0.0, 0.0, 5.0, 90.0, 0 )&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
此例演示在玩家登陆时生成(重生)一个玩家.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
spawnTeam = createTeam (&amp;quot;Teamname&amp;quot;, 255, 0, 0) -- 创建一个名为&amp;quot;Teamname&amp;quot;的团队.&lt;br /&gt;
function spawnOnLogin (prevA, curA )&lt;br /&gt;
	outputChatBox (&amp;quot;Welcome to ...&amp;quot;, source, 255, 0, 0, false)&lt;br /&gt;
	spawnPlayer (source, 0, 0, 5, 0, math.random (0,288), 0, 0, spawnTeam) -- 生成(重生)玩家并使玩家皮肤ID数从0到288随机一个数&lt;br /&gt;
	fadeCamera (source, true)&lt;br /&gt;
	setCameraTarget (source, source)&lt;br /&gt;
end&lt;br /&gt;
addEventHandler(&amp;quot;onPlayerLogin&amp;quot;, getRootElement(), spawnOnLogin)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
{{Player_functions}}&lt;br /&gt;
[[ru:spawnPlayer]]&lt;/div&gt;</summary>
		<author><name>Skyvzla</name></author>
	</entry>
	<entry>
		<id>https://wiki.multitheftauto.com/index.php?title=ZH-CN/%E8%84%9A%E6%9C%AC%E7%BC%96%E5%86%99%E4%BB%8B%E7%BB%8D_-_%E5%B8%A6%E6%9C%89%E5%9B%BE%E5%BD%A2%E7%95%8C%E9%9D%A2&amp;diff=50406</id>
		<title>ZH-CN/脚本编写介绍 - 带有图形界面</title>
		<link rel="alternate" type="text/html" href="https://wiki.multitheftauto.com/index.php?title=ZH-CN/%E8%84%9A%E6%9C%AC%E7%BC%96%E5%86%99%E4%BB%8B%E7%BB%8D_-_%E5%B8%A6%E6%9C%89%E5%9B%BE%E5%BD%A2%E7%95%8C%E9%9D%A2&amp;diff=50406"/>
		<updated>2017-02-10T13:49:12Z</updated>

		<summary type="html">&lt;p&gt;Skyvzla: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- place holder --&amp;gt;&lt;br /&gt;
在MTA:SA中有一个重要的特点，那就是可以制作自定义的GUI(Graphic User Interface)。GUI由窗口，按钮，编辑框，复选框等组成，拥有在图形环境中大部分的基础控件。它们可以在玩家正在游戏的时候显示，常常被用来代替传统命令的输入和输出。&lt;br /&gt;
&lt;br /&gt;
[[Image:AdminGUI.png|thumb|管理员后台GUI]]&lt;br /&gt;
&lt;br /&gt;
==一个做登陆窗口的教程==&lt;br /&gt;
在这个教程中我们会使用两个编辑框和一个按钮制作一个登录窗口。 窗口会在玩家加入游戏的时候出现，一旦按钮被点击，玩家将会被出生。 这个教程会继续我们在 [[Scripting Introduction|脚本介绍]] 中制作的游戏模式''（如果你已经使用了 [[Scripting Introduction|脚本介绍]]，你需要在你的代码中移除或者注释掉在 &amp;quot;joinHandler&amp;quot; 行的 [[spawnPlayer]] 函数， 我们也将会在这个教程中使用一个gui替换掉它）''。 我们也会接触一下客户端脚本的编辑. &lt;br /&gt;
&lt;br /&gt;
===画一个窗口===&lt;br /&gt;
所有的GUI只能在客户端制作。 这是一个很好的练习机会来把所有客户端脚本放在分开的目录。&lt;br /&gt;
&lt;br /&gt;
浏览到目录 /你的MTA服务端目录/mods/deathmatch/resources/myserver/ , 然后创建一个名为 &amp;quot;client&amp;quot; 的目录。 在 /client/ 目录下, 创建一个文件，文件名为 &amp;quot;gui.lua&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
在这个文件里，我们将会写一个画窗口的函数。使用[[guiCreateWindow]]来创建一个窗口：&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function createLoginWindow()&lt;br /&gt;
	-- 定义窗口的X和Y坐标&lt;br /&gt;
	local X = 0.375&lt;br /&gt;
	local Y = 0.375&lt;br /&gt;
	-- 定义窗口的宽度和高度&lt;br /&gt;
	local Width = 0.25&lt;br /&gt;
	local Height = 0.25&lt;br /&gt;
        -- 创建一个窗口，并且把它放入变量 'wdwLogin' 里&lt;br /&gt;
        -- 点击这个函数的名字可以阅读这个函数的相关信息&lt;br /&gt;
	wdwLogin = guiCreateWindow(X, Y, Width, Height, &amp;quot;请登录&amp;quot;, true)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===相对和绝对坐标===&lt;br /&gt;
注意：传给 guiCreateWindow 的最后一个参数在上面的例子中是 ''true''。这个表明了窗口的坐标和尺寸都是 '''相对的'''，也就是说他们是一个相对于屏幕大小的 ''百分比''。我来解释一下：如果屏幕最左侧是0，那么最右侧就是1，那么X坐标为0.5将代表着屏幕中央。同理，屏幕顶部和底部也是一样的，最顶部为0，最底部为1，Y坐标为0.2则代表着是屏幕高度的20%。宽度和高度也是一样的道理（宽度为0.5则意味着窗口是屏幕的一半宽）。&lt;br /&gt;
&lt;br /&gt;
另外的，也可以用 '''绝对的''' （将传入 guiCreateWindow 的最后一个参数改为 ''false'' 即可）。绝对值被计算为父级的左上角开始到右下角的像素总数（如果没有GUI元素指定父级，那么父级是屏幕本身）. 如果我们假设屏幕的分辨率为1920*1200，那么从屏幕左边开始为0像素，到屏幕右边为1920像素，X坐标为960代表屏幕的中点。同理，屏幕顶部为0，到屏幕底部则为1200，Y坐标为20代表着距离屏幕顶部的20个像素。宽度和高度也是相同的道理（宽度为50则意味着窗口为50个像素宽）。 ''你可以使用 [[guiGetScreenSize]] 和一点数学来计算某些绝对坐标。''&lt;br /&gt;
&lt;br /&gt;
使用相对值和绝对值的不同点非常简单：使用绝对值创建gui经常精确地保持着相同的像素大小和坐标，然而使用相对值创建gui经常是与它父级gui大小的比值。&lt;br /&gt;
&lt;br /&gt;
当你用手敲代码的时候，绝对值一般是更容易维护的。然而你的选择是根据你的目的而变化的。&lt;br /&gt;
&lt;br /&gt;
为了本介绍的目的，我们将使用相对值。&lt;br /&gt;
&lt;br /&gt;
===添加组件===&lt;br /&gt;
接下来， 我们要添加文本标签（里面写上 “帐号：” 和 “密码：”）、编辑框（为了输入你的数据）和一个登录按钮。&lt;br /&gt;
&lt;br /&gt;
我们可以使用 [[guiCreateButtonTo]] 创建按钮，创建 [[guiCreateEdit]] 创建编辑框。&lt;br /&gt;
&lt;br /&gt;
'''注意：我们现在正在给我们已经存在的 ‘createLoginWindow’函数写更多的代码。这不是一个新的函数，这个函数是用来替换你脚本中已经存在的那个。''' &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function createLoginWindow()&lt;br /&gt;
	local X = 0.375&lt;br /&gt;
	local Y = 0.375&lt;br /&gt;
	local Width = 0.25&lt;br /&gt;
	local Height = 0.25&lt;br /&gt;
	wdwLogin = guiCreateWindow(X, Y, Width, Height, &amp;quot;请登录&amp;quot;, true)&lt;br /&gt;
	&lt;br /&gt;
	-- 给第一个文本标签定义新的X和Y坐标&lt;br /&gt;
	X = 0.0825&lt;br /&gt;
	Y = 0.2&lt;br /&gt;
	-- 给第一个文本标签定义新的宽度和高度&lt;br /&gt;
	Width = 0.25&lt;br /&gt;
	Height = 0.25&lt;br /&gt;
	-- 创建第一个文本标签，注意最后一个传入的参数是 ‘wdwLogin’，这个参数是你刚刚创建的窗口（window）&lt;br /&gt;
	-- 我们在它的父级gui上方创建了这个文本标签（所以所有的坐标和大小的值都是相对于这个窗口的）&lt;br /&gt;
	guiCreateLabel(X, Y, Width, Height, &amp;quot;帐号&amp;quot;, true, wdwLogin)&lt;br /&gt;
	-- 改变Y的值，所以第二个文本标签是在第一个下方的&lt;br /&gt;
	Y = 0.5&lt;br /&gt;
	guiCreateLabel(X, Y, Width, Height, &amp;quot;密码&amp;quot;, true, wdwLogin)&lt;br /&gt;
	&lt;br /&gt;
&lt;br /&gt;
	X = 0.415&lt;br /&gt;
	Y = 0.2&lt;br /&gt;
	Width = 0.5&lt;br /&gt;
	Height = 0.15&lt;br /&gt;
	edtUser = guiCreateEdit(X, Y, Width, Height, &amp;quot;&amp;quot;, true, wdwLogin)&lt;br /&gt;
	Y = 0.5&lt;br /&gt;
	edtPass = guiCreateEdit(X, Y, Width, Height, &amp;quot;&amp;quot;, true, wdwLogin)&lt;br /&gt;
	-- 设置编辑框的最大文字长度为50&lt;br /&gt;
	guiEditSetMaxLength(edtUser, 50)&lt;br /&gt;
	guiEditSetMaxLength(edtPass, 50)&lt;br /&gt;
	&lt;br /&gt;
	X = 0.415&lt;br /&gt;
	Y = 0.7&lt;br /&gt;
	Width = 0.25&lt;br /&gt;
	Height = 0.2&lt;br /&gt;
	btnLogin = guiCreateButton(X, Y, Width, Height, &amp;quot;Log In&amp;quot;, true, wdwLogin)&lt;br /&gt;
	&lt;br /&gt;
	-- 让这个窗口不显示&lt;br /&gt;
	guiSetVisible(wdwLogin, false)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
注意：每个GUI组件都是创建为window的子级，这是当创建组件时通过指定父级元素（在这种情况下是wdwLogin）来完成的&lt;br /&gt;
&lt;br /&gt;
这是很有用的，因为这不仅意味着所有组件都是附着在window并且移动的时候会带着这些组件一起移动，而且任何对父级window的改变都会被应用到这些子级元素。例如，我们可以隐藏所有的GUI，我们只要隐藏window就行了：&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
guiSetVisible(wdwLogin, false) --隐藏我们制作的所有的GUI，这样我们才能在适当的时候显示GUI&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===使用我们编写完成的功能===&lt;br /&gt;
'''createLoginWindow''' 功能编写完成,但是它'''不会'''执行,必须要我们'''调用'''它才可以执行.建议在'''客户端启动资源时'''创建所有的GUI,当前不需要显示时可以进行隐藏（如果需要立即显示就不用隐藏）,或在我们需要用到它的时候显示给玩家.因此,我们需要为GUI窗口创建一个'''事件触发器''',事件触发器的事件为:&amp;quot;[[onClientResourceStart]]&amp;quot;:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- 将事件处理程序添加到资源的根元素&lt;br /&gt;
-- 只有在这个资源本启动时才会触发&lt;br /&gt;
addEventHandler(&amp;quot;onClientResourceStart&amp;quot;, getResourceRootElement(), &lt;br /&gt;
	function ()&lt;br /&gt;
		createLoginWindow()&lt;br /&gt;
	end&lt;br /&gt;
)	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
因为脚本是登陆界面,所以我们要在玩家进入游戏后显示出来. &lt;br /&gt;
也可以使用 &amp;quot;[[onClientResourceStart]]&amp;quot; 事件来完成,所以我们将上面的代码进行修改,来达到我们的目的:&lt;br /&gt;
&lt;br /&gt;
'''注意,我们是为现有的 'onClientResourceStart' 事件处理程序编写更多的代码.不是新创建的一个事件处理程序.''' &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
addEventHandler(&amp;quot;onClientResourceStart&amp;quot;, getResourceRootElement(), &lt;br /&gt;
	function ()&lt;br /&gt;
		-- 创建已经编写好的窗口以及窗口组件&lt;br /&gt;
		createLoginWindow()&lt;br /&gt;
&lt;br /&gt;
		-- 输出一个欢迎信息给玩家&lt;br /&gt;
                outputChatBox(&amp;quot;Welcome to My MTA:SA Server, please log in.&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
		-- 用if函数判断窗口是否创建成功，如果成功将窗口显示给玩家&lt;br /&gt;
	        if (wdwLogin ~= nil) then&lt;br /&gt;
			guiSetVisible(wdwLogin, true)&lt;br /&gt;
		else&lt;br /&gt;
			-- 如果窗口创建失败时，告诉玩家窗口创建失败&lt;br /&gt;
			outputChatBox(&amp;quot;An unexpected error has occurred and the log in GUI has not been created.&amp;quot;)&lt;br /&gt;
	        end &lt;br /&gt;
&lt;br /&gt;
		-- 启用玩家的鼠标显示,以便点击窗口中的组件&lt;br /&gt;
	        showCursor(true)&lt;br /&gt;
		-- 设置玩家绑定的所有按键都不生效,包括'T '键的本地发言,以保证输入的信息都输入到编辑框内&lt;br /&gt;
	        guiSetInputEnabled(true)&lt;br /&gt;
	end&lt;br /&gt;
)	&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
注意,在显示窗口之前,我们使用了一个if函数来判断窗口是否创建成功,创建成功的情况下将显示出来,创建不成功时就意味着 wdwLogin 不是一个元素,服务器也不会出现错误提示,只会提示玩家窗口创建失败.&lt;br /&gt;
下一步我们将给按钮(button)创建点击后的功能&lt;br /&gt;
==给按钮写一个功能==&lt;br /&gt;
既然我们已经创建了我们的GUI并且展示给了玩家看，我们需要让它运行。&lt;br /&gt;
&lt;br /&gt;
===检测点击===&lt;br /&gt;
When the player clicks on any part of the GUI, the event &amp;quot;[[onClientGUIClick]]&amp;quot; will be triggered for the GUI component you clicked on. This allows us to easily detect any clicks on the GUI elements we want to use.&lt;br /&gt;
For example, we can attach the event to the btnLogin button to catch any clicks on it:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- attach the event onClientGUIClick to btnLogin and set it to trigger the 'clientSubmitLogin' function&lt;br /&gt;
addEventHandler(&amp;quot;onClientGUIClick&amp;quot;, btnLogin, clientSubmitLogin, false)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
'''Note the final argument passed is &amp;quot;false&amp;quot;. This indicates that the event will only trigger directly on btnLogin, not if the event has propagated up or down the tree. Setting this to &amp;quot;true&amp;quot; while attaching to gui elements will mean that clicking on any element in the same branch will trigger this event.'''&lt;br /&gt;
&lt;br /&gt;
This line of code can now be added inside the createLoginWindow function. It is a common mistake to try and attach events to non-existant GUI elements, so make sure you always attach your events '''after''' the gui element (in this case, the button) has been created:&lt;br /&gt;
&lt;br /&gt;
'''Note that we are now writing more code for our existing 'createLoginWindow' function.''' &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function createLoginWindow()&lt;br /&gt;
	-- create all our GUI elements&lt;br /&gt;
	...&lt;br /&gt;
&lt;br /&gt;
	-- now add our onClientGUIClick event to the button we just created&lt;br /&gt;
	addEventHandler(&amp;quot;onClientGUIClick&amp;quot;, btnLogin, clientSubmitLogin, false)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Managing the click===&lt;br /&gt;
Now that we can detect when the player clicks on the button, we need to write code to manage what happens when they do.&lt;br /&gt;
In our [[onClientGUIClick]] event handle, we told it to call the function clientSubmitLogin whenever btnLogin is clicked.&lt;br /&gt;
Therefore, we can now use the function clientSubmitLogin to control what happens when the button is clicked:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- create the function and define the 'button' and 'state' parameters&lt;br /&gt;
-- (these are passed automatically by onClientGUIClick)&lt;br /&gt;
function clientSubmitLogin(button,state)&lt;br /&gt;
	-- if our login button was clicked with the left mouse button, and the state of the mouse button is up&lt;br /&gt;
	if button == &amp;quot;left&amp;quot; and state == &amp;quot;up&amp;quot; then&lt;br /&gt;
		-- move the input focus back onto the game (allowing players to move around, open the chatbox, etc)&lt;br /&gt;
		guiSetInputEnabled(false)&lt;br /&gt;
		-- hide the window and all the components&lt;br /&gt;
		guiSetVisible(wdwLogin, false)&lt;br /&gt;
		-- hide the mouse cursor&lt;br /&gt;
		showCursor(false)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Now, when the button is clicked, the window will be hidden and all controls will be returned to the player. Next, we will tell the server to allow the player to spawn.&lt;br /&gt;
&lt;br /&gt;
===触发服务器事件===&lt;br /&gt;
Triggering the server can be done using [[triggerServerEvent]]. This allows you to trigger a specified event on the server from the client. The same can be done in reverse using [[triggerClientEvent]].&lt;br /&gt;
Here, we use the [[triggerServerEvent]] function to call our own custom event on the server, named &amp;quot;submitLogin&amp;quot;, which will then control the spawning of the player serverside.&lt;br /&gt;
&lt;br /&gt;
'''Note that we are now writing more code for our existing 'clientSubmitLogin' function. This is not a new function and is meant to replace what you already have.''' &lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function clientSubmitLogin(button,state)&lt;br /&gt;
	if button == &amp;quot;left&amp;quot; and state == &amp;quot;up&amp;quot; then&lt;br /&gt;
		-- get the text entered in the 'username' field&lt;br /&gt;
		local username = guiGetText(edtUser)&lt;br /&gt;
		-- get the text entered in the 'password' field&lt;br /&gt;
		local password = guiGetText(edtPass)&lt;br /&gt;
&lt;br /&gt;
		-- if the username and password both exist&lt;br /&gt;
		if username and password then&lt;br /&gt;
			-- trigger the server event 'submitLogin' and pass the username and password to it&lt;br /&gt;
			triggerServerEvent(&amp;quot;submitLogin&amp;quot;, getRootElement(), username, password)&lt;br /&gt;
&lt;br /&gt;
			-- hide the gui, hide the cursor and return control to the player&lt;br /&gt;
			guiSetInputEnabled(false)&lt;br /&gt;
			guiSetVisible(wdwLogin, false)&lt;br /&gt;
			showCursor(false)&lt;br /&gt;
		else&lt;br /&gt;
			-- otherwise, output a message to the player, do not trigger the server&lt;br /&gt;
			-- and do not hide the gui&lt;br /&gt;
			outputChatBox(&amp;quot;Please enter a username and password.&amp;quot;)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Creating the serverside event===&lt;br /&gt;
At this point we now have all the code needed on the client side, so open up your serverside 'script.lua' file (from the [[Scripting Introduction|Introduction to Scripting]]) or another suitable serverside file to work with.&lt;br /&gt;
&lt;br /&gt;
On the server side, recall that we are spawning the player as soon as they login.&lt;br /&gt;
So, first of all, we will need to define the custom event that we used before on the client. This can be done using [[addEvent]] and [[addEventHandler]].&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
-- create our loginHandler function, with username and password parameters (passed from the client gui)&lt;br /&gt;
function loginHandler(username,password)&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- define our custom event, and allow it to be triggered from the client ('true')&lt;br /&gt;
addEvent(&amp;quot;submitLogin&amp;quot;,true)&lt;br /&gt;
-- add an event handler so that when submitLogin is triggered, the function loginHandler is called&lt;br /&gt;
addEventHandler(&amp;quot;submitLogin&amp;quot;,root,loginHandler)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Logging in===&lt;br /&gt;
Now we have a function that is called through the custom event 'submitLogin', we can start to work on logging in and spawning the player, using our 'loginHandler' function:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;lua&amp;quot;&amp;gt;&lt;br /&gt;
function loginHandler(username,password)&lt;br /&gt;
	-- check that the username and password are correct&lt;br /&gt;
	if username == &amp;quot;user&amp;quot; and password == &amp;quot;apple&amp;quot; then&lt;br /&gt;
		-- the player has successfully logged in, so spawn them&lt;br /&gt;
		if (client) then&lt;br /&gt;
			spawnPlayer(client, 1959.55, -1714.46, 10)&lt;br /&gt;
			fadeCamera(client, true)&lt;br /&gt;
                        setCameraTarget(client, client)&lt;br /&gt;
			outputChatBox(&amp;quot;Welcome to My Server.&amp;quot;, client)&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		-- if the username or password are not correct, output a message to the player&lt;br /&gt;
		outputChatBox(&amp;quot;Invalid username and password. Please re-connect and try again.&amp;quot;,client)&lt;br /&gt;
        end			&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
addEvent(&amp;quot;submitLogin&amp;quot;,true)&lt;br /&gt;
addEventHandler(&amp;quot;submitLogin&amp;quot;,root,loginHandler)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
'''For the purposes of this tutorial, a very basic username and password system is shown. For a more comprehensive alternative, you can use the Account System or a MySQL database.'''&lt;br /&gt;
&lt;br /&gt;
Also note the use of the variable &amp;quot;client&amp;quot;, it's an internal variable used by MTA to identify the player who triggered the event. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Finally, do not forget to include the new gui.lua file in the meta.xml of the main resource, and label it as a client script:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;script src=&amp;quot;client/gui.lua&amp;quot; type=&amp;quot;client&amp;quot; /&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
At this point, we now have a basic login window that checks the player's username and password when the login button is clicked. If they are correct, the player is automatically spawned.&lt;br /&gt;
&lt;br /&gt;
有关GUI的进一步帮助,请参考 [[:Category:GUI_Tutorials|GUI tutorials]].&lt;br /&gt;
&lt;br /&gt;
[[Category:GUI_Tutorials]]&lt;br /&gt;
[[it:Introduzione_allo_scripting_della_GUI]]&lt;br /&gt;
[[ru:Introduction to Scripting the GUI]]&lt;br /&gt;
[[es:Introducción a la Programación de GUI]]&lt;/div&gt;</summary>
		<author><name>Skyvzla</name></author>
	</entry>
</feed>