Learn Luau through engine code
Outcome and prerequisites
You can already navigate Explorer and run a Script. This lesson gives you enough Luau to read the course examples without translating every line. It is a focused orientation, not a language reference.
Luau is derived from Lua 5.1 and adds features including optional static types. Studio provides completion, diagnostics and API information. A language type checker and the engine runtime solve different problems: neither eliminates the need to test game behavior.
Translate familiar concepts carefully
Use local for variables unless a global is intentional. A table can represent a sequence or a key/value record. Numeric sequences conventionally begin at 1; do not mechanically translate zero-based indexing. nil represents an absent value and removes a table entry when assigned to that key. Only false and nil are false-like; zero and the empty string are true-like.
Use == for equality, ~= for inequality and .. for string concatenation. Blocks close with end; indentation communicates structure but does not delimit a block. Functions are values and can close over local variables. -- begins a comment.
A colon call such as part:GetAttribute("Index") passes part as the receiver. It is not interchangeable with part.GetAttribute("Index"). This is a common source of confusing argument errors when arriving from another language.
Run a compact language exercise
Create ServerScriptService.LuauTour as a Script in a disposable Baseplate. Replace the entire default body. The --!strict directive asks Studio's analyzer for stricter checking; it is not a runtime security mechanism.
-- Place: ServerScriptService.LuauTour (Script)
--!strict
type Checkpoint = {name: string, height: number}
local checkpoints: {Checkpoint} = {
{name = "Start", height = 1},
{name = "Bridge", height = 3},
{name = "Finish", height = 5},
}
local function totalHeight(items: {Checkpoint}): number
local total = 0
for _, checkpoint in items do
total += checkpoint.height
end
return total
end
for index, checkpoint in checkpoints do
print(index, checkpoint.name, checkpoint.height)
end
print("Total", totalHeight(checkpoints))
local origin = Vector3.new(0, 1, 0)
local destination = Vector3.new(12, 2, 0)
print("Distance", (destination - origin).Magnitude)
assert(totalHeight(checkpoints) == 9, "Unexpected total")
Download: luau-tour.luau
Expected output includes three indexed records, Total 9, and a distance a little over 12. The type declaration describes a record; the braces around the type describe a sequence. Vector3 is an engine datatype, not an ordinary Luau table. Its Magnitude is a property.
Change a height to a string and inspect Studio's diagnostic. Restore the number, then intentionally change the expected assertion to 10. Run again and inspect the runtime error. These are different feedback loops: static analysis sees some mistakes before execution; assertions check assumptions during execution.
References, mutation and engine objects
Assigning a table or Instance to another variable does not make a deep copy. If two variables refer to the same table, a mutation through one is visible through the other. Use an explicit copying strategy when you actually need independent data. Avoid copying a whole hierarchy simply to change one scalar setting.
Instance.new("Part") constructs an object. Set Name, properties and then Parent to put it in the tree. Destroy() removes an instance and associated resources; setting a local variable to nil only drops that local reference. The engine object and the local reference have different lifetimes.
Not every lookup should wait. FindFirstChild returns an object or nil immediately. WaitForChild can yield until a child appears; a misspelled name can therefore look like a hung script. During development, use clear assertions or a timeout when absence should be treated as an error.
Scheduling and errors
Some operations yield, allowing other work to run before the next line continues. For timing use task.wait rather than a busy loop. Use pcall around fallible external operations such as data-store calls; do not wrap an entire game in a silent catch that hides programming mistakes.
For a coin award, checking eligibility and marking the coin consumed should happen without a yield between them. For a save, the network call can fail and needs an explicit policy. Those differences become concrete in collectibles and saving.
Troubleshooting and exercise
The error mentions self or an unexpected argument: check colon versus dot. A loop skips entries: check whether you have a sequence or a sparse dictionary. A condition accepts 0: that is normal Luau truthiness. An object is nil: check the tree and whether it exists at that moment.
Add a function that returns the highest checkpoint, returning nil for an empty list. State the return type you chose and test both a populated and empty table. Delete LuauTour after the experiment so its diagnostic output does not obscure later lessons.
Sources and navigation
Verified 2026-10-03: Roblox Luau overview, Luau syntax, Luau types, Instance API, task library.
Previous: Data model · Next: Events · Related: Modules