Welcome to this Defold API Cheat Sheet.
Defold is a tool which you can use to make games.
Visit the Defold Official Site to learn more.
This ref can be downloaded as a .zip file at the bottom of the page.
This ref is meant as easy API reference. Check the Book of Defold too.
Explore the vmath module of Defold. Many of the functions below have built in versions. The information below is provided as a reference in case you have need.
You can use ZeroBrane Studio to quickly test code samples.
Angles, Radians, Degrees
With Defold (and in math), 0 degrees is equal to east, 90 to north, 180 to west, 270 to south, and 360 to east again.
Radians are often used in game development as an alternative to degrees for representing angles. Many programming languages have built in functions for converting between the two. Radians are far more useful, are cleaner, and easier to use once you understand them. If you do not have a strong understanding of them you should dig into any resources you can find which explain them until they click for you.
Radians are based on the radius of a circle (the distance between the center of a circle and any point along its length) - 1 radius measure is equal to 1 radian along the circle's length. 1 radian is about 57 degrees.
There are 2pi radians in a circle. In most math, and in Defold, we begin to count from 0 facing right and then begin to count counter clockwise upward for pi/2 (90 degrees), then continue to count until we are facing left at pi (180 degrees), then continue to count until we are facing downward at 3pi/2 (270 degrees), and then continuing to count until we are again facing right at 2pi (360 degrees) to complete a full circle.
There are about 6.283... radians for the length of a circle. That is equal to 2 * pi. To visualize this better imagine you have a picture of a circle and a dot at its center. You take some string and cut it to the length from center of the circle to any point on the circle's circumference. You place the cut string along the circumference, and repeat measuring, cutting string, and placing. You will find that your seventh piece of string doesn't fit - only about 28% of its length fits. So you have 6.28... pieces of string.
math.pi = 3.14...
math.deg() converts radians into degrees
math.rad() converts degrees into radians
To convert from degrees to radians divide the degrees by (180/pi).
To convert from radians to degrees multiply the radians by (180/pi).
print(math.deg(2 * math.pi)) -- 360
print((2 * math.pi) * (180/math.pi)) -- 360
print(math.rad(360)) -- 2pi ... 6.2831853071796
print(360 / (180/math.pi)) -- 2pi ... 6.2831853071796
print(2*math.pi) -- 2pi ... 6.2831853071796
2pi = one full circle
pi = one half circle
2pi/3 = one third circle
pi/2 = one fourth circle
2pi/5 = one fifth circle
pi/3 = one sixth circle
2pi/7 = one seventh circle
pi/4 = one eighth circle
2pi/9 = one ninth circle
pi/5 = one tenth circle
2pi/11 = one eleventh circle
pi/6 = one twelfth circle
30 degrees = pi/6 radians
45 degrees = pi/4 radians
60 degrees = pi/3 radians
90 degrees = pi/2 radians
180 degrees = pi radians
270 degrees = 3pi/2 radians
360 degrees = 2pi radians
Dot Product (inner product) - Calculating Angle Differences
The dot product of two vectors tells you how far apart they are in a single number - the angle amount between them. It produces a scalar value - how much of one vector is pointing in the direction of another.
vector1.x * vector2.x + vector1.y * vector2.y
This is equal to
vector1.magnitude * vector2.magnitude * math.cos(angle between vector 1 and vector 2)
If vector1 and vector2 are unit vectors (magnitude of 1) then their dot product will be equal to just math.cos of the angle between the two vectors. Defold does have built in functionality for creating and using vectors and normalization of vectors which you should use when working with the built in vectors.
If you are using unit vectors then the dot product of two vectors can give you useful information. This can be useful when you want to avoid other CPU expensive functionality.
If > 0 : angle between vectors is less than 90 degrees - it is acute
If < 0 : angle between vectors is more than 90 degrees - it is obtuse
If == 0 : angle between vectors is 90 degrees - the vectors are orthogonal
If == 1 : angle between the two vectors is 0 degrees - vectors are parallel and point in same direction
If == -1 : angle between the two vectors is 180 degrees - vectors are parallel, point in opposite directions
This information can be used to find relative orientation of other objects in your game. Such as if certain objects are facing others. If you rotate one of the vectors by 90 degrees first you can find if objects are to the left or the right of each other.
Rounding errors may give you values very close to these options in situations where they should be exact (such as being exactly 0). You may want to add extra code to see if values are close enough for you to accept the various conditions.
Cross Product (vector product) tells you the perpendicular direction of any two vectors, how much a vector is perpendicular to another. It can give the axis to rotate around to face something. The 2d form returns a scalar. Its 3d form returns a vector.
cross_product_vectors = function (x1, y1, x2, y2)
return x1*y2 - y1*x2 -- 0 == parallel
end
cross_product_vectors_3d = function (x1, y1, z1, x2, y2, z2)
resX = y1 * z2 - z1 * y2
resY = z1 * x2 - x1 * z2
resZ = x1 * y2 - y1 * x2
return resX, resY, resZ
end
If the z of both vectors in the 3d version is zero then the 3d form will return the "same end result" as the 2d form.
print(cross_product_vectors(1,2,3,4)) -- -2
print(cross_product_vectors_3d(1,2,0,3,4,0)) -- 0 0 -2
Angle Between Two 2D Vectors - difference in orientation
angle_between_vectors = function (x1, y1, x2, y2)
local arc = math.sqrt((x1*x1 + y1*y1) * (x2 * x2 + y2 * y2))
if arc > 0 then
arc = math.acos((x1*x2 + y1*y2) / arc)
if x1*y2 - y1*x2 < 0 then
arc = -arc
end
end
return arc
end
print(angle_between_vectors(1,1,-1,-1)) -- 3.14... 180 degrees
Angles in Defold
When changing the direction of a 2d object in Defold, by using the Euler (pronounced oiler) property, the angle starts facing right and 0 degrees, goes up to 90 degrees and so on.
go.animate("." "euler.z", go.PLAYBACK_LOOP_PINGPONG, 360, go.EASING_LINEAR, 10)
Animates the current Game Object so that its angle direction on the z plane is animated back and forth between its current (default 0) and + 360 amount of difference. If you were to first set the euler.z to another value first you would see the animation would go back and forth between a 360 degree arc from that value and +360 degrees of that value.
go.set(".", "euler.z", 180)
go.animate("." "euler.z", go.PLAYBACK_LOOP_PINGPONG, 360, go.EASING_LINEAR, 10)
Points and Vectors
A point is a coordinate - it can be x,y for a coordinate on a 2D space, or x,y,z for a coordinate on a 3D space.
A vector is a direction toward a point (relative to an origin) along with a magnitude (length).
Convert Radian Angle to a 2D Vector
angle_to_vector = function (angle, magnitude)
magnitude = magnitude or 1 -- if no magnitude supplied make it a unit vector
local x = math.cos ( angle ) * magnitude
local y = math.sin ( angle ) * magnitude
return x, y
end
Converting Vectors to Radian Angle
vector_to_angle = function (x,y)
return math.atan2 (y, x)
end
temp_angle = math.pi
temp_vector_x, temp_vector_y = angle_to_vector(temp_angle)
print(temp_vector_x, temp_vector_y) -- -1 1.2246063538224e-016
new_angle = vector_to_angle(temp_vector_x, temp_vector_y)
print(new_angle) -- 3.1415926535898
x = math.cos(math.pi)
y = math.sin(math.pi)
radian = math.atan2(y,x)
print(radian)
print(math.pi)
Get magnitude (length) of 2D Vector
get_vector_magnitude = function (x,y)
return math.sqrt( x * x + y * y)
end
print(get_vector_magnitude(2,9)) -- 9.21...
Get magnitude (length) of 3D Vector
get_vector_magnitude_3d = function (x,y,z)
return math.sqrt( x * x + y * y + z * z)
end
Rotating 2D Vectors
Vectors can be rotated without computation by 90 or 180 degrees simply by swapping / negating their x,y values.
x,y = -y,x to rotate 90 degrees counterclockwise
x,y = y,-x to rotate 90 degrees clockwise
x,y = -x, -y to rotate 180 degrees
Rotating a vector based on a radian angle
rotate_vector = function(x, y, angle)
local c = math.cos(angle)
local s = math.sin(angle)
return c*x - s*y, s*x + c*y
end
print(rotate_vector(1,1, math.pi)) -- rotates by 180 degrees to -1, -1
Scaling 2D Vectors
To scale a vector means to increase its magnitude (length) by another magnitude - here called a scalar. A scaled vector has the same orientation but a new magnitude.
scale_vector = function (x,y,scalar)
return x * scalar, y * scalar
end
If you scale a vector by its inverse length (1 / magnitude of vector) the vector will get a length of 1 - it will become a unit vector - it will become normalized.
Normalizing 2D Vectors
Normalization of vectors maintains their orientation, but reduces their magnitude (length) to 1. This new vector is called a unit vector, and is very useful for certain calculations.
normalize_vector = function (x,y)
if x == 0 and y == 0 then return 0, 0 end
local magnitude = math.sqrt(x * x + y * y)
return x / magnitude, y / magnitude
end
print(normalize_vector(100,0)) --> 1,0
Checking Circular Collisions - Check for collisions between circle shapes
circular_collision_check = function (ax, ay, bx, by, ar, br)
-- x and y positions both both objects
-- along with their radius
local combined_radius = ar + br
local dx = bx - ax
local dy = by - ay
local dist = math.sqrt(dx * dx + dy * dy) -- check distance between objects
return dist < combined_radius -- check if the distance is less than the combined radius (overlap)
end
print(circular_collision_check(1,1,30,25,5,10)) -- false
print(circular_collision_check(1,2,36,25,50,100)) -- true
Distance Between 2D Points - Find distance between objects on a 2d space
distance_between_points = function (x1,y1,x2,y2)
local dX = x2 - x1
local dY = y2 - y1
return math.sqrt((dX^2) + (dY^2))
end
print(distance_between_points(0,0,1,1)) -- 1.4142...
print(distance_between_points(0,0,0,8)) -- 8
print(distance_between_points(12,4,45,64)) -- 8
Distance Between 3D Points - Find distance between objects in a 3d space
distance_between_points_3d = function (x1,y1,z1,x2,y2,z2)
local dX = x2 - x1
local dY = y2 - y1
local dZ = z2 - z1
return math.sqrt((dX^2) + (dY^2) + (dZ^2))
end
print(distance_between_points_3d(0,0,0,1,1,0)) -- 1.4142...
Get the distance between two x positions
get_distance_x = function (x1, x2)
return math.sqrt( (x2 - x1) ^ 2)
end
print(get_distance_x(-5,10)) -- 15
Get the distance between two y positions
get_distance_y = function (y1, y2)
return math.sqrt( (y2 - y1) ^ 2)
end
print(get_distance_y(-5,10)) -- 15
Average of radian angles
average_angles = function (...)
local x,y = 0,0
for i=1, select("#", ...) do
local a = select(i, ...)
x, y = x + math.cos(a), y + math.sin(a)
end
return math.atan2(y, x)
end
print(average_angles(1.4, 1.2, 1.6))
Find the angle in degrees between 0,0 and a point
angle_of_point = function (x, y)
local radian = math.atan2(y,x)
local angle = radian*180/math.pi
if angle < 0 then angle = 360 + angle end
return angle
end
print(angle_of_point(0,-4))
Find the degrees between two points
angle_between_points = function (x1, y1, x2, y2)
local dX, dY = x2 - x1, y2 - y1
return angle_of_point(dX, dY)
end
print(angle_between_points(0,0,1,1)) -- 45
Find smallest angle between two angles - for dealing with "angles" below 0 or greater than 360 and finding the shortest turn to reach the second
smallest_angle_difference = function ( a1, a2 )
local a = a1 - a2
if ( a > 180 ) then
a = a - 360
elseif (a < -180) then
a = a + 360
end
return a
end
print(smallest_angle_difference(720,270)) -- 90
Round up to the nearest multiple of a number - say you have a number 450 and you want to round it to 500, or round to 1000 if the number was 624 - this sort of thing is useful for dynamic layouts
round_up_nearest_multiple = function (input, target)
return math.ceil( input / target) * target
end
print(round_up_nearest_multiple(24, 100)) -- 100
print(round_up_nearest_multiple(124, 100)) -- 200
Rounds up or down to the nearest container size multiple
round_nearest_multiple = function (input, target)
local result = math.floor( (input / target) + 0.5) * target
if result == 0 then result = target end -- ensure non-zero container size
return result
end
print(round_nearest_multiple(24, 100)) -- 100
print(round_nearest_multiple(124, 100)) -- 100
print(round_nearest_multiple(155, 100)) -- 200
Clamps a number into a range
clamp_number = function (number, low, high)
return math.min(math.max(number, low), high)
end
print(clamp_number(45, 0, 100)) -- 45
print(clamp_number(45, 50, 100)) -- 50
print(clamp_number(101, 0, 100)) -- 100
Lerp - linear interpolation - useful to use with delta time (dt) to get smooth movements
lerp_number = function (start, stop, amount)
amount = clamp_number(amount, 0, 1) -- amount is also called the "factor"
return((1-amount) * start + amount * stop)
end
print(lerp_number(0,1,0.4)) -- 0.4
print(lerp_number(20,100,0.1)) -- 28
Normalize (percentage) two numbers so that they sum to unity (1)
Note that there are multiple meanings for "normalization"
This kind of normalization is useful for example for turning event counts into probabilities
Or conditional probabilities, such as rolling a dice
Also useful in graphics stats
When normalizing vectors it means change the magnitude to 1 so that it's more useful for orientation
normalize_numbers = function (a,b)
n = a + b
if n == 0 then return 0,0,0
else
return a/n,b/n,n
end
end
print(normalize_numbers(5,10)) -- 0.333... 0.666... 3
print(normalize_numbers(1,99)) -- 0.01 0.99 100
Get sign of number : 1 if positive, -1 if negative, 0 if 0
get_sign = function (number)
return number > 0 and 1 or number < 0 and -1 or 0
end
Round a number
round_number = function(number)
return math.floor(number + 0.5)
end
-- normalize all values of an array to be within the range of 0 to 1 based on their "weight"
-- divide each number in your sample by the sum of all the numbers in your sample
Matrices
A matrix is an array of related values. Vectors can be represented as matrices. In most game dev situations, you will be using matrices which have the same number of rows and columns.
A B
C D
The above is a matrix with a 2x2 dimension. A is element 1,1 and D is element 2,2.
A B C D
E F G H
The above is a matrix with a 2x4 dimension. A is element 1,1 and H is element 4,2.
Vectors can be represented as matrices either as a long row or a tall column. These representations allow you to modify matrices with vectors.
x y z
or
x
y
z
Adding, Subtracting of Matrices
To add two matrices together you simply add the elements together. Same with subtraction.
A B
C D
+
W X
Y Z
=
A+W B+X
C+Y Z+D
Multiplying Matrices
You can multiply a matrix by a single number, a scalar, by multiplying that number by each element individually.
2
x
1 0
2 -4
=
2 0
4 -8
To multiply two matrices together it requires to do the dot product of the rows and columns - multiply matching members and then add them up together.
Consider two matrices multiplied together
1 2 3
4 5 6
x
7 8
9 10
11 12
=
(1,2,3)*(7,9,11) (1,2,3)*(8,10,12)
(4,5,6)*(7,9,11) (4,5,6)*(8,10,12)
=
1*7+2*9+3*11 1*8+2*10+3*12
4*7+5*9+6*11 4*8+5*10+6*12
=
58 64
139 154
To be able to multiply two matrices, the number of columns in the first matrix must equal the number of rows in the second matrix. The resulting matrix will have the same number of rows as the second matrix, and the same number of columns as the first matrix. A 1x3 matrix multiplied by a 3x4 matrix produces a 1x4 matrix.
A B
C D
*
W X
Y Z
=
A*W+B*Y A*X+B*Z
C*W+D*Y C*X+D*Z
Matrix multiplication is not commutative, order matters. If you multiply two matrices together in a different order you will get different results.
1 2
3 4
x
5 6
7 8
=
(1,2)*(5,7) (1,2)*(6,8)
(3,4)*(5,7) (3,4)*(6,8)
=
1*5+2*7 1*6+2*8
3*5+4*7 3*6+4*8
=
19 22
43 50
vs
5 6
7 8
x
1 2
3 4
=
(5,6)*(1,3) (5,6)*(2,4)
(7,8)*(1,3) (7,8)*(2,4)
=
5*1+6*3 5*2+6*4
7*1+8*3 7*2+8*4
=
23 34
31 46
You can write your own functions for deal with matrices, but Defold has most of what you need built into its own API.
Matrices are mostly used with graphics programming such as transformation matrices in rendering.
Quaternions
Unit quaternions (magnitude of 1) are useful for representing orientation. Imagine a sphere with a dot at its center. Imagine an airplane's tail end attached to the dot. The airplane can be rotated around the sphere to face any direction, and it can be rotated so that its upward direction is changed. If you can imagine this then you can imagine how a unit quaternion looks like and how it can be useful. While Euler angles are convenient for 2D z rotation, for anything 3D you will want to use quaternions for representing orientation change.
local function find_center_of_points_2d(points)
local total_x = total_x or 0
local total_y = total_y or 0
local count = #points
for id, point in pairs(points) do
total_x = total_x + point.x
total_y = total_y + point.y
end
if not (total_x == 0) then total_x = total_x / count end
if not (total_y == 0) then total_y = total_y / count end
return total_x, total_y
end
points_2d = {{x=0,y=0}, {x=10,y=10}, {x=100,y=100}, {x=-10,y=-100}, {x=54,y=-247} }
midpoint_x, midpoint_y = find_center_of_points_2d(points_2d)
print(midpoint_x, midpoint_y)
local function find_center_of_points_3d(points)
local total_x = total_x or 0
local total_y = total_y or 0
local total_z = total_z or 0
local count = #points
for id, point in pairs(points) do
total_x = total_x + point.x
total_y = total_y + point.y
total_z = total_z + point.z
end
if not (total_x == 0) then total_x = total_x / count end
if not (total_y == 0) then total_y = total_y / count end
if not (total_z == 0) then total_z = total_z / count end
return total_x, total_y, total_z
end
points_3d = {{x=0,y=0, z=22}, {x=10,y=10, z=15}, {x=100,y=100, z=0}, {x=-10,y=-100, z=0}, {x=54,y=-247, z=0} }
midpoint3d_x, midpoint3d_y, midpoint3d_z = find_center_of_points_3d(points_3d)
print(midpoint3d_x, midpoint3d_y, midpoint3d_z)
http://www.lua.org/manual/5.1/manual.html
https://www.lua.org/pil/contents.html
http://www.lua.org/cgi-bin/demo
Lua Coding Pro Tips
Write simple code that's easy to understand.
Keep is simple, stupid.
Comment code where necessary, but your code should be obvious too.
Your future you will thank you for simplicity.
You may be clever, but your future you may forget.
If you don't follow this your future you will be unhappy.
Avoding writing code as compact as possible.
Break your code into multiple lines.
More packed into a single line doesn't mean it's faster, and can be more difficult to read.
Clarity is very important.
What a single line of code does should be obvious.
Use math instead of conditional statements.
Knowing more math can help you to do less work.
For most cases, math is superior than using conditionals. It's more flexible.
Code with many conditionals is more likely to be buggy.
Avoid duplicating function calls.
If you have a chunk of code which calls the same functions over and over consider a rewrite.
It makes refactoring more difficult, and increases chance for bugs.
Fewer function calls makes your code easier to update and port.
Don't create extra Lua tables when you don't need to.
If you wish to return many values you can do so directly, you don't have to wrap them in a table.
Creating many tables can make the garbage collection hang often.
If your code is creating many Lua tables consider a refactor to eliminate them. Ask for help!
Don't get lost in feature creep. Only code features you need NOW.
Don't write tools that you don't need to.
Use tools by others first, then refactor them out later if you need to.
Be productive, don't get lost in things that don't actually matter.
This is where many indie game devs fail - they make tools and don't focus on shipping their game!
Keep your code tidy.
Don't keep duplicate functionality all over your code.
Spend your time learning the features of Lua very well.
Tables
Metatables
Coroutines
Modules
Closures
Upvalues - aka "external local variables"
Tail call recrusion
Study the sample code here: http://lua-users.org/wiki/SampleCode
Read this: http://www.luafaq.org/
Learn the API of some of the popular modules such as LuaSocket
and
break
do
else
elseif
end
false
for
function
if
in
local
nil
not
or
repeat
return
then
true
until
while
print("this will print")
nil
boolean
number
string
table
function
thread
userdata
'...' and "..." and [[...]] define what's inside a string
The various types are for allowing easy inclusion of others as parts of strings.
Escape sequences are ignored when using double bracket quotes.
Lua uses C-style escape sequences.
\a -- bell - originally used to make sounds - a stands for "alert"
\b -- backspace
\f -- formfeed
\n -- newline
\r -- carriage return
\t -- horizontal tab
\v -- vertical tab
\\ -- backslash
\" -- qutoation mark / double quote
\' -- apostrophe / single quote
You can specify decimal style ASCII character codes with \ddd where each d is a digit.
Look up ASCII table codes for a reference.
Use .. for concactenation of strings : print("This costs $" .. 5 .. " to buy!")
Numbers can be concatenated onto strings, and doing so coerces them to the string type first.
Each time you concatenate you are creating a new string in memory.
Lua has a library of useful string functions.
string.byte(string, n) -- returns the ASCII value of the character of the nth character
string.char(...) -- retruns a string constructed from a comma delimiated list of ASCII values
string.find(string, string2) -- returns the starting and ending character of the first mention of the substring "string2" within "string"
string.format(rule, thing1, thing2, ...) -- returns a single formatted result composing one or more comma seperated things based on the rule
string.gsub(string, pattern, thing, n) -- returns a modified string modified by pattern and thing for a maximum of n times (n is optional): string.gsub("Hello", "l", 7, 1) --> He7lo
string.format follows similar rules as the standard C printf function.
The rule is composed of regular text and control directives and flags that define where to place the arguments.
Directives
%c - converts number to a single character (like string.char) : string.format("%c", 90) --> Z
%o - convert to octal (base 8) : string.format("%o", 777) --> 1411
%f - convert to floating point
%d - convert to decimal
%x - convert to hexadecimal
%X - convert to hexadecimal in uppercase
%s - convert to string
%e - convert to scientific notation
%E - convert to scientific notation in upper case
%q - formats string so that Lua can read it : print(string.format("%q", '"hello"' )) -> "\"hello\""
%% - outputs a % character
--Flags
- : left justify align result : string.format("(%8s)", "ping") --> ( ping), string.format("(%-8s)", "pong") --> (pong )
+ : always prefix with - or + sign : string.format("%+d", 2 ) --> +2
n : add spaces until total characters fit n : string.format("(%3s)", "pig") --> (pig), string.format("(%6s)", "pig") --> ( pig)
0n : left fill (pad) with n number zeroes : string.format("%03d", 2) --> 002
# : changes behavior of various formats : octal prefixes with 0 if necessary, hex prefixes with 0x/0X, e/F/f always show decimal,
--Operators
From most precedence to least
^ (exponentiation)
not (logical)
# (length) - (negation) (these unary operators operate on single operands) : -5; print(#"hello")
* / %
+ -
.. (right associative string concatenation)
< > <= >= ~= == (equality)
and (stops on false or nil)
or (stop on true and not false or nil)
x and y or z -- ternary operator
a = 1 -- single assignment
b = a -- variables can hold any value
b = 5 + 5 -- a second variable which shadows the first, observe for yourself below
local x, x = 1, 2 -- example of multiple assignment
local i = 0
repeat
i = i + 1
local name, value = debug.getlocal(1, i)
if name == 'x' then
print(name..' = '..value)
end
until not name
c = "c"
d = 25.4 -- all numbers are double-precision floating-point numbers, there are no integers, modern CPUs are fast
z, y = y, z -- Lua evaluates all values and then executes assignment which allows swapping of values
w[i], x[j] = x[j], w[i]
aa, bb, cc = 1 -- aa --> 1, bb --> nil, cc --> nil
xx, yy, zz = 0, 0, 0 -- print(xx,yy,zz) --> 0 0 0
health = 0 or health -- if health is nil then it will be set to 0, else it will be set to itself
ab, bc = 1, 2, 3 -- 3 is discarded
a = nil -- a is destroyed, eligible for garbage collection
a = zzzzzz -- if zzzzzz is not defined then a will = nil
a = "1" + "2" -- a = 3
a = 1 .. 2 -- a = "12"; type(a) --> string
--Control Structures
do ... end -- code between do ... end has local scope
if ... then ... elseif ... then ... else ... end -- conditional exeuction
while ... do ... end -- loop as long as what is between while ... do is true
repeat ... until ... -- if what is after until is true continue to do what is between repeat and until
for variable = start, end, step do ... end -- do what is between do and end until the variable set to start is equal to the end
for variables in interator do ... end
break -- exists loops
local pets = {"Tom", "Lucy", "Dino", "Ruby", "Disco"}
while #pets > 0 do -- while table pets is not empty
local pet = table.remove(pets, 1) -- pet gets the value of the removed pet name
if pet == "Ruby" then break end -- example of break
print(pet)
end
for i = 1,10 do
print(i.." fish")
end -- count up to 10 fish
for i = 10,0,-1 do
print(i.." HP")
end -- count down to 0 HP
do
local name = "Ana"
print("Hello, " .. name .. "!")
end
print(name .. " are you still there?") -- name is no longer available
local coins = 7
if coins > 0 then print("You're rich! You have " .. coins .. "C!") end
if coins == 0 then
print("You're poor! Go out and make some money!")
elseif coins == 1 then
print("You have a single coin. Cherish it.")
else
print("You have so many coins!!!")
end
repeat
coins = coins - 1
print("I'm stealing your riches! You only have " .. coins .. "C left!")
until coins == 0
local loot = { keys = 2, "sword", "axe", "poition", coins = 99 }
for key, val in ipairs(loot) do -- ipairs only cares for numerical indexed elements
print(key, val)
end
for key, val in pairs(loot) do -- pairs cares for all elements
print(key, val)
end
; can be used to have multiple statements on a single line purely to avoid ambiguity
spaces are not always necessary either
a,b=1,2; print(a+b)
a,b=1,2 print(a+b) -- works too
print(1)print(2)print(1+2) -- works, but don't do this
--Table Constructors
table = {} - empty table
t = {"y",2,3} - array
t = {[1]="y",[2]=2,[3]=3} -- explicit assignment
t = {["key"]="value"}; print(t.key) --> value
t = {key="value"}; print(t.key); --> value
t = {x=1; "hi"}; print(t[1],t.x) -- mixed content
--Functions
print "Hello!" -- () must be included to execute *most* functions
print { key = "value" } -- excemptions are single object / string calls
function hello_world(name) -- simple function
print("Hello, "..name)
end
hello_world("Defold")
local yello_world = function(name) -- anonymous function
print("Yello, "..name)
end
yello_world("Refold")
local dragon = {}
function dragon.attack()
print("Dragon Fire!")
end
dragon.attack()
(function (name) print("Hello, "..name) end)("Aefold") -- self-executing anonymous function
function get_dice_rolls(d, n)
-- this kind of function could have added logic to allow any number of dice and any side number...
-- for better random numbers, use an Mersenne Twister implementation
math.randomseed(os.time()) -- comment this line to always get the same "random" result (5,5,4)
if d == 6 and n == 3 then return math.random(6), math.random(6), math.random(6) end
end
local d1, d2, d3 = get_dice_rolls(6, 3)
print(d1,d2,d3)
function print_list(...) -- varargs, variable number of arguments -- use with caution
print(...)
end
print_list(1,2,3,4,5, "donut")
local tiger = {name = "Dusty"}
function tiger.get_name(self)
return self.name -- "self" could be anything
end
print(tiger.get_name(tiger))
print(tiger:get_name()) -- shortcut to pass what's before the colon as the argument
local lion = {name = "Rufus"} -- another way to write the above
function lion:get_name()
return self.name
end
print(lion:get_name())
print(lion.get_name(lion))
--Object Orientation
-- This is not at all necessary, but you can see it is possible.
local Inventory = { gold = 0 }
function Inventory:new (o)
o = o or {} -- if o is nil / not provided as an argument then set it to a blank table
setmetatable(o, self)
self.__index = self
return o
end
function Inventory.take(self, v)
self.gold = self.gold - v
end
function Inventory.give(self, v)
self.gold = self.gold + v
end
my_stuff = Inventory:new{gold = 0}
my_stuff:give(777)
my_stuff:take(-1)
print(my_stuff.gold) -- I have loads of gold
your_stuff = Inventory:new()
print(your_stuff.gold) -- you have no gold
--Metatables
-- Any table can have a metatable attached to it.
-- A metatable is a table with special keys set that can define unique behavior for its various operations.
t = {} -- define normal table
mt = {} -- soon to be meta table but empty still
setmetatable(t, mt) -- sets mt to be t's metatable - however if t's metatable has a __metatable field then returns t
getmetatable(t) -- returns mt - returns either __metatable field of t's metatable, t's metatable, or nil
-- The first three lines of the above could be shortened to:
t = setmetatable({}, {}) -- setmetatable returns its first argument
rawget(t,i) -- gets t[i] of a table directly
rawset(t,i,v) -- sets t[i] of a table directly
rawequal(t1,t2) -- returns boolean equality, checks directly
-- Metatables respond to keys which are strings and begin with two underscores __
-- The values of these keys are usually set to functions or tables.
__add -- +
__sub -- -
__mod -- %
__unm -- unary - for negation
__concat -- ..
__lt -- <
__index -- get values based on keys
__call -- call tables as functions
__metatable -- value from getmetatable()
__mul -- *
__div -- /
__pow -- ^
__len -- # length
__eq -- checking for equality ==
__le -- <=
__newindex -- set values of keys
__tostring -- convert table to string
__mode -- see: https://www.lua.org/pil/17.html
-- __index is the most used metatable key - Lua looks for __index keys to find values
-- if Lua doesn't find the value of the key in the main table it looks for the __index
-- and then looks for the same key within the table of __index
-- if __index contains a function then it's called with its parent table and the key as the arguments
stuff = { thing = "ball"}
t = setmetatable({}, { __index = stuff })
print(t.thing) -- prints "ball"
t2 = setmetatable({}, {
__index = function(t,key)
if key == "goof" then
return "goofs and gafs"
else
print("Just so you know!")
return cashtable[key]
end
end
})
print(t2.goof) -- prints "goofs and gafs"
t2.goof2 = "dosh" -- prints "dosh" but not "Just so you know!"
print(t2.goof2)
cashtable = { gabber = "cash" }
print(t2.gabber) -- prints "Just so you know!" and the value of gabber key in cashtable
-- __newindex contains either a function or a table
-- Lua looks for a __newindex table when you attempt to set values of keys
toys = {}
t = setmetatable({}, {__newindex = toys})
t.best = "bear"
print(t.best) -- prints nil
print(toys.best) -- prints "bear"
t2 = setmetatable({}, {
__newindex = function(t,key,value)
if key == "cat" then
rawset(t,key,"I love cats!")
else
rawset(t,key,value)
end
end
})
t2.cat = "Luna"
print(t2.cat) -- prints "I love cats!"
t2.dog = "Spot"
print(t2.dog) -- prints "Spot"
-- rawget and rawset allow you to bypass __index and __newindex functionality commonly used to avoid recursion
-- these are a bypass only, not related to performance
-- you can set custom functionality for operators with metatables such as + - / * and so on
t = setmetatable ({10,20,30}, {
__add = function(t,val)
tt = {}
for _, v in ipairs(t) do
table.insert(tt, v+val)
print(v+val)
end
return new
end
})
t = t + 9 -- inserts a copy of each value in the table + 9
-- __call allows you to call tables as functions
-- it is commonly used for forwarding function calls to
-- for example be able to stay game instead of game.start
t = setmetatable ({}, {
__call = function(...)
local args ={...}
for i = 1, #args do
print(args[i])
end
end
})
t(5, 2, 6, 4, 555) -- prints each value
-- __tostring is used by tostring to convert a table to a string
-- when you print() a table usually get you get something like "table: 0x00321ca0"
-- you can change this behavior to print its contents
t = setmetatable({777}, {
__tostring = function (t)
return t[1]
end
})
print(t)
--Lua Library
--Enviroment, globals
getfenv([f]) -- returns enviroment of a function f, if f is 1 return current enviroment, if f is 0 return global enviroment, if the set enviroment has a field __fenv that is returned instead
setfenv(f,t) -- see: https://www.lua.org/pil/14.3.html
_G -- a global variable that holds the global enviroment _G._G == _G
-- for n in pairs(_G) do print(n) end -- print out all global top level enviroment
-- for n in pairs(_G.gui) do print(n) end -- print contents of gui related - this can help you explore Defold
_VERSION -- current Lua interpreter version (Lua 5.1)
--Loading, executing
require(module) -- loads a module
dofile(file) -- load, executes, returns returned values
load(ld) -- loads chunk from string or if ld is a function it's called to get the chunk
loadfile(file) --
loadstring(s)
pcall(f)
xpcall(f,h)
--Output, errors
print() -- prints comma seperated list of arguments to console
pprint() -- Defold adds support for pprint() which prints the contents of tables in a pretty way
error() -- terminates program with error message (does Defold modify this behavior?)
assert(v [,message]) -- calls error() if v is nil/false and if not then returns the argument(s), it can be used to execute prepared loaded code ex: assert(loadstring(s))() - you can set message to any kind of error message
--Information, conversion
select(index, ...) -- if index is a number returns arguments after argument at the location of index, else index must be the string "#" and then the number of arguments is returned -- print(select(3,1,2,3,4)) -- prints "3 4"; print(select("#",1,2,3,4,5)) -- prints "5"
type(x) -- returns the type of x as a string
tonumber(x [,b]) -- converts string to a number, b optional to define base (default 10)
unpack(t) -- returns the contents of a table as individual values
--Iterators
ipairs(t) -- ipairs only cares for numerical indexed elements - returns index, value pairs of array t
pairs(t) -- pairs cares for all elements - returns key value pairs of table t
next(t [,index]) -- traverse all fields of a table -- if index is nil it starts at the first value -- returns next index of the following field -- you can use next to check if a table is empty
nextvar
--Garbage Collection
collectgarbage(opt [,arg])
--Modules / packages
module(name, ...)
package.loadlib(lib,func)
package.path, package.cpath
package.loaded
package.preload
package.seeall(module)
--Coroutines
coroutine.create(f)
coroutine.resume(coroutine,arguments)
coroutine.yield(arguments)
coroutine.status(coroutine)
coroutine.running()
coroutine.wrap(f)
--Tables
table.insert(t, [i,] v)
table.remove(t [,i])
table.maxn(t)
table.sort(t [,cf])
table.concat(t [,s [, i [, j]]])
--Maths
--Basic operations
math.abs(x)
math.mod(x,y)
math.floor(x)
math.ceil(x)
math.min(arguments)
math.math(arguments)
--Exponential, logarithmic
math.sqrt(x)
math.pow(x,y)
__pow(x,y) -- what is called when the ^ operator is used
math.exp(x)
math.log(x)
math.log10(x)
--Trigonometry
math.deg(a)
math.rad(a)
math.pi
math.sin(a)
math.cos(a)
math.tan(a)
math.asin(x)
math.acos(x)
math.atan(x)
math.atan2(y,x)
--Splitting on powers of 2
math.frexp(x)
math.ldexp(x,y)
--PRNG (pseudo-random number generator)
math.random([n [,m])
math.randomseed(n)
--Input / Output (I/O)
io.open(fn [,m])
file:close()
file:read(formats)
file:lines()
file:write(values)
file:seek([p][,of])
file:flush()
--Simple I/O
io.input([file])
io.output([file])
io.close([file])
io.read(formats)
io.lines([fn])
io.write(values)
io.flush()
--Standard files and utility functions
io.stdin, io.stdout, io.stderr
io.popen([prog [,mode]])
io.type(x)
io.tmpfile()
--Operating System (OS)
--System interaction
os.execute(cmd)
os.exit([code])
os.getenv(var)
os.setlocale(s[,c])
os.remove(fn)
os.rename(of,nf)
os.tmpname()
--Date/time
os.clock()
os.time([tt])
os.date([fmt[,t]])
os.difftime(t2, t1)
--Time formatting directives
%c
%x
%y
%j
%m
%b
%d
%U
%w
%a
%H
%p
%H
%p
%M
%S
%Z
%X
%y
%B
%W
%A
%I
--Debugging
--Basics
debug.debug()
debug.getinfo(f[,w])
debug.getlocal(n,i)
debug.getupvalue(f,i)
debug.traceback([msg])
debug.setlocal(n,i,v)
debug.setupvalue(f,i,v)
debug.sethook([h,m[,n]])
debug.gethook()
debug.info result fields
source
short_src
linedefined
what
name
namewhat
nups
func
debug.getinfo options
n
f
S
--Command line syntax
lua [options][script[arguments]]
Options
-
-e stats
-l filename
-i executes script then goes into interactive mode
-v prints Lua version number
-- stops parsing options
Lua enviroment varaibles
LUA_INIT
LUA_PATH
LUA_CPATH
_PROMPT[2]
Special Lua variables
arg
_PROMPT[x]
Lua compiler command line syntax
luac[options][filenames]
-
-l
-o filename
-p
-s
-v
--
-- Y combinator
function Y(f)
local function recurse(...)
return f(recurse, ...)
end
return recurse
end
factorial = Y(function(factorial, n)
if n == 1 then
return 1
else
return factorial(n - 1) * n
end
end)
print(factorial(5)) --> 120
-- Fibonacci series
fibotable = {[1] = 0, [2] = 1}
setmetatable(fibotable, {
__index = function (t, i)
t[i] = t[i-1]+t[i-2]
return t[i]
end
})
print(fibotable[11])