Jump to content

General scripting introduction


Ceeser

Recommended Posts

Hello scripters and the ones who want to become a scripter,
 

since a few days I've been frequently visiting this site and I often see problems that shouldnt even exist or some that can easily be fixed by knowing what you do.
So thats why I wanted to make this topic to give some general instructions about how to script
And by 'how to script' I don't mean that I'll teach you scripting, instead I would like to give you some tips and guidelines which you can follow to improve scripting, debugging, remembering your script.

 


1. Variables


Okay, lets start with some instructions to Lua:
Lua is a very beginners-friendly 'programming language'. In Lua itself there are only a few syntaxes you need to remember, it doesnt cry if you use or dont use brackets at some points etc.
The variables are type-undefined which is nice for an easy use, but also a point where you can lose track of what you are doing. For example:

 

local myVariable = 1234 -- I gave this variable the value 1234 now

-- some stuff in the script between

myVariable = "asdf" -- Now I overwrote 'myVariable' with a string

Lua is fine with that. You can do it. BUT it's very bad practice.
By doing this you can easily break your script if you lose track of your script somewhere and actually need the number instead of a string.

That's the point for my first tip:

1.1: Hungarian Notation

Hungarian Notation actually comes from other programming languages, but it's a nice way to declear your variables in Lua as well.
Using Hungarian Notation basically means: Add the variable type as a prefix to your variables name.
This will immediatly help you knowing what type your variable will / should contain. (And let you know that you made a mistake somewhere if there is a wrong type)

