-
Notifications
You must be signed in to change notification settings - Fork 7
Lua Coding Docs: Basics of Coding
Note: You can skip this section if you want just to let ya' know.
Lua is a lightweight, high-level, dynamically typed, multi-paradigm scripting language that is mainly used in Psych Engine. This allows you to execute Lua code without compiling the game over and over again. And it's simple and easy scripting language that an 8 year old can understand easily. The only cons that Lua has are, global scopes are always in default, limited error handling, limited pattern matching, no unicode support, no default parameter values just to name a few.
You don't know they're basically a text editor program designed specifically for editing source code of computer programs. They usually add such as syntax highlighting, indentation, autocomplete and brace matching. To make your programming experience more easier to code, the most popular source-code editors are Visual Studio Code, Notepad++, Vim, Sublime Text, etc. You can use any source-code editors that you're comfortable to use.
When coding with Lua in Psych Engine, there are some minor differences. This is due to HaxeFlixel, the main engine used by Psych Engine. Anyways uuuuhhhhh here's a list of minor differences that you should 100% totally know about:
- The code should be located inside any of the Callback Templates which are special functions. That uses the games mechanics like note types, custom events, countdowns, etc to execute the code; Examples:
onCreate()
,onUpdate()
,onEvent()
just to name a few of them. Note that variables, functions, and built-in Lua functions can be declared outside of any Callback Templates. - Printing is also different instead of using the
print()
function it's replaced by thedebugPrint()
function. The arguments passed on thedebugPrint()
function will appear at the top-left of the screen. And will fade out in a couple of seconds. - Psych Engine uses Lua version 5.1 which there are missing features that Lua 5.1 doesn't has; Examples: Floor division, Bitwise operators, basic utf-8 library, support for both 64-bit and 32-bit platforms, and new functions.
Example:
local vary1 = 'hamburgers are cool'
local vary2 = 'potatoes are also cool, i think'
function onCreate()
debugPrint(vary1) -- will print > 'hamburgers are cool'
debugPrint(vary2) -- will print > 'potatoes are also cool, i think'
end
Comments are used to explain the context of code and prevent the execution on a specific code; Lua will just ignore them. Comments starts with a double minus -- characters as the syntax for single-line comment. For multi-line comments should have double brackets [[]] characters with the double minus -- characters before it.
Example:
function onCreate() -- Triggered at the start of the Lua script
makeLuaSprite('tag', 'imagePath', 0, 0) -- initializes the lua sprite
addLuaSprite('tag', true) -- Adds the lua sprite
end
--[[
function onCreatePost()
setProperty('iconP1.visible', false)
setProperty('iconP2.visible', false)
end
]]
You can also nest multi-line comments just add an equal = character between the double brackets [[]] characters of the comment; Example: --[=[]=]. The amount of equal = character determines how many levels of nesting will be created.
Example:
--[=[
Here's a multi-line comment with the equal
sign to prevent the double bracket strings []
inside the comment to break
debugPrint([[ hi ]])
]=]
Variables are an abstract manipulable containers for storing data values they can be used throughout the Lua program. They're paired with an associated name which contains the data value of the variable to be used. The data from the variable can be updated when you assigned a new value from it.
To declare the variable, you must specify the variable's type
, name
, and data
value. The type
determines what scope the variable should have. It could be global
type by default in Lua or local
type by using the local
keyword. Followed by the name
of your variable name it what-ever you want it, but there are some rules to follow when declaring them. The variable's specified data
value is then assigned using an equal = character.
Example:
local foo = 73
local bar = 29
local baz = foo - bar
function onCreate()
debugPrint(baz) -- will print > 44
end
You can also assign multiple variables in one line, if you want to reduce the lines of code for some reason. Each variable's names
and values
should be separated by a comma , character. But if the names list is not equal to the values list then the other values will be asign into nil
value. If the opposite happens then the other extra values
will be ignore, making them impossible to call them.
Example:
local foo, bar, baz = 134, 72, 722 -- 'name and value lists' are equal
local plu, clu, zur = 'Hello', 'Bye' -- 'value list' is greater than 'name list'
local qux, quy = true, true, false -- 'name list' is greater than 'value list'
function onCreate()
debugPrint(foo, bar, baz) -- will print > 134, 72, 722
debugPrint(plu, clu, zur) -- will print > 'Hello', 'Bye', nil
debugPrint(qux, quy) -- will print > true, true
end
- Variable names can only have alphanumeric
Ab12
and underscore _ characters. - Variable names cannot have numbers at the start of the name.
- Variable names are case-sensitive so variable a and A are completely different to each-other.
- Variable names are recommended to have descriptive name like (
health
,misses
,alpha
) to make the code more readable. - Variable names cannot be named after Lua keywords.
Example: (Valid)
varname = 'Hi' -- a variable (lower case)
varName = 'Hi' -- a variable with a capitalize letter (camel case)
var_name = 'Hi' -- a variable with an underscore '_' character (snake case)
_var_name = 'Hi' -- a variable with an underscore '_' character at the start
VARNAME = 'Hi' -- a variable that is all capitalize letters (upper case)
varname2 = 'Hi' -- a variable with a number
_______ = 'Hi' -- what the hell is this variable???
Example: (Invalid)
1varName = 'Error' -- a variable with a number at the start
var-name = 'Error' -- a variable with a minus '-' character (kebab case)
var name = 'Error' -- a variable with a space ' ' character
var$name = 'Error' -- a variable with a special '$' character
Global variables allows you to call them outside a Callback Templates or block, at any time throughout the program. But it's only recommended if you're getting a variable on another Callback Templates or block for data checking or something.
Example:
function onCreate()
globalTextTag = 'myText' -- global variable
makeLuaText(globalTextTag, 'This is a dumb text!', nil, 0, 0) -- initialize the text
setTextSize(globalTextTag, 30) -- sets the text size
addLuaText(globalTextTag) -- adds the text in the game
end
function onBeatHit() -- checks every beat hit
if curBeat == 5 then -- checks if the 'curBeat' is equal to '5'
setProperty(globalTextTag..'.alpha', 0.5) -- sets the text opacity into '0.5'
end
end
Local variables can only be called within a function or block; if you attempted to call them outside it will return a nil
value because there only local within that function block. They're more faster to call because they're integrated into the environment in which they are created.
Example:
function onCreatePost()
local localOpacity = 0
setProperty('camHUD.alpha', localOpacity) -- sets the camera HUD elements opacity into '0'
end
function onSongStart()
debugPrint(localOpacity) -- will print > nil
doTweenAlpha('HUDTween', 'camHUD', 1, 3, 'linear') -- will tween the camera HUD elements to '1'
end
Strings are a sequence of characters it can either represent an alphabetical, digital, punctuation, or all of them. Their main purpose is to store human-readable text, like words and sentences. They're commonly surrounded by single-quotes '', double-quotes "", or double-brackets [[]] for multi-line strings.
Example:
local textString1 = 'Hello' -- a single quote
local textString2 = "World" -- a double quote, this is optional to use
local textString3 = [[
Culturae organicae te capiet ad loca quae numquam
expectata non visere!
]] -- a double bracket, used this for longer texts
function onCreate()
debugPrint(textString1) -- will print 'Hello'
debugPrint(textString2) -- will print "World"
debugPrint(textString3) --[=[ will print [[
Culturae organicae te capiet ad loca quae numquam
expectata non visere!
]]]=]
end
Escape characters are special characters used within a string. They are an alternate interpretation of the characters in the following character sequence. It can be interpreted as a single '', double "" quotes, or backslash \ character, they are constructed by backslash \ character with the specified character to use after that; Example: \'. And there is a reason for their existence, it's to avoid an error from occurring.
So, lets assume you constructed a string that is surrounded by double-quote "" characters and you want to add a double-quote inside the string, so you inserted the double-quote "" and it resulted an error. Because Lua thinks the string would finish there due to the inserted double-quote "" character, it created an error. As a result, escape characters exist to solve this issue.
Example:
function onCreate()
local textString1 = 'i can smell the "bitch" on you!!'
local textString2 = "that's racist i feel like"
debugPrint(textString1) -- will print 'i can smell the "bitch" on you!!'
debugPrint(textString2) -- will print 'that's racist i feel like'
local textString3 = 'Don\'t press \'Alt + F4\'' -- single quote
local textString4 = "dead \"(in a cool way)\"" -- double quote
local textString5 = 'C:\\Windows\\System32' -- backslash
debugPrint(textString3) -- will print 'Don't press 'Alt + F4''
debugPrint(textString4) -- will print "dead "(in a cool way)""
debugPrint(textString5) -- will print 'C:\Windows\System32'
end
-
\'
- Single-quote Character -
\"
- Double-quote Character -
\\
- Backslash Character -
\n
- New Line -
\r
- Carriage Return -
\t
- Horizontal Tab -
\v
- Vertical tab
Numbers are arithmetic values that represent the quantity or amount of something. It can have positive or negative values, and numbers can be expressed as Float or Int; Float numbers support decimal numbers, whilst Int numbers only uses whole numbers.
Example:
local numInt = 81 -- integer number
local numFlt = 23.42 -- floating-point number
local numAltFlt1 = 12.
local numAltFlt2 = .56
function onCreate()
debugPrint(numInt, numFlt) -- will print > 81, 23.42
debugPrint(numAltFlt1, numAltFlt2) -- will print > 12.0, 0.56
end
Numbers can also be represented in Scientific (Exponent) Notation. They are a way to expressing numbers that are too large or too small to be conveniently written in float form. You could also represent it as a Hexadecimal number system for colors, just to let you know.
Example:
function onCreate()
debugPrint(263e+4) -- will print > 2630000.0
debugPrint(326e-5) -- will print > 0.00326
debugPrint(0xFF0000) -- will print > 16711680
end
Booleans, often shortened to Bools, are data types that can have two possible values: true
or false
. This is commonly used for conditional statements, which allow for different actions by modifying control flow based on whether the condition is true
or false
.
Nil represents nothingness or non-existence of a value. This can be used for destroying a variable or table values if not used anymore. Or use conditional statements to check if the value is a nil
or not.
Tables are a data structuring mechanism in Lua the only one in fact. They can store multiple types values like strings, numbers, booleans, even its self. This is a better alternative than listing them in each variables and makes it easy to access each element of the Table. And for making modules, metatables, and object orientation stuff which is useful in some-cases; Tables can be represented as an Array or Dictionary.
Array are the most common way to declare a Table, they're are always listed in ordered. To declare a Table Array add a pair of curly-braces {} characters with the elements each separated by a comma , character.
To read a Table Array element get the name of it followed by a pair of bracket [] characters. With the referring to the index number inside the pair of bracket [] characters for specified element to get.
Warning: Lua uses 1-based index rather than 0-based index like other programming languages. In other words, the first index position always start at 1.
Example:
local tableGroup1 = {'string', true, nil} -- a table with string, boolean, and nil values
local tableGroup2 = {{45, 13}, {34, 76}} -- a table with nested tables
function onCreate()
debugPrint(tableGroup1) -- will print > ['string', true, nil]
debugPrint(tableGroup1[1]) -- will print > 'string'
debugPrint(tableGroup2[1][2]) -- will print > 13
end
Dictionaries uses key-value pairs for storing elements instead of index-value pairs that Table Arrays uses. It basically uses names for referring the elements inside the Table Dictionary. The key from the Table Dictionary can be surrounded by bracket [] characters with single-quoted '' or double-quoted "". This is only used if the key name has a special character inside of it.
To read a Table Dictionary get the name of it followed by a dot . character with the name of the key to be used. Or used the bracket method as an alternative or if it has a special key name.
Example:
local tableDict1 = {isFatherless = false, hasTouchGrass = true}
local tableDict2 = {['air'] = 'Oxygen', ["food"] = 'potato', ["love"] = 'carbon monoxide'}
function onCreate()
debugPrint(tableDict1) -- will print > {isFatherless => false, hasTouchGrass => true}
debugPrint(tableDict1.isFatherless) -- will print > false
debugPrint(tableDict1['hasTouchGrass']) -- will print > true
debugPrint(tableDict1.air) -- will print > 'Oxygen'
debugPrint(tableDict2['food']) -- will print > 'potato'
debugPrint(tableDict2["love"]) -- will print > 'carbon monoxide'
end
Tables can insert elements in any given position of the table. You can do this by calling the table with the referring to the index number or key name. followed by the element that is then assigned using an equal = character. Or alternative you can use the table.insert()
function to forcefully insert an element from the table without changing the element's value.
Example:
local tableGroup1 = {'luz', 'erm', 'duq'}
local tableGroup2 = {foo = 93, bar = 23, baz = 74}
function onCreate()
tableGroup1[4] = 'rod'
tableGroup1[5] = 'xur'
tableGroup2['muz'] = 91
tableGroup2['quz'] = 32
debugPrint(tableGroup1) -- will print > {foo => 93, bar => 23, baz => 74, muz => 91, quz => 32}
debugPrint(tableGroup2) -- will print > ['luz', 'erm', 'duq', 'rod', 'xur']
end
Functions are a collection of code that is designed perform a specific task. They can enable reusable code across the Lua program and reduces the duplication of code. functions are defined with the function
keyword followed by the name of the function with the pair of parentheses () characters.
To call a function get the function name followed by the parentheses () characters. If the parentheses are not present it will return the function's memory address; Example: function: 0x5616d89c0770
.
Example:
function hello()
debugPrint('Hello Function')
end
function onCreate()
hello() -- will print > 'Hello Function'
end
Parameters are special type of variables inside the functions parentheses () characters. If there are two or more parameters they must be separated by a comma , character. Their main purpose is to add more functionality to the function. If you're calling a function you must values or "arguments" which in each parameters, this is a requirement.
Example:
function setPos(obj, pos) -- concatenates setProperty x and y
if pos[1] ~= nil then -- makes pos parameter acts like a table
setProperty(obj..'.x', pos[1])
end
if pos[2] ~= nil then
setProperty(obj..'.y', pos[2])
end
end
function onCreatePost()
setPos('boyfriend', {100, 500}) -- Changes the position to x = 100 and y = 500
end
Parameters can be defined as an infinite parameter with the ellipsis ... character. It enables an indefinite number of arguments to pass. If there are non parameters present, it must be the last parameters from the list.
Example:
function table.pack(...)
return {...}
end
function onCreate()
debugPrint(table.pack(34, 23, 12, 343)[1])
debugPrint(table.pack(true, false, true, true)[4])
end
Operators are unique symbols that are used to carry out operations on operands. For the conditional statements to use to determine if the value is true
or false
before executing the code block. They can be represented as Arithmetic, Relational, Logical, and Miscellaneous operators.
Arithmetic operators are mathematical operators used to perform calculations for numeric values.
Operators | Name | Example | Returns |
---|---|---|---|
+ |
Addition | 5 + 5 |
10 |
- |
Subtraction | 8 - 3 |
5 |
* |
Multiplication | 5 * 3 |
15 |
/ |
Division | 9 / 2 |
4.5 |
% |
Modulus | 8 % 4 |
0 |
^ |
Exponentiation | 2^4 |
16 |
- |
Unary Negation | -8 |
-8 |
Relational operators are used to compare multiple operands inside a condition in order for the code block to execute.
Operators | Description | Example | Returns |
---|---|---|---|
== |
Checks if the condition is equal to the right. | a == b |
false |
~= |
Checks if the condition is not equal to the right. | a ~= b |
true |
> |
Checks if the condition is greater than the right. | 4 > 5 |
false |
< |
Checks if the condition is lesser than the right. | 4 < 5 |
true |
>= |
Checks if the condition is greater or equal to the right. | 7 >= 7 |
true |
<= |
Checks if the condition is lesser or equal to the right. | 2 <= 5 |
true |
Logical operators are used to combine multiple conditions and to specify on what conditions needs to be true
.
Operators | Description | Example | Returns |
---|---|---|---|
and |
Returns true if both statements are true ;Combines multiple conditions together. |
a == false and b == true |
false |
or |
Returns true if one of the statements are true ;Combines multiple conditions together. |
a == false or b == true |
true |
not |
Reverses the condition; If the condition is false it will return true and vice versa. |
not false |
true |
Miscellaneous operators only features two operators the Length and Concatenate operators.
Operators | Description | Example |
---|---|---|
# |
Length operator, Checks the maximum length size of a string or table . |
#'sussy' |
.. |
Concatenate operator, Merges multiple string or numbers together. |
'snow'..'ball' |
Control Statements allows you to control the execution of other statements. Which analyzes the statements condition and decide whether to execute the code if it's true
or not.
These are a type of control structures that specifies whether or not to execute the block code. They are the most common control structures to use. There are only 3 if else statements the if
, else
, elseif
statements.
Conditional Statements are a type of Control Statements that whether or not to execute the block code return true
. They are the most common control structures to use; Lua only has 3 Conditional Statements which are If
, Else
, and ElseIf
Statements.
If Statement checks the condition if it's true
or not. They are define with the if
keyword followed by the specified condition to execute the statement with the then
keyword.
Example:
local getWeekDay = os.date('*t').wday -- checks the current day of week
function onCreate()
if getWeekDay == 6 then -- checks if the current day is 'Friday'
debugPrint('Day: Friday')
end
end
Else statement checks if the condition fails then this statement will be executed. They are define with the else
keyword at the bottom of the If or ElseIf Statement that's it nothing else to say about it.
Example:
local getWeekDay = os.date('*t').wday -- checks the current day of week
function onCreate()
if getWeekDay == 6 then -- checks if the current day is 'Friday'
debugPrint('Day: Friday')
else -- if the condition returns 'false'
debugPrint('Not Friday')
end
end
Elseif Statement is an alternative condition if the other condition returns false
. They are define with the elseif
keyword with the new condition to execute a new condition followed by the then
keyword.
Example:
local getDateWeekDay = os.date('*t').wday -- checks the current day of week
function onCreate()
if getDateWeekDay == 7 then -- checks if the day is 'Saturday'
debugPrint('Day: Saturday')
elseif getDateWeekDay == 1 then -- checks if the day is 'Sunday'
debugPrint('Day: Sunday')
else -- checks if both of the condition fails
debugPrint('Day: Weekdays')
end
end
For loop statement allows you to loop a specific number of times. This loop is commonly used for setPropertyFromGroup()
and getPropertyFromGroup()
functions for note modification, modcharts, or something. And used for reading a table values or performing on numeric values. There are 2 types of loops Generic loop or Numeric Loop.
Numeric Loop are a type of loop that use numeric values to increment or to decrement a value. This loop is usually the most common loop to use for setPropertyFromGroup()
and getPropertyFromGroup()
functions. There are 3 expressions when declaring a Numeric loop initialize
, condition
, and iteration
.
-
initialize
- The initial variable for the loop to use. -
condition
- The condition of the loop to execute the code block. -
iteration
- An optional expression, The iteration of the loop you can either increment + or decrement - the value; Default value:incremented
.
Example:
function onCreate()
for index = 0, 5, 1 do -- Increment loop
debugPrint(index) -- will print > '0, 1, 2, 3, 4, 5'
end
for index = 5, 0, -1 do -- Decrement loop
debugPrint(index) -- will print > '5, 4, 3, 2, 1, 0'
end
for index = 0, 300, 50 do -- Increment Loop with each value incremented by 50
debugPrint(index) -- will print > '0, 50, 100, 150, 200, 250, 300'
end
end
Generic Loop are a type of loop that commonly uses pair functions to read all the table values. This is just an alternative loop for reading every table values. There are 3 expressions when declaring a Generic loop key
, value
, and iteration
.
-
key
- The key values from the table, you can name how you want; Example:key
. -
value
- The value types from the table, you can name how you want; Example:value
. -
iterate
- The iteration for the loop to use, can be either thepair
functions orgmatch
function.
Example:
function onCreate()
local tableThingy = {num1 = 231, num2 = 345, num3 = 234}
for key, value in pairs(tableThingy) do
debugPrint(key, value) -- will print > 'num1 = 231, num2 = 345, num3 = 234'
end
end
-
pairs()
- Returns every key-value pairs inside a table for table dictionaries; Returns as an unorganized table sort; Not to be confused withipairs()
functions. -
ipairs()
- Returns every index-value pairs inside a table for table arrays. If the table value hasnil
it will stop executing the loop there.
While Loop statement will loop through a block of code infinitely until the specified condition returns false
. To declare a while loop, Make an if
statement but just replace the if
keyword with the while
keyword and the then
keyword with the do
keyword.
Warning: Check the condition when making a
while
loop statement. Because it might loop infinitely and crash your game! I'd suggest you test it here before implementing into the Lua script.
Example:
function onCreate()
local counter = 5
local factorial = 1
while counter > 0 do
factorial = factorial * counter
counter = counter - 1
end
debugPrint(factorial) -- will print > '120'
end
Return statement as the name suggests returns the results from the function. And stops the execution of the function. It must be relative at the end of the function or conditional statement.
Example:
function getMidpointNum(ope1, ope2)
return (ope1 + ope2) / 2 -- returns the middle value between 'ope1' and 'ope2'
end
function onCreate()
debugPrint(getMidpointNum(9 + 10)) -- will print > '9.5'
end
Break statement stops the loop statements from looping. You can use this for specific conditions for the loop to end. It must be relative at the end of the function or conditional statement.
Example:
function onCreate()
local nums = {1, 3, 9, 5, 7, 4, 19, 13}
for i = 1, #nums do
if i > #nums / 2 then
break
else
debugPrint(nums[i])
end
end
end
Modules are a code library these mostly contain functions or variables. They can help you maintain a code-base and break your code into different Lua files. If you're using them frequently when coding your weird Lua scripts.
Instead of using import
or export
statements, Lua uses a Table "Dictionary" which can consist variable or functions that you frequently and, thus creating a your very own simple module. With the return
statement at the last line of code to export the module to other files to use.
Module Location (Path Visualizer):
mods/
├╴ scripts/
│ ├╴ main.lua
│ └╴ modules/
│ └╴ global.lua
Example:
local modules = {}
modules.red = 'ff0000'
modules.green = '00ff00'
modules.blue = '0000ff'
function myGamingModules.tobool(boo) -- boolean to string
local boo = boo:lower()
if boo:match('true') or boo:match('false') then -- check if it's actually true of false
return boo == 'true' and (true or false) -- ternary operator > "cond ? exp1 : exp2"
end
return 'Not a boolean value'
end
function modules.setPos(obj, pos) -- Concatenates setProperty x and y
if pos[1] ~= nil then -- makes pos parameter acts like a table
setProperty(obj..'.x', pos[1])
end
if pos[2] ~= nil then
setProperty(obj..'.y', pos[2])
end
end
return modules
You want to require or import the desired module file you created, use the require()
function. And will controls whether a file has already been run to avoid duplicating the work. The arguments should have the location of the Lua module file to be used; Starts outside the mods
folder.
Example:
local moduleName = require('mods/scripts/modules/global') -- gets the module
function onCreate()
debugPrint(type(moduleName.tobool('true'))) -- will print 'boolean'
debugPrint(moduleName.red) -- will print 'ff0000'
debugPrint(moduleName.green) -- will print '00ff00'
debugPrint(moduleName.blue) -- will print '0000ff'
end
The Global Variable _G
is special type of table dictionary that holds the global environment. This allows you to insert variables and functions across all of your Lua scripts.
But unfortunately doesn't work why? idk, there are alternatives to Global Variable _G
such as setVar()
and setOnLuas()
functions. The only thing its used for getting multiple global variables from a loop and modify the values easily.
Example:
function onCreate()
myGlobalVar0, myGlobalVar1 = 183, 231
myGlobalVar2, myGlobalVar3 = 963, 263
for nummys = 0, 3 do
debugPrint(_G['myGlobalVar' .. nummys]) -- will print > '183, 231, 963, 263'
debugPrint(_G['defaultPlayerStrumX' .. nummys]) -- will print > '732, 844, 956, 1068'
end
end
Converts any number
or boolean
values into real string
values.
-
num
- The value to be converted
Converts any string
values into real number
values.
-
num
- The value to be converted
Checks the current type of the value; Can either return: string
, number
, boolean
, nil
, table
, function
.
-
data
- The value to be checked.
Loads a Lua code from the argument and converts into real Lua code.
Example:
local ope = load('return 4 + 3')()
function onCreate()
debugPrint(ope) -- will print > 7
end
Is the page in some way inaccurate? an error, a typo, or outdated data? To report it, use the "Issue Tab". Or do you wish to include a new function or add new information? use the "Pull Request Tab". Help is always appreciated!
- Event Callbacks
- Custom Sprite
- Custom Text
- Object Functions
- General Functions
- Scripting & File Functions
- Game Input Control Functions
- Language Translation
- HScript Functions
- Custom Substates
- Custom Shaders
- Deprecated & Removed Functions
- Sound & Music Functions
- Tweens & Timers Functions
- Reflection Functions
- Variables