ZH-CN/脚本编写介绍 - 带有图形界面: Difference between revisions

From Multi Theft Auto: Wiki
Jump to navigation Jump to search
(Blanked the page)
Line 1: Line 1:
<!-- place holder -->
在MTA:SA中有一个重要的特点,那就是可以制作自定义的GUI(Graphic User Interface)。GUI由窗口,按钮,编辑框,复选框等组成,拥有在图形环境中大部分的基础控件。它们可以在玩家正在游戏的时候显示,常常被用来代替传统命令的输入和输出。


[[Image:AdminGUI.png|thumb|管理员后台GUI]]
==一个做登陆窗口的教程==
在这个教程中我们会使用两个编辑框和一个按钮制作一个登录窗口。 窗口会在玩家加入游戏的时候出现,一旦按钮被点击,玩家将会被出生。 这个教程会继续我们在 [[Scripting Introduction|脚本介绍]] 中制作的游戏模式''(如果你已经使用了 [[Scripting Introduction|脚本介绍]],你需要在你的代码中移除或者注释掉在 "joinHandler" 行的 [[spawnPlayer]] 函数, 我们也将会在这个教程中使用一个gui替换掉它)''。 我们也会接触一下客户端脚本的编辑.
===画一个窗口===
所有的GUI只能在客户端制作。 这是一个很好的练习机会来把所有客户端脚本放在分开的目录。
浏览到目录 /你的MTA服务端目录/mods/deathmatch/resources/myserver/ , 然后创建一个名为 "client" 的目录。 在 /client/ 目录下, 创建一个文件,文件名为 "gui.lua".
在这个文件里,我们将会写一个画窗口的函数。使用[[guiCreateWindow]]来创建一个窗口:
<syntaxhighlight lang="lua">
function createLoginWindow()
-- 定义窗口的X和Y坐标
local X = 0.375
local Y = 0.375
-- 定义窗口的宽度和高度
local Width = 0.25
local Height = 0.25
        -- 创建一个窗口,并且把它放入变量 'wdwLogin' 里
        -- 点击这个函数的名字可以阅读这个函数的相关信息
