Roblox Luau Tutorial: Understanding Vector3
Learn what a Vector3 actually represents, how Roblox's 3D coordinate system is laid out, and how to add, scale, measure, and combine vectors to move objects and calculate distances and directions in your own scripts.
1. What is a Vector3?
A Vector3 is just three numbers — X, Y, and Z — bundled together. Roblox uses it anywhere a value needs three dimensions:
- Position — where something is in the world.
- Size — how big a Part is along each axis.
- Velocity — how fast, and in which direction, something is moving.
- Direction — a "pointing arrow," often with a length of exactly 1.
Roblox's Axis Convention
Roblox is a right-handed, Y-up coordinate system. That means:
+Y (Up)
|
|
|______ +X (Right / East)
/
/
+Z (Toward the default camera / South)
- +X points right, -X points left.
- +Y points up, -Y points down.
- +Z points toward you (south), -Z points away from you (north) — this trips people up constantly, so remember it now: "forward" in Roblox is usually -Z, not +Z.
2. Setting Up the Scene in Roblox Studio
- Insert a Part:
- Press
Ctrl + I(Windows) orCmd + I(Mac) to open the Insert Object menu. - Search for Part and select it.
- Press
- Insert a Script inside the Part:
- With the
Partselected, pressCtrl + I/Cmd + Iagain. - Search for Script and place it inside the
Part.
- With the
3. Creating Vector3 Values
Vector3.new(x, y, z) builds one from three numbers. Every argument defaults to 0 on its own, so you can omit trailing ones:
local origin = Vector3.new() -- (0, 0, 0)
local partial = Vector3.new(5, 10) -- (5, 10, 0) - z defaults to 0
local full = Vector3.new(5, 10, -20) -- (5, 10, -20)
Roblox also ships a handful of ready-made constants for the values you'll reach for constantly:
local origin = Vector3.zero -- Vector3.new(0, 0, 0)
local up = Vector3.yAxis -- Vector3.new(0, 1, 0)
local uniform = Vector3.one -- Vector3.new(1, 1, 1)
local doubleSize = Vector3.one * 4 -- Vector3.new(4, 4, 4)
4. Using Vector3 for Position and Size
Position and Size are both plain Vector3 properties on a Part:
local part = script.Parent
-- Move the part to (10 studs right, 5 studs up, 0)
part.Position = Vector3.new(10, 5, 0)
-- Resize it to 4x2x4 studs (width, height, depth)
part.Size = Vector3.new(4, 2, 4)
Position is actually a shortcut - under the hood every Part's real transform is a CFrame (position and rotation together), and Position just reads/writes the position part of it. You'll meet CFrame in its own tutorial once you're comfortable here.
5. Vector3 Arithmetic - Moving and Combining Positions
Vector3 supports +, -, and scalar *//, and they do exactly what you'd hope:
local part = script.Parent
-- Move the part 5 studs straight up, relative to where it already is
part.Position = part.Position + Vector3.new(0, 5, 0)
-- Move it 3 studs back (toward +Z) and shrink the offset by half
local offset = Vector3.new(0, 0, 3) / 2
part.Position = part.Position + offset
-- The midpoint between two positions - just average them
local pointA = Vector3.new(0, 0, 0)
local pointB = Vector3.new(10, 0, 10)
local midpoint = (pointA + pointB) / 2
The key habit to build: current + offset moves relative to where you are. Setting part.Position = Vector3.new(0, 5, 0) outright teleports to that exact world position instead - both are useful, but they're not the same thing.
6. Distance and Direction - Magnitude and Unit
Subtracting two positions gives you a vector that points from one to the other. Its length (Magnitude) is the distance between them, and normalizing it (Unit) gives you a pure direction with a length of exactly 1:
local partA = workspace.PartA
local partB = workspace.PartB
local offset = partB.Position - partA.Position
local distance = offset.Magnitude -- how far apart they are, in studs
local direction = offset.Unit -- which way to travel to go from A to B
print(("PartB is %.1f studs from PartA"):format(distance))
-- Nudge PartA one stud closer to PartB
partA.Position = partA.Position + direction * 1
This direction * distance pattern (or a fraction of it, like direction * 1 above) is how you move something toward a target step by step, rather than jumping straight there.
7. Dot and Cross Products - What They're Actually For
These two look intimidating in math class, but each has one job you'll use constantly.
:Dot(other) returns a single number that tells you how aligned two directions are:
- Positive → pointing roughly the same way.
- Zero → perpendicular (90° apart).
- Negative → pointing roughly opposite ways.
local facing = Vector3.new(0, 0, -1) -- facing "forward" (-Z)
local toTarget = (target.Position - part.Position).Unit
local alignment = facing:Dot(toTarget)
if alignment > 0 then
print("The target is roughly ahead")
else
print("The target is roughly behind")
end
:Cross(other) returns a new vector that's perpendicular to both inputs - useful for finding a "sideways" or "up" direction from two other directions:
local forward = Vector3.new(0, 0, -1)
local up = Vector3.new(0, 1, 0)
local right = forward:Cross(up) -- the direction 90° to both - "right" relative to forward/up
8. Smooth Movement with Lerp
:Lerp(goal, alpha) blends between two vectors. alpha is 0 to 1, where 0 gives you back the start, 1 gives you the goal, and anything in between gives you a point along the straight line connecting them:
local part = script.Parent
local startPosition = part.Position
local goalPosition = startPosition + Vector3.new(20, 0, 0)
for i = 0, 1, 0.05 do
part.Position = startPosition:Lerp(goalPosition, i)
task.wait(0.03)
end
Run that and the part glides smoothly from its start position to 20 studs away, instead of teleporting.
9. Putting It Together - Moving Toward a Target
This combines direction, distance, and a loop into one useful pattern: move toward a target every frame until you're close enough to stop.
local part = script.Parent
local target = workspace:WaitForChild("Target")
local speed = 8 -- studs per second
local stopDistance = 1
game:GetService("RunService").Heartbeat:Connect(function(deltaTime)
local offset = target.Position - part.Position
local distance = offset.Magnitude
if distance <= stopDistance then
return -- close enough, stop moving
end
local direction = offset.Unit
part.Position = part.Position + direction * speed * deltaTime
end)
deltaTime (the time since the last frame) keeps the movement speed consistent no matter how fast or slow the game is running - multiplying by it is what turns "studs per second" into "studs this frame."
10. Try It Yourself
The CFrame Viewer tool lets you type Vector3 expressions like the ones above and see them drawn live as arrows in 3D, with sliders on every number - a fast way to build real intuition before moving on to the CFrame tutorial.
11. Verification / Testing
- Insert a Part named
Targetsomewhere else inWorkspace(no script needed on it). - Paste the Section 9 script into a Script inside your original
Part. - Click Run in Roblox Studio.
- Watch the Explorer/viewport:
- The Part should glide smoothly toward
Targetand stop about 1 stud away, rather than snapping there instantly.
- The Part should glide smoothly toward
- Try the Section 6 distance snippet in the Command Bar (
View > Command Bar) with two Parts selected to sanity-check theMagnitude/Unitnumbers you're seeing.