Table of Contents
Understanding Scope in Lua
Scope in Lua describes where a variable exists in your code and where you are allowed to use it. When you understand scope, you avoid mysterious bugs like variables being nil when you expect a value, or one part of your script accidentally changing data that belongs to another part.
Roblox Lua uses two main kinds of scope. One is global scope, which can be seen from almost anywhere in the script. The other is local scope, which is limited to a specific area, such as inside a function or a block of code.
Important rule: In Roblox Lua, always prefer local variables unless you have a very specific reason not to.
Global Scope
A global variable is created when you assign to a name without using the word local. Global variables live in the global environment of the script. Any code in that same environment can read and change them, as long as it runs after the variable is created.
For example:
GameName = "My Obby"
function PrintGameName()
print(GameName)
end
PrintGameName()
Here, GameName is global. The function PrintGameName can see it, because both the function and the variable live in the same global environment. If another part of the script changes GameName, every place that uses GameName will see the new value.
This can be useful, but it also means global variables can be changed by accident. If you reuse a common name such as count in different parts of your script as a global variable, they will all point to the same value, which often leads to bugs.
Local Scope
A local variable is declared with the word local. It only exists in a certain region of your code. That region might be the whole file, a function, or a smaller block, depending on where you write local.
Here is a simple example:
local GameName = "My Obby"
function PrintGameName()
print(GameName)
end
PrintGameName()
Now GameName is local to the entire script file, but not global. Code inside PrintGameName can still see it, because functions can access locals that were already declared when the function was defined.
If you try to use a local variable before it is declared, Lua does not see it. The position of the local line matters.
Rule: A local variable is only visible from the line after it is declared up to the end of its scope block.
So, if you write:
print(x) -- x is nil here, it does not exist yet
local x = 10
print(x) -- x is 10 here
The first print shows nil, because x does not exist until the local x line.
Function Scope
Every function creates its own scope. Parameters and local variables inside a function exist only while that function is running. They are not visible outside the function.
For example:
function Add(a, b)
local result = a + b
return result
end
print(Add(2, 3)) -- prints 5
print(result) -- error: result is nil here
The variable result is local to the function Add. Outside the function, result does not exist. If you try to print it outside the function, you only get nil.
Function parameters like a and b are also local variables. They exist only inside the function and are not known in the outer code.
This is useful when you build game logic. For instance, a function that handles giving coins to a player should keep its internal calculations inside local variables, so other parts of the script do not accidentally change them.
Block Scope
Lua also has block scope. A block is any chunk of code that starts with a keyword like do, if, for, or while and ends with end. Variables declared local inside a block are visible only inside that block and vanish when Lua leaves it.
Here is a do block:
do
local x = 5
print(x) -- prints 5
end
print(x) -- x is nil here
Inside the block, x exists. After end, x is gone. This pattern is often used to keep temporary variables from leaking into the rest of the script.
In an if block you can do something similar:
local health = 15
if health <= 20 then
local warning = "Low health!"
print(warning)
end
print(warning) -- warning is nil here
The variable warning exists only while Lua executes the if block.
for loops also have their own scope for loop variables:
for i = 1, 3 do
print(i)
end
print(i) -- i is nil here
The loop variable i is local to the loop. After the loop ends, i no longer exists.
Shadowing Variables
When you declare a local variable with the same name as a variable in an outer scope, the inner one hides the outer one inside its region. This is called shadowing.
Example:
local score = 0
function Test()
local score = 10
print(score) -- prints 10
end
Test()
print(score) -- prints 0
Inside Test, score refers to the inner local value 10. Outside the function, score refers to the outer value 0.
Shadowing can be confusing if you do it by accident, because you might think you are changing the outer variable but you are really only changing the inner one.
Rule: Avoid reusing the same variable name in nested scopes unless you are very sure what you are doing.
Scope and Functions Defined Inside Blocks
You can define functions inside blocks. Such functions can see local variables from outer scopes, as long as those locals were declared before the function definition.
For example:
local bonus = 5
function MakeAdder()
local base = 10
local function AddBonus(x)
return x + base + bonus
end
return AddBonus
end
local func = MakeAdder()
print(func(3)) -- prints 18
Here, AddBonus can see base and bonus. Both are outside the function, but inside scopes that were active when the function was created. This pattern is powerful, but for beginners it is enough to remember that a function can use local variables that were defined earlier in the same outer scope.
Scope and Script Files in Roblox
Each Script or LocalScript in Roblox has its own environment. A global variable in one Script is not automatically visible in another Script. If you write:
-- Script A
GameName = "My Obby"and in another Script:
-- Script B
print(GameName)
the second script does not see the GameName from Script A. From its point of view, GameName is nil. So even though GameName is global inside Script A, it is not a universal global across all scripts.
This is one more reason to rely on local variables, and to communicate between scripts using Roblox systems such as BindableEvents, RemoteEvents, or shared objects in ReplicatedStorage, which are introduced in other chapters.
Good Habits for Scope in Roblox Lua
In most Roblox scripts you should start by declaring your main references as local variables at the top of the file. For example, if you work with the Workspace or Players services, create local references one time and reuse them.
For instance:
local Players = game:GetService("Players")
local Workspace = game.Workspace
local function OnPlayerAdded(player)
local character = player.Character or player.CharacterAdded:Wait()
-- use character here
end
Players.PlayerAdded:Connect(OnPlayerAdded)
Here, Players and Workspace are local to the whole script. The function OnPlayerAdded can see them, because they are in an outer scope. Inside the function, player and character are local. They are not visible outside OnPlayerAdded.
This style keeps your variables tightly controlled and reduces naming conflicts. It also makes your script easier to read, because you can quickly see which values are meant to be shared and which ones are temporary.
Summary of rules:
- Use
localfor almost every variable. - Remember that a local variable only exists from its declaration line to the end of its scope block.
- Each function and block creates a new scope for local variables.
- Avoid accidental shadowing by reusing the same variable name in nested scopes.
When you know how scope works, you can structure your Lua code in a clear way, keep your game logic separate, and avoid many common errors in Roblox scripting.