Verification: Static review complete; Studio/device tests unverifiedLast verified: 2026-10-03Prerequisites: Read the Studio data model

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

Progress and help notes

Nothing is sent automatically. Copying happens only when you press the button.

Sources