I personally use these prefixes for variables (Lua, MTA specific):

  • i = integer (any number value - ignoring float, double, unsigned etc since Lua doesnt care if your number is an integer (round value), float (comma value) or positive/negative
  • str = string (string means a combination of characters, a-z, A-Z, 0-9 - yes, numbers can also be displayed as characters in a string, and all sorts of special characters)
  • tbl = table (a list / array of variables)
  • u = userdata (players, vehicles, objects, timers, ... all MTA elements are a kind of userdata)
  • f = function (yes, you can also store a function in a variable for faster/easier to handle/more performant use.

Code examples:

local iPositionLeft = 500; -- i = The name describes that this variable is meant for numbers only and I will never put another type in it.
local strMessage = "Hello World"; -- This is a string (str)
local tblPositions = {x = 200, y = 333, z = 1234}; -- This is a table/array

 

Okay, now that we have the correct prefixes it's time for part 2:
1.2: Proper names for variables

Give all variables a proper name which you remember and from which you know what its meaning/what its purpose.
I know, programmers are lazy - but writing a few more characters will help you later on. Example:

 

-- The following variables are no joke. They actually exist in a script of a server-lobby.
local x,y = guiGetScreenSize() 
local l_sizeX,l_sizeY = x/9,x/4/4.5
local l_posX,l_posY = x/2-l_sizeX/2,y/70
local l_alpha = 0
local r_scrollX = 0
local r_px,r_py,r_size = x/2-x/10/2,y/2,x/10
local a_size = x/40
local a_posX,a_posY = x/2-a_size/2,y/2-a_size
local a_posY_d = 0
-- When you define these variables like this you may know what they mean.. but take a look into the script a few days later or even
-- weeks/months later.. have fun searching for every single variable. And if another scripter sees this script - rip.

-- Instead of doing the stuff as above, better do it like this:
SX, SY = guiGetScreenSize(); -- define the screen sizes properly as global instead of seperate for every client script.

local iLoginSizeX = SX / 2;
local iLoginSizeY = SY / 2;
local iLoginPosX = SX / 2 - iLoginSizeX / 2;
local iLoginPosY = SY / 2 - iLoginSizeY / 2;
...

-- Like this you immediatly know what each variable means.. but it takes alot of space to declear each variable. Please read below.

Also, as you see in the example above: I really do recommend to seperate words by an uppercase character.. what is more readable:

local iloginsizex = SX / 2;
local iLoginSizeX = SX / 2;

 


Now, since our variables are well defined and readable, lets move to the next step:
1.3: Constants and variables that belong together

Tables are a nice way to sort and organize your variables - especially when you have to deal with UI rendering.
Rendering an UI can be a pain in the ass or fun to make, depending on how you do it imo.
I personally always use a 'constants_c.Lua' file (_c describes that its defined as client file in meta and used on client side only).
Inside that file I create all variables that will be used often inside a table of the name it belongs to.
This will help you organize your script even more and saves alot of performance, because you dont need to calculate every number onClientRender anymore.
It can also help you remembering positions etc - so faster scripting for UI's.
Also, make a difference between "normal" variables and constants. Constants don't really exist in Lua, so you have to make your own rules.
Constant mean that they will never be overwritten by a script (exception: If you can drag/move a panel or whatever they can be recalculated).
Give a constant variable an full-uppercase name so that you immediatly see that it is a constant. (mainly for positioning variables)
Additionaly, store them in a table with the name it belongs to. (POS as a general positions table, sub-names as element specified names)
I personally only name the variable name itself with a lowercase character and without hungarian notation in a positions table. Example:
 

-- So instead of declearing these variables
local SX, SY = guiGetScreenSize();
local iLoginPosX = SX / 2;
....

-- Do this: Create a constants_c.Lua file and define all variables there as global.
SX, SY = guiGetScreenSize(); -- Define the screen size X and Y once for the whole resource

POS = {}; -- Global positions table
POS.LOGIN = {}; -- Login specific positions table
POS.LOGIN.sizeX = SX / 2; -- sizeX of the login
POS.LOGIN.sizeY = SY / 2;
POS.LOGIN.posX = SX / 2 - POS.LOGIN.sizeX / 2;
POS.LOGIN.posY = SY / 2 - POS.LOGIN.sizeY / 2;
-- If we now render a rectangle 'dxDrawRectangle(POS.LOGIN.posX, POS.LOGIN.posY, POS.LOGIN.sizeX, POS.LOGIN.sizeY, white)'
-- The 'login window rectangle' will be perfectly centered

-- For other UI 'elements' continue like this:
POS.SCOREBOARD = {};
...

-- Yes I know, usually you dont have a login panel and scoreboard in the same resource.. this is just an example.

 

1.4: Globals and performance

Basically every variable you didn't define as 'local' is a global variable, but global variables are accessable from any script of the resource (ofc split to client and server side).
This will cost some performance - so try to define everything as local if you can, you can even define functions as a 'local function XY()'
Even (global) math functions like math.sin can be defined as 'local sin = math.sin' to save some performance if you use it often (onClientRender + for loop as example)

If you want to make a (non-constant) variable globally accessable (so for multiple scripts inside the resource) always mark it as a global by putting g_ infront of the variables name.
This will help you notice its a global, non-constant variable and prevent unwanted overwriting / name-duplications.

 

2. Debugging

Debugging is a very important part of programming, especially if you are new to scripting / the type of script you are doing.
(PS: Dont be afraid of making bugs.. programming is 50% creating bugs and 50% fixing bugs until you get what you want^^)
MTA offers several ways of debugging:

  • outputChatBox - The mose obvious one, but it will/can spam the chat, errors in onClientRender using this debug method can also cause lag and it gives only the information you enter - useless for most debugs. - Only use this for test-wise debugging, alone.
  • outputDebugString - This is the one you should take for debugging and information outputs, you can also specify errorType and color.
  • iprint - A bit lazy, huh? Joke. This one has its 'i' for a reason: Its itelligent. It's displaying various datatypes without requiring tostring - it can even display table structures
  • More detailed information on screen? Use dxDrawText and display the information you need where you want it to be.

 

3. Help

For any problem you have there is a solution - thats for sure. But if you need help, the best practice is not to make a big shout-out for help on any mta-scripting-help-forum, instead:
Please try to fix the problem yourself - make several tests / experiment around, because by finding out what you have done wrong you will learn the most.
If you immediatly ask for help and get a finished solution you basically learned nothing about it - always remember that: Testing will help yourself improve.

If you cant find a solution: MTA Wiki is your friend. Its well documented and give you any information you need about any implemented function.
Maybe search for the function you have problems with or utility (aka 'useful') functions that users have created.
In many cases you can find a solution by searching in the wiki.

In the case you really cant find any solution there are some free sites to ask like this mta-sa scripting forum or the ffs-gaming scripting forum.

I bet you will find somebody who can help you with any problem there if you cant find any solution.

 

4. Script organizing

Organizing each script, documentation and comments can help you alot.
When writing code,  simply add a comment ('-- text') behind some importent lines while the comment should explain what it does / for what reason as short but understandable as possible.
Seperate your code in several sections so that its easier to find functions that are somehow connected (if you have multiple render functions, put them all in a '-- Render' section for example)
Also organize your script by adding a (lets call it:) 'namespace' to each script - so each script get its own table (defines as local or global as you need) - THIS IS NOT OOP! Example:

-- ************************************************************************************************************************** --
-- VARIABLES, EVENTS AND INFO --

local Login = {}; -- Define the scripts "namespace", this is now local - so for this script only, not for the resource.
Login.show = true;
Login.accEdit = guiCreateEdit(0, 0, 0, 0, "");
Login.passEdit = guiCreateEdit(0, 0, 0, 0, "");
Login.selectedEdit = Login.accEdit; -- The selected edit (focused) to enter characters in

-- ************************************************************************************************************************** --
-- RENDERING --

function Login.render() -- Yes, this function is basically a variable of "Login".
    if (Login.show) then 
        -- Render login window
    end
end

-- ************************************************************************************************************************** --
-- CLICKING --

function Login.click(strButton, strState)
    if (strState == "down") then
        -- Handle clicking of login window
    end
end

-- ************************************************************************************************************************** --
-- START AND STOP --

function Login.start()
    addEventHandler("onClientClick", root, Login.click);
    addEventHandler("onClientRender", root, Login.render);
end
addEventHandler("onClientResourceStart", resourceRoot, Login.start);

function Login.stop()
    removeEventHandler("onClientClick", root, Login.click);
    removeEventHandler("onClientRender", root, Login.render);
end

 

5. Stick to one language

Yes i know, this can sometimes be hard, but its nevertheless way better than a mix of different languages.
English is ofc the language you should chose if you can, because its the most spoken/known language in the world.
This can not only improve your english skills, it will also help others alot if they see your script for help/revision/enhancement.



Thats it for now, this topic might be continued.
If you have questions, simply write a comment, maybe I'll add the answer to this topic.

Edited by Ceeser
Link to comment
  • 1 month later...
  • 1 month later...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...