New to scripting? The guide Scripting for Beginners on visionaire-studio.net shows with a small example when Lua is worth it and how to write your first scripts.

Overview

A'llo & welcome to AFRLme's guide to Lua script; for code dummies & monkeys alike. Here, I will be explaining the general basic fundamentals of Lua script, along with examples & images. If you are wanting to learn how to use Lua script in combination with Visionaire Studio, then you need to check out scripting, data structure, player commands, common commands, script index, & also the official Visionaire Studio luadocs page.

Before we begin, if you have not already done so, then have a look at the notes on trying out the examples below, or skip to the index list & have fun learning.

Index

# Contents Description
0 Index This page...
1 Introduction Quick overview of Lua script, with some additional notes.
2 Basics The basic fundamentals of Lua script.
3 Operators Lua operators are expressions used to perform calculations or to pass arguments between different value types.
4 Types Lua types can range from numbers, to strings, to tables, to conditions & even functions.
5 Tables Tables are one of the most fundamental features of Lua script.
6 Iteration and loopsLoops repeat code for every entry of a table or as long as a condition is true.
Functions Functions are used to process & return data. They are also great for reducing workload & increasing workflow.

Trying out the examples

You can try every example of this guide without extra software: create a script in the script section of Visionaire Studio, paste the code and start the game. Messages from print appear in the log file and in the console of the player (open it with the key Tab while the game is running in test mode).

If you prefer to try Lua outside of Visionaire, the online demo on lua.org runs plain Lua in the browser. Code editors such as Visual Studio Code with a Lua extension also help with syntax highlighting and error hints. Keep in mind that Visionaire specific commands like getObject only work inside the engine.

Introduction

What is Lua script

Lua Script is a lightweight, cross-platform scripting language that was originally created as an extension - of sorts - to further extend upon already existing programming languages; but over time it has grown in popularity due to how easy it is to learn, & the amount of time saved in creating simple procedural scripts, functions & tables, which is why Lua script is now the primary or secondary scripting language used by multiple game engines.

For more information please see: here & here.

What is the meaning of LUA

Actually it's not LUA at all. Lua is not an abbreviation or acronym of any kind, it is simply the Portuguese word for moon.

What languages is Lua similar to

Lua, is similar to programming languages such as C, C#, C++, & Java to name a few, although Lua is relatively basic in comparison because we don't have to declare the variable type each time we create a new variable.

Why learn Lua

Why would you want to learn Lua script? I mean honestly why would you? Visionaire Studio already comes with enough pre-existing actions & queries, that you could technically (almost) make a finished game of commercial caliber; the caliber part being entirely up to you &/or your team, but still...

My answer is pretty simple. Why not? Lua can be used to further enhance the pre-existing actions & queries, or create entirely new functions & features that do not come standard with Visionaire Studio by default.

Lua is fairly easy to learn, has a short learning curve, & anyone with any previous programming experience - I'm even including basic html in this category - shouldn't have much trouble getting to grips with the basic fundamentals. Plus you will need to use it, if you want to allow players to create screenshots, have your game remember the players options configuration each time the game is launched, or go beyond the limitations of the Visionaire Studio: Adventure Game Engine, in general.

In the very least, I recommend trying to learn the basics, as from there you could potentially apply what you have learned into other scripting & programming languages; & who knows... you might even have an aptitude for learning code & be able to learn more advanced programming languages like c# or c++, thus allowing you to make games with more powerful engines like Unity, UDK (unreal engine) or CRYENGINE; or potentially bang out the next biggest metroidvania hit since sliced bread with Game Maker Studio.


Basics

Here I am covering a few of the basic fundamentals of Lua script that I can't really create individual pages for. You will probably see most of these - in one context, or another - throughout the various pages of this guide.

Print

The print function is used for printing messages to the log file or lua console & is useful for debugging your scripts & functions.

print("hello world") -- prints "hello world" into the log/console (ah yes how cliché of me, but it had to be done)

Variables

Variables are like keywords that you can use to store & access data. Variables in Lua script can be local or global in scope, & unlike most other programming languages, they do not require you to specify their type when creating them as variables can be overwritten at any time with new data.

