3. Functions

A function is a piece of code with a name. You write it once and run it from anywhere, as many times as you like.

You have used one already: print is a function.

Writing one

local function greet()
	print("Welcome back!")
end

greet()    --> Welcome back!
greet()    --> Welcome back!

The first part defines the function. Nothing happens until it is called, with its name and a pair of brackets.

Arguments

A function can take values to work with. They are named in the brackets:

local function greet(name)
	print("Welcome back, " .. name .. "!")
end

greet("Mira")    --> Welcome back, Mira!
greet("Tov")     --> Welcome back, Tov!

name is a local variable that exists only inside the function. It gets whatever the caller passed.

Several arguments are separated by commas:

local function damage(health, amount)
	print("Health is now " .. health - amount)
end

damage(100, 30)    --> Health is now 70

An argument the caller leaves out is nil. or gives it a default:

local function spawn(kind, count)
	count = count or 1
	print("Spawning " .. count .. " " .. kind)
end

spawn("slime", 3)    --> Spawning 3 slime
spawn("dragon")      --> Spawning 1 dragon

Results

return hands a value back to the caller, and ends the function.

local function square(n)
	return n * n
end

local area = square(6)
print(area)             --> 36
print(square(3) + 1)    --> 10

A function that returns a value can be used anywhere a value can.

return also leaves a function early. This is a common shape: deal with the cases that need nothing done, then do the work.

local function heal(health, amount)
	if health <= 0 then
		return 0    -- dead stays dead
	end
	return math.min(health + amount, 100)
end

print(heal(0, 50))     --> 0
print(heal(80, 50))    --> 100

More than one result

A function can return several values, separated by commas:

local function divide(a, b)
	return a // b, a % b
end

local whole, rest = divide(17, 5)
print(whole, rest)    --> 3   2

Engine functions use this. Input.GetMouseDelta() returns two numbers, how far the mouse moved across and down:

-- file: example
local dx, dy = Input.GetMouseDelta()

Functions that come with Lua

Lua's math and string libraries are there in every script.

print(math.max(3, 9, 4))      --> 9
print(math.min(3, 9, 4))      --> 3
print(math.floor(3.7))        --> 3
print(math.abs(-5))           --> 5
print(math.random(1, 6))      -- a whole number from 1 to 6
print(string.upper("go"))     --> GO
print(string.format("%.1f seconds", 12.3456))   --> 12.3 seconds

string.format is the tidy way to show a number with a fixed count of decimals, for a timer on screen.

The engine adds a few to math that games want constantly:

-- file: example
math.clamp(value, 0, 100)      -- value, kept between 0 and 100
math.lerp(a, b, 0.5)           -- halfway from a to b

Functions are values

A function can be stored in a variable and passed to another function, like any value. The two ways of writing one mean the same:

local function double(n)
	return n * 2
end

local triple = function(n)
	return n * 3
end

print(double(5), triple(5))    --> 10   15

This matters because the engine often asks for a function to run later. A timer takes one:

-- file: example
Timer.After(2, function()
	print("Two seconds have passed")
end)

The function has no name. It is written right where it is needed and handed to Timer.After, which keeps it and calls it when the time comes.

Remembering what was around

A function can use the local variables that were in sight where it was written, even after that place has finished running:

local function makeCounter()
	local count = 0
	return function()
		count = count + 1
		return count
	end
end

local nextId = makeCounter()
print(nextId())    --> 1
print(nextId())    --> 2
print(nextId())    --> 3

count lives on inside the returned function. This is called a closure, and it is why the timer above can use variables from the code around it.

Where a variable can be seen

A local exists from the line that makes it to the end of the block it is in: the function, loop or if.

local function test()
	local inside = 5
	if inside > 3 then
		local deeper = 10
		print(inside + deeper)    --> 15
	end
	print(deeper)    --> nil: it ended with the if
end

test()

Because of this, a local function must be written above the code that calls it.

Try it

  1. Write isEven(n) that returns true for an even number. Use it in a loop to print the even numbers up to 10.
  2. Write clampHealth(health) that returns the health kept between 0 and 100, without using math.clamp.
  3. Write stats(a, b, c) that returns the smallest and the largest of three numbers, and print both.

Next: tables.