wdwLogin = guiCreateWindow(X, Y, Width, Height, "请登录", true)
end
</syntaxhighlight>
===相对和绝对坐标===
注意:传给 guiCreateWindow 的最后一个参数在上面的例子中是 ''true''。这个表明了窗口的坐标和尺寸都是 '''相对的''',也就是说他们是一个相对于屏幕大小的 ''百分比''。我来解释一下:如果屏幕最左侧是0,那么最右侧就是1,那么X坐标为0.5将代表着屏幕中央。同理,屏幕顶部和底部也是一样的,最顶部为0,最底部为1,Y坐标为0.2则代表着是屏幕高度的20%。宽度和高度也是一样的道理(宽度为0.5则意味着窗口是屏幕的一半宽)。
另外的,也可以用 '''绝对的''' (将传入 guiCreateWindow 的最后一个参数改为 ''false'' 即可)。绝对值被计算为父级的左上角开始到右下角的像素总数(如果没有GUI元素指定父级,那么父级是屏幕本身). 如果我们假设屏幕的分辨率为1920*1200,那么从屏幕左边开始为0像素,到屏幕右边为1920像素,X坐标为960代表屏幕的中点。同理,屏幕顶部为0,到屏幕底部则为1200,Y坐标为20代表着距离屏幕顶部的20个像素。宽度和高度也是相同的道理(宽度为50则意味着窗口为50个像素宽)。 ''你可以使用 [[guiGetScreenSize]] 和一点数学来计算某些绝对坐标。''
使用相对值和绝对值的不同点非常简单:使用绝对值创建gui经常精确地保持着相同的像素大小和坐标,然而使用相对值创建gui经常是与它父级gui大小的比值。
当你用手敲代码的时候,绝对值一般是更容易维护的。然而你的选择是根据你的目的而变化的。
为了本介绍的目的,我们将使用相对值。
===添加组件===
接下来, 我们要添加文本标签(里面写上 “帐号:” 和 “密码:”)、编辑框(为了输入你的数据)和一个登录按钮。
我们可以使用 [[guiCreateButtonTo]] 创建按钮,创建 [[guiCreateEdit]] 创建编辑框。
'''注意:我们现在正在给我们已经存在的 ‘createLoginWindow’函数写更多的代码。这不是一个新的函数,这个函数是用来替换你脚本中已经存在的那个。'''
<syntaxhighlight lang="lua">
function createLoginWindow()
local X = 0.375
local Y = 0.375
local Width = 0.25
local Height = 0.25
wdwLogin = guiCreateWindow(X, Y, Width, Height, "请登录", true)
-- 给第一个文本标签定义新的X和Y坐标
X = 0.0825
Y = 0.2
-- 给第一个文本标签定义新的宽度和高度
Width = 0.25
Height = 0.25
-- 创建第一个文本标签,注意最后一个传入的参数是 ‘wdwLogin’,这个参数是你刚刚创建的窗口(window)
-- 我们在它的父级gui上方创建了这个文本标签(所以所有的坐标和大小的值都是相对于这个窗口的)
guiCreateLabel(X, Y, Width, Height, "帐号", true, wdwLogin)
-- 改变Y的值,所以第二个文本标签是在第一个下方的
Y = 0.5
guiCreateLabel(X, Y, Width, Height, "密码", true, wdwLogin)
X = 0.415
Y = 0.2
Width = 0.5
Height = 0.15
edtUser = guiCreateEdit(X, Y, Width, Height, "", true, wdwLogin)
Y = 0.5
edtPass = guiCreateEdit(X, Y, Width, Height, "", true, wdwLogin)
-- 设置编辑框的最大文字长度为50
guiEditSetMaxLength(edtUser, 50)
guiEditSetMaxLength(edtPass, 50)
X = 0.415
Y = 0.7
Width = 0.25
Height = 0.2
btnLogin = guiCreateButton(X, Y, Width, Height, "Log In", true, wdwLogin)
-- 让这个窗口不显示
guiSetVisible(wdwLogin, false)
end
</syntaxhighlight>
注意:每个GUI组件都是创建为window的子级,这是当创建组件时通过指定父级元素(在这种情况下是wdwLogin)来完成的
这是很有用的,因为这不仅意味着所有组件都是附着在window并且移动的时候会带着这些组件一起移动,而且任何对父级window的改变都会被应用到这些子级元素。例如,我们可以隐藏所有的GUI,我们只要隐藏window就行了:
<syntaxhighlight lang="lua">
guiSetVisible(wdwLogin, false) --隐藏我们制作的所有的GUI,这样我们才能在适当的时候显示GUI
</syntaxhighlight>
===Using the function we wrote===
The createLoginWindow function is now complete, but it won't do anything until we call it. It is recommended to create all GUI when the client resource starts, hide them, and show them to the player later when needed. Therefore, we'll write an event handler for "[[onClientResourceStart]]" to create the window:
<syntaxhighlight lang="lua">
-- attach the event handler to the root element of the resource
-- this means it will only trigger when its own resource is started
addEventHandler("onClientResourceStart", getResourceRootElement(),
function ()
createLoginWindow()
end
)
</syntaxhighlight>
As this is a log in window, we now need to show the window when the player joins the game.
This can be done using the same event, "[[onClientResourceStart]]", so we can modify the above code to include showing the window:
'''Note that we are now writing more code for our existing 'onClientResourceStart' handler. This is not a new event handler and is meant to replace what you already have.'''
<syntaxhighlight lang="lua">
addEventHandler("onClientResourceStart", getResourceRootElement(),
function ()
-- create the log in window and its components
createLoginWindow()
-- output a brief welcome message to the player
                outputChatBox("Welcome to My MTA:SA Server, please log in.")
-- if the GUI was successfully created, then show the GUI to the player
        if (wdwLogin ~= nil) then
guiSetVisible(wdwLogin, true)
else
-- if the GUI hasnt been properly created, tell the player
outputChatBox("An unexpected error has occurred and the log in GUI has not been created.")
        end
-- enable the players cursor (so they can select and click on the components)
        showCursor(true)
-- set the input focus onto the GUI, allowing players (for example) to press 'T' without the chatbox opening
        guiSetInputEnabled(true)
end
)
</syntaxhighlight>
Note that we have a simple security check before making the window visible, so in the unlikely event that the window has not been created, meaning wdwLogin is not a valid element, we don't get an error and just inform the player what has happened.
In the next step, we will create the button functionality for the log in button.
==给按钮写一个功能==
既然我们已经创建了我们的GUI并且展示给了玩家看,我们需要让它运行。
===检测点击===
When the player clicks on any part of the GUI, the event "[[onClientGUIClick]]" 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.
For example, we can attach the event to the btnLogin button to catch any clicks on it:
<syntaxhighlight lang="lua">
-- attach the event onClientGUIClick to btnLogin and set it to trigger the 'clientSubmitLogin' function
addEventHandler("onClientGUIClick", btnLogin, clientSubmitLogin, false)
</syntaxhighlight>
'''Note the final argument passed is "false". 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 "true" while attaching to gui elements will mean that clicking on any element in the same branch will trigger this event.'''
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:
'''Note that we are now writing more code for our existing 'createLoginWindow' function.'''
<syntaxhighlight lang="lua">
function createLoginWindow()
-- create all our GUI elements
...
-- now add our onClientGUIClick event to the button we just created
addEventHandler("onClientGUIClick", btnLogin, clientSubmitLogin, false)
</syntaxhighlight>
===Managing the click===
Now that we can detect when the player clicks on the button, we need to write code to manage what happens when they do.
In our [[onClientGUIClick]] event handle, we told it to call the function clientSubmitLogin whenever btnLogin is clicked.
Therefore, we can now use the function clientSubmitLogin to control what happens when the button is clicked:
<syntaxhighlight lang="lua">
-- create the function and define the 'button' and 'state' parameters
-- (these are passed automatically by onClientGUIClick)
function clientSubmitLogin(button,state)
-- if our login button was clicked with the left mouse button, and the state of the mouse button is up
if button == "left" and state == "up" then
-- move the input focus back onto the game (allowing players to move around, open the chatbox, etc)
guiSetInputEnabled(false)
-- hide the window and all the components
guiSetVisible(wdwLogin, false)
-- hide the mouse cursor
showCursor(false)
end
end
</syntaxhighlight>
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.
===触发服务器事件===
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]].
Here, we use the [[triggerServerEvent]] function to call our own custom event on the server, named "submitLogin", which will then control the spawning of the player serverside.
'''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.'''
<syntaxhighlight lang="lua">
function clientSubmitLogin(button,state)
if button == "left" and state == "up" then
-- get the text entered in the 'username' field
local username = guiGetText(edtUser)
-- get the text entered in the 'password' field
local password = guiGetText(edtPass)
-- if the username and password both exist
if username and password then
-- trigger the server event 'submitLogin' and pass the username and password to it
triggerServerEvent("submitLogin", getRootElement(), username, password)
-- hide the gui, hide the cursor and return control to the player
guiSetInputEnabled(false)
guiSetVisible(wdwLogin, false)
showCursor(false)
else
-- otherwise, output a message to the player, do not trigger the server
-- and do not hide the gui
outputChatBox("Please enter a username and password.")
end
end
end
</syntaxhighlight>
===Creating the serverside event===
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.
On the server side, recall that we are spawning the player as soon as they login.
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]].
<syntaxhighlight lang="lua">
-- create our loginHandler function, with username and password parameters (passed from the client gui)
function loginHandler(username,password)
end
-- define our custom event, and allow it to be triggered from the client ('true')
addEvent("submitLogin",true)
-- add an event handler so that when submitLogin is triggered, the function loginHandler is called
addEventHandler("submitLogin",root,loginHandler)
</syntaxhighlight>
===Logging in===
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:
<syntaxhighlight lang="lua">
function loginHandler(username,password)
-- check that the username and password are correct
if username == "user" and password == "apple" then
-- the player has successfully logged in, so spawn them
if (client) then
spawnPlayer(client, 1959.55, -1714.46, 10)
fadeCamera(client, true)
                        setCameraTarget(client, client)
outputChatBox("Welcome to My Server.", client)
end
else
-- if the username or password are not correct, output a message to the player
outputChatBox("Invalid username and password. Please re-connect and try again.",client)
        end
end
addEvent("submitLogin",true)
addEventHandler("submitLogin",root,loginHandler)
</syntaxhighlight>
'''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.'''
Also note the use of the variable "client", it's an internal variable used by MTA to identify the player who triggered the event.
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:
<syntaxhighlight lang="xml">
<script src="client/gui.lua" type="client" />
</syntaxhighlight>
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.
For further help with GUI, see the [[:Category:GUI_Tutorials|GUI tutorials]].
[[Category:GUI_Tutorials]]
[[it:Introduzione_allo_scripting_della_GUI]]
[[ru:Introduction to Scripting the GUI]]
[[es:Introducción a la Programación de GUI]]

Revision as of 15:41, 31 March 2016