a = 1

Concatenation

Concatenation is essentially the joining of two or more strings & is represented by two full stops, like so ..

a = "hello"
b = "world"

print(a .. " " .. b) -- prints "hello world" - we had to add " " to create a space between the 2 words

Return

Return allows us to return data from a script or function, which can then be used inside of another script or function.

function hello()
 return "hello world!"
end

print( hello() ) -- prints "hello world!"

Global vs Local

Global & local refers to anything from variables, to tables, to functions. The difference between global & local is that local only works inside of the script or function that it was created in, whereas global can be used throughout multiple scripts.

Global is often used for functions, tables & variables that need to be accessed by multiple scripts, or from various different locations.

local a = 1 -- only accessible in this script

a = 1 -- can be accessed from anywhere

function return_n(n) 
 local a = n -- only valid inside of this function but can be returned outside of function
 return a 
end
Quick note: you need to be careful when using global conventions, that you do not use the same instance in other scripts, as this would be considered cross-polluting & may break your script because you would be overwriting the existing data that the variable, or function contained.

Comments

Single line comments

Can only be placed on their own line or at the end of a line.

-- this is a single line comment
i = 10 -- comment placed after working code
-- comment place before code; i = 10 -- notice how the code is also commented out?

Multi-line comments

Can be placed anywhere in the script & allow you to write comments over multiple lines.

--[[
this is a multiple line comment
I can go on & on & on
& on & on
& on.
]]

local i = 2 * --[[ this is a comment ]] 2
print(i) -- would print 4 to the log (2x2)

Nested multi-line comments

Allow you to comment out entire blocks of code, even if they already contain multi-line comments, thus preventing existing multi-line comments from ending the nested comment.

--[=[
this is a nested comment
see how --[[ we can include ]]
a multi-line comment inside of it?
]=]

--[[
this is what would happen if we were to use
a regular multi-line --[[ comment ]]
i = 10
]]

Types

There are multiple lua types available, which are automatically determined by the returned value of the variable, function or table.

Types

boolean This returns a value of true or false.
function A function usually contains a list of actions to be executed & can often be used to calculate & return values.
nil If no data type is returned then whatever you queried is either empty or does not exist, thus it is nil.
number This usually contains an integer (whole) or floating point (decimal) number.
string This usually contains text or numbers wrapped in "quotation marks" or 'apostrophes'.
table This usually consists of an array, or multiple arrays of data & data types.

Boolean

prints true
local a = true

print( a, type(a) )

Function

prints 7
function a(i)
 return i
end

print( a(7), type(a) )

Nil

prints nil, nil
local a = nil

print( a, "  type = " .. type(a) )
print( b, "  type = " .. type(b) )

Number

prints 7, 7.25
local a = 7
local b = 7.25

print( a, type(a) )
print( b, type(b) )

String

prints hello world
local a = "hello world"

print( a, type(a) )

Table

prints 1, 2, 3, 4, 5, 6, 7, 8
local a = {1,1+1,1*3,2*2,20/4,"6",3.5*2,10-2}

print( a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], type(a) )

Operators

Lua operators are expressions used to perform calculations or to pass arguments between different value types.

Conditional Operators

if Query if something does or does not meet a certain condition.
else Do something else if the query condition was not met.
elseif Used to add additional if queries, if the initial query condition was not met.
end This is used to close various queries or functions; there must be the same amount of end as if; except in instances where elseif has been used.

if

if condition is true (method 1)
local a = true

if a then
  print("a = true")
end

if condition is true (method 2)
local a = true

if a == true then
  print("a = true")
end

if condition is false
local a = false

if a == false then
  print("a = false")
end

else

if condition is not met, then do else
local a = false

if a then
  print("a = true")
else
  print("a = false")
end

elseif

if condition is not met, then do elseif
local a = false

if a then
  print("a = true")
elseif a == false then
  print("a = false")
end

Logical Operators

and Allows you to check multiple conditions in a single if query.
or Allows you to add an alternative if query into a single if query.
not Checks if condition equals false or negative.

and

if condition a is true and condition b is false then
local a = true
local b = false

if a == true and b == false then
  print("a = true", "b = false")
end

or

if condition is true or condition == true then
local a = true

if a or a == true then
 print("a = true")
end

not

if condition is not true
local a = false

if not a then
 print("a = false")
end

Comparison Operators

== Equal to.
~= Does not equal.
>= Greater than or equal to.
> Greater than.
<= Less than or equal to.
< Less than.

equal to

if a equals 1
local a = 1

if a == 1 then
 print("a = 1")
end

does not equal

if a does not equal 2
local a = 1

if a ~= 2 then
 print("a = " .. a)
end

greater than or equal to

if a is greater than or equal to 2
local a = 5

if a >= 2 then
 print("a = " .. a)
end

greater than

if a is greater than 2
local a = 3

if a > 2 then
 print("a = " .. a)
end

less than or equal to

if a less than or equal to 2
local a = 2

if a <=  2 then
 print("a = " .. a)
end

less than

if a less than 2
local a = 0

if a <  2 then
 print("a = " .. a)
end

Mathematical Operators

+ Add
- Subtract or invert
* Multiply
/ Divide
^ Power
% Remainder

add

1 + 2 = 3
local a = 1
local b = 2
local result = a + b

print( result )

subtract

3 - 2 = 1
local a = 3
local b = 2
local result = a - b

print( result )

inverse

inv of 3 = -3
local a = 3
local result = -a

print( "original value: " .. a, "inversed value: " .. result )

multiply

2 x 2 = 4
local a = 2
local result = a * a

print( result )

divide

10 ÷ 5 = 2
local a = 10
local b = 5
local result = a / b

print( result )

power

base of 2, power of 8 (2ⁿ) = 256 (2x2=4x2=8x2=16x2=32x2=64x2=128x2=256)
local a, b = 2, 8
local result = a ^ b -- generate answer

-- break down power function into string result (ignore this code)
function power(i1, i2)
 local val = "" -- clear string
 --+--
 for i = 1, i2 do -- generate the longhand math formula
  if i < i2 then val = val..i1.."*" else val = val..i1 end
 end
 return "which is the equivalent of " .. val
end

print("power value of "..a.." & "..b.." = " .. result, power(a,b))

remainder

remainder of 25 ÷ 4 = 1 (25 ÷ 4 = 6.25 - 6 = 0.25 * 4 = 1)
local a = 25
local b = 4
local result = a / b -- 25 ÷ 4 = 6.25
local result_decimal = math.fmod(result, 1) -- 6.25 - 6 = 0.25
local result_rounded = math.modf(result) -- 6.25 - 0.25 = 6
local remainder = a % b -- 0.25 * 4 = 1

print("result: " .. result .. ", decimal: " .. result_decimal .. ", rounded: " .. result_rounded .. ", remainder: " .. remainder)

Tables

Tables are one of the features of Lua script, that make the scripting language so dynamic & easy to use, as they allow us to easily create tables, insert, remove & sort data. Tables are often comprised of arrays that usually involve keywords - or an index number - & a value. Tables can be accessed using multiple different methods.

Creating Tables

Below I am going to show you how to create various different types of tables/arrays.

Index Tables

method 1: automatically generated index values
local t = {1, 2, 3, 4, 5, "six", 7, "ate", 9} -- automatically assigns an index number to each value starting from 1
local str = "" -- empty string

for i = 1, #t do -- for 1 to table total, print value of index number
 str = str .. t[i] -- add current table index value to existing text data in the str variable
 if i < #t then str = str .. ", " end -- if i is less than table entry total then insert ", " into the str variable
end

print("table data: " .. str) -- print the table data
print( "table entry total: " .. #(t) ) -- print total of table entries

method 2: manually created index values
local t = {

[1] = 1,
[2] = "two",
[3] = 3,
[4] = 2 * 2,
[5] = "five",

}

local str = "" -- empty string

for i = 1, #t do -- for 1 to table total, print value of index number
 str = str .. t[i] -- add current table index value to existing text data in the str variable
 if i < #t then str = str .. ", " end -- if i is less than table entry total then insert ", " into the str variable
end

print("table data: " .. str) -- print the table data
print( "table entry total: " .. #(t) ) -- print total of table entries

Keyword Tables

keywords & values
local t = {

["hello world"] = "hello world",
example = "this also works",
camelCaseExample = "this will also work",
snake_case_example = "as will this"

}

for k, v in pairs(t) do
  print(k ..": " .. v)
end

print("\n...but as you can see it does not necessarily return them in the order they were created")

Index Values and Sub-Arrays

auto generated index entries with sub-arrays containing keywords
local t = {

{x = 100, y = 300},
{x = 200, y = 111}

}

print("x = " .. t[1].x .. ", y = " .. t[1].y)

Keywords and Sub-Arrays

keyword tables with index based sub-arrays containing keywords
local t_texts = {

["English"] = {
    {txt = "hello world!"},
    {txt = "goodbye world!"}
},

["German"] = {
  {txt = "hallo welt!"},
  {txt = "auf wiedersehen welt"}
}

}

print( t_texts["English"][1].txt )

Advanced Methods and Table Functions

Below are various table functions & advanced methods for interacting with tables.

Table Concatenation

table concatenation without any formatting (index tables only)
local t = {1, 2, 3, 4}

print( table.concat(t) )

table concatenation with formatting (index tables only)
local t = {1, 2, 3, 4}

print( table.concat(t, ", ") )

table concatenation with formatting & specified table index range (index tables only)
local t = {1, 2, 3, 4}

print( table.concat(t, ", ", 2, 4) )

Table Insert

insert a value into index position 2 in the specified table (index tables only)
local t = {"one", "three"}

print("before: " .. table.concat(t, ", ") )

table.insert(t, 2, "two") -- insert "two" into index position 2

print("after: " .. table.concat(t, ", ") )

append a value to the end of the specified table (index tables only)
local t = {"one", "two", "three"}

print("before: " .. table.concat(t, ", ") )

table.insert(t, "four") -- insert "four" at the end of the table

print("after: " .. table.concat(t, ", ") )

insert a keyword & value into a table
local t = {

["one"] = 1,
["three"] = 3

}

local str = "" -- init str variable
for k, v in pairs(t) do str = (str .. v .. " ") end -- concatenate data
print("before: " .. str)

t["two"] = 2 -- insert key "two" along with a value into the t table

str = "" -- reset str
for k, v in pairs(t) do str = (str .. v .. " ") end -- concatenate data
print("after: " .. str)

Quick note: Technically it's possible to mix & match keyword & index tables, but it's cleaner & less confusing all around if you don't. However feel free to use index values inside of sub-arrays if you don't want to use keywords, or keywords in sub-arrays of index tables.

Table Remove

remove entry from table (index table only)
local t = {1, 2, 3, 4}

print("before: " .. table.concat(t, ", ") )

table.remove(t, 3) -- remove index value 3 from the table

print("after: " .. table.concat(t, ", ") )

Quick note: Whenever you insert a new index entry into a table containing index values it will automatically adjust all of the index values; the same thing also happens whenever you remove an entry.

remove keyword entry from specified table
local t = {

["one"] = 1,
["two"] = 2,
["three"] = 3

}

local str = ""
for k, v in pairs(t) do str = (str .. v .. " ") end -- concatenate data
print("before: " .. str)

t["two"] = nil -- remove keyword from table by setting it as nil

str = ""
for k, v in pairs(t) do str = (str .. v .. " ") end -- concatenate data
print("after: " ..str)

Table Sort

sort index table alphabetically (ascending)
local t = {"seven", "ate", "nine", "and", "one", "ran away" }

print("before: " .. table.concat(t, ", ") )

table.sort(t) -- sort alphabetically (ascending a, b, c, etc)

print("after: " .. table.concat(t, ", ") )

Quick note: Mixed data tables will return an error if you try to sort them with the table.sort function, the tables need to contain strings or numbers, & not both at the same time; however you can get around it by inserting the numbers in as strings.

sort index table alphabetically (descending)
local t = {"7", "ate", "9", "and", "1", "ran away" }

print("before: " .. table.concat(t, ", ") )

table.sort(t, function(a, b) return a > b end) -- sort alphabetically (descending c, b, a, etc)

print("after: " .. table.concat(t, ", ") )

Table Count

get total amount of entries in index table (current level)
local t = {1, 2, 3, 4}

print( #t )

get total amount of entries in keyword table (current level)
local t = {

["one"] = 1,
["two"] = 4/2,
["three"] = 3

}

function countKeys(tbl)
  local keys = 0 -- init keys variable
  for k, v in pairs(tbl) do -- iterate through table
    keys = keys + 1 -- increment key value by 1 per key found
  end
  return keys -- return the total amount of keys found
end

print( countKeys(t) )

Iteration and loops

Being able to iterate through data for the purpose of reading it or manipulating it is very important when it comes to scripting as it is a real time saver. Lua offers numeric for loops, for loops over tables, as well as while and repeat loops.

Quick note: a loop runs completely before the next line after it is executed. In Visionaire this also means the game cannot draw a new frame while a loop is running. Never use a loop to wait for something (a value, a click, a timer). Use an event handler for the main loop, setDelay or the action part "Pause" instead.

Numeric for loop

Counts from a start value to an end value. An optional third value sets the step size.

local t = {"Hi,", "my", "name", "is", "Barry."}
local str = ""

for i = 1, #t do -- for variable equals 1 to table total entries do
  str = str .. t[i] -- insert string from table based on current index value into the str variable
  if i < #t then str = str .. " " end -- if i is less than table total then insert white space after each word
end

print(str) -- prints: Hi, my name is Barry.

for i = 10, 1, -2 do -- count down from 10 to 1 in steps of 2
  print(i) -- prints 10, 8, 6, 4, 2
end

ipairs: index tables

ipairs walks through an index table in order, starting at index 1, and stops at the first empty entry.

local fruit = {"apple", "banana", "cherry"}

for i, name in ipairs(fruit) do
  print(i .. ": " .. name) -- prints 1: apple, 2: banana, 3: cherry
end

pairs: keyword tables

pairs walks through every entry of a table, including keyword entries. The order is not guaranteed.

local player = {name = "Barry", coins = 12, hasKey = true}

for key, value in pairs(player) do
  print(key .. " = " .. tostring(value))
end

while

Repeats a block as long as a condition is true. The condition is checked before each run.

local n = 1

while n < 100 do
  n = n * 2
end

print(n) -- prints 128

repeat … until

Like while, but the condition is checked after each run, so the block runs at least once.

local roll

repeat
  roll = math.random(1, 6)
until roll == 6

print("finally a six")

break

break ends the current loop immediately.

local t = {3, 8, 15, 4}

for i = 1, #t do
  if t[i] > 10 then
    print("first value above 10 is at index " .. i) -- prints 3
    break
  end
end

Loops over Visionaire objects

Lists of the data structure, for example the objects of a scene or the items of a character, can be read like an index table.

local objects = game.CurrentScene.Objects

for i = 1, #objects do
  print(objects[i]:getName()) -- prints the name of every object of the current scene
end

for i, item in ipairs(game.CurrentCharacter.Items) do
  print(item:getName()) -- prints every item the current character carries
end

Functions

Functions can be really useful as they let us process/calculate huge amounts of data & return it, also if you include input arguments then they can potentially be reused multiple times over, which can reduce overall workload & increase workflow.

Creating Functions

Below I am going to show you how to create your own custom functions.

Classic Function

print hello world!
function hello()
 print("hello world!")
end

hello()

Variable as Function

print hello world!
hello = function()
 print("hello world!")
end

hello()

Function with Input Arguments

print 6
function plus(a, b)
 return a + b
end

print( plus(2, 4) )

Input Arguments and Fallback

print 0.5, 5
function divideAndConquer(a, b, c)
  c = c or 1 -- if c equals nil then c equals 1
  return (a / b) * c
end

print( divideAndConquer(2, 4) ) -- 2 ÷ 4 x 1 = 0.5
print( divideAndConquer(2, 4, 10) ) -- 2 ÷ 4 x 10 = 5