01 Get Roblox Studio +
Roblox Studio is free on PC and Mac. Grab it from create.roblox.com (Start Creating → download), sign in with your Roblox account, and open it.
On the New screen, pick a template. Start with Baseplate — it's an empty flat world, perfect for messing around without anything getting in the way.
02 The interface — four windows that matter +
Studio throws a lot at you, but as a beginner you mostly live in four panels:
Explorer
The tree of everything in your game. Workspace holds the 3D world; parts, models, and scripts all sit somewhere in here. If it's hidden: View → Explorer.
Properties
Click anything in the Explorer and its settings show here — size, color, position, and dozens more. This is where you tweak stuff by hand.
Toolbox
Free models, images, and sounds other people made. Handy, but drop things in carefully — some free models carry junk scripts.
Output
View → Output. This is where your code's print() messages and errors appear. Keep it open the moment you start scripting.
03 Your first Part +
A Part is a single building block — a brick. On the Home or Model tab, click Part to drop one into the world.
- Move / Scale / Rotate — the tools on the Model tab (or press R, T handles). Drag the arrows to position it.
- Select the Part and look at Properties. Three to know right away:
Anchored
When true, the part is frozen in place and won't fall or get pushed. Turn this on for floors and platforms.
Color / Material
Repaint it and change what it's "made of" (Plastic, Neon, Wood…).
Transparency
0 is solid, 1 is invisible. 0.5 is see-through.
04 Scripts & where they live +
Code goes in scripts, and scripts sit in the Explorer. There are three kinds, and putting them in the right place matters:
Script (server)
Runs on the server — the shared game everyone is in. Put these in ServerScriptService (or on a part in the Workspace). Use for game rules, spawning, scoring.
LocalScript (client)
Runs on one player's device. Put these in client spots like StarterPlayerScripts, StarterGui, or StarterPack. Use for UI, camera, input.
ModuleScript
Doesn't run on its own — it holds reusable code that other scripts require(). Great once you start repeating yourself.
To add one: hover a container in the Explorer, click +, and choose Script. A new script opens with print("Hello world!") already in it.
05 Luau basics — variables & Output +
Luau is Roblox's language (a fast, safe version of Lua). A variable stores a value — always start it with local:
local health = 100 -- a number
local playerName = "Alice" -- a string (text)
local isAlive = true -- a boolean (true/false)
print(playerName) -- shows "Alice" in Output
Press Play (or run the script) and watch the Output window. print() is how you check what your code is doing — you'll use it constantly.
06 Change a Part with code +
You reach into the game with dot notation — walk down the Explorer tree with dots. Put a Part named Part in the Workspace, then in a Script:
local part = game.Workspace.Part
part.Anchored = true
part.Transparency = 0.5
part.Color = Color3.fromRGB(255, 77, 46) -- R,G,B 0–255
Use Color3.fromRGB(255, 77, 46) for normal 0–255 colors. There's also Color3.new(1, 0.3, 0.18), but that one takes values from 0 to 1 — mixing them up is the classic beginner bug.
07 Functions & events +
A function is a reusable block of code:
local function takeDamage(amount)
health = health - amount
print("Health: " .. health) -- .. joins text
end
takeDamage(10)
An event lets the game call your function when something happens. The classic one is Touched — fires when something bumps a part:
local part = game.Workspace.Part
part.Touched:Connect(function(hit)
part.Color = Color3.fromRGB(72, 202, 228) -- turn cyan when touched
end)
:Connect(...) means "when this event fires, run this function." That single pattern powers a huge amount of Roblox games.
08 If statements & loops +
If makes decisions:
if health <= 0 then
print("You lost!")
elseif health < 50 then
print("Low health!")
else
print("Healthy")
end
Loops repeat things:
-- count 1 to 5
for i = 1, 5 do
print(i)
end
-- keep going while a condition holds
while health > 0 do
takeDamage(1)
wait(1) -- pause 1 second so it doesn't freeze
end
A while loop with no wait() can freeze Studio. Always give long loops a small pause.
09 Test & publish +
- Test: hit the Play button (or F5) to drop your avatar into the game and try it. Shift+F5 stops.
- Save vs publish: File → Publish to Roblox puts it online so friends can play. Give it a name, set it to public, and share the link.
- Iterate in small steps: change one thing, press Play, check Output, repeat. That loop is the whole job.
10 Leaderstats & saving data +
leaderstats is a magic name: make a Folder called exactly leaderstats inside a player, drop IntValue/StringValue children in it, and Roblox shows them on the in-game leaderboard automatically. Use a server Script in ServerScriptService:
local Players = game:GetService("Players")
Players.PlayerAdded:Connect(function(player)
local stats = Instance.new("Folder")
stats.Name = "leaderstats" -- must be this exact name
stats.Parent = player
local coins = Instance.new("IntValue")
coins.Name = "Coins"
coins.Parent = stats
end)
To make it stick between sessions, save with a DataStore. First turn it on: File → Experience Settings → Security → Enable Studio Access to API Services. Then, in a server Script:
local DataStoreService = game:GetService("DataStoreService")
local store = DataStoreService:GetDataStore("PlayerData")
-- load (after you make the leaderstats)
local ok, saved = pcall(function()
return store:GetAsync("User_" .. player.UserId)
end)
if ok and saved then coins.Value = saved end
-- save when they leave
Players.PlayerRemoving:Connect(function(player)
pcall(function()
store:SetAsync("User_" .. player.UserId, player.leaderstats.Coins.Value)
end)
end)
Always wrap GetAsync/SetAsync in pcall() (the network can fail), and add a game:BindToClose() save for server shutdowns. DataStores only work from a server Script, never a LocalScript.
11 Make a simple GUI +
On-screen menus live in StarterGui — each player gets their own copy. The nesting is ScreenGui → Frame → TextLabel / TextButton. You can build it by clicking (Insert → ScreenGui, then add a TextButton) or in a LocalScript:
-- LocalScript inside StarterGui
local gui = Instance.new("ScreenGui")
gui.Parent = script.Parent
local btn = Instance.new("TextButton")
btn.Size = UDim2.new(0, 200, 0, 50) -- 200 x 50 pixels
btn.Position = UDim2.new(0.5, -100, 0.5, -25) -- centered
btn.Text = "Click me"
btn.Parent = gui
btn.MouseButton1Click:Connect(function()
btn.Text = "Clicked!"
end)
UDim2.new(xScale, xOffset, yScale, yOffset) — scale is a fraction of the screen (0.5 = halfway), offset is exact pixels. Mixing them (like 0.5, -100) is how you center things on any screen size.
12 Tweening & animation +
TweenService smoothly slides a property from its current value to a goal over time — no loops needed. Three parts: the instance, a TweenInfo (duration + easing), and a table of goals.
local TweenService = game:GetService("TweenService")
local part = workspace.Part
local info = TweenInfo.new(1.5, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
local goal = { Position = Vector3.new(0, 10, 0), Transparency = 0.5 }
local tween = TweenService:Create(part, info, goal)
tween:Play()
It works on almost anything with a number/Color/Vector/UDim2 property — so you can tween a GUI Frame's Position, Size, or BackgroundColor3 the exact same way. Control it with :Play(), :Pause(), and :Cancel().
13 RemoteEvents — client ↔ server +
The client (a LocalScript) and the server (a Script) are separate and can't touch each other's variables directly. A RemoteEvent passes messages between them. Put the RemoteEvent in ReplicatedStorage so both sides can see it.
Client → server — the client calls FireServer(); the server listens with OnServerEvent (its first argument is always the player):
-- LocalScript (client)
local rs = game:GetService("ReplicatedStorage")
rs.GiveCoin:FireServer()
-- Script (server)
local rs = game:GetService("ReplicatedStorage")
rs.GiveCoin.OnServerEvent:Connect(function(player)
player.leaderstats.Coins.Value += 1
end)
Server → client — the server calls FireClient(player) or FireAllClients(); the client listens with OnClientEvent.
A hacked client can fire any RemoteEvent it wants. The server is the source of truth: before you hand out that coin, check the request is legit (did they really pick it up? is the cooldown done?). Never just do what the client asks.
14 ModuleScripts & reusable code +
Once you catch yourself copy-pasting the same code, it's time for a ModuleScript. It doesn't run on its own — it returns a table (usually of functions) that other scripts load with require(). Store it in ReplicatedStorage so both server and client can use it.
-- ModuleScript in ReplicatedStorage, named "Utils"
local Utils = {}
function Utils.double(n)
return n * 2
end
return Utils
-- any Script or LocalScript
local rs = game:GetService("ReplicatedStorage")
local Utils = require(rs:WaitForChild("Utils"))
print(Utils.double(5)) --> 10
Write a thing once, use it everywhere. require() runs the module once and caches it, so every script shares the same copy — great for shared settings and helper functions.
15 Sounds & particle effects +
Sound: put a Sound inside a part (so it plays from that spot) or in workspace for everywhere, point its SoundId at an asset, and :Play() it.
local sound = Instance.new("Sound")
sound.SoundId = "rbxassetid://9120386436" -- an audio asset id
sound.Parent = workspace.Part
sound:Play()
Particles: a ParticleEmitter inside a part sprays particles while Enabled, or you can fire a one-off burst with :Emit().
local fx = Instance.new("ParticleEmitter")
fx.Rate = 20
fx.Lifetime = NumberRange.new(1, 2)
fx.Parent = workspace.Part
fx:Emit(15) -- a single burst of 15
16 Character animations +
Every character has a Humanoid, and animations play through its Animator. You make the animation in Studio's Animation Editor (pose a rig across keyframes, then export — that gives you an AnimationId). Then load and play it:
local anim = Instance.new("Animation")
anim.AnimationId = "rbxassetid://507771019" -- your exported animation
local humanoid = character:WaitForChild("Humanoid")
local animator = humanoid:WaitForChild("Animator")
local track = animator:LoadAnimation(anim)
track:Play()
You can only publish animations to a rig you own the rights to, and the AnimationId only works once you've exported it from the Animation Editor.
17 Monetization — passes & products +
Selling things runs through MarketplaceService. Two kinds:
- Game pass — a one-time, permanent purchase (VIP, a special tool).
- Developer product — bought repeatedly (coins, potions, revives).
Create either on the Creator Hub → Monetization first to get its ID. For a game pass, check ownership and prompt a purchase:
local Marketplace = game:GetService("MarketplaceService")
local PASS_ID = 00000000
-- does the player already own it?
local ok, owns = pcall(function()
return Marketplace:UserOwnsGamePassAsync(player.UserId, PASS_ID)
end)
if ok and owns then
-- give the VIP perk
end
-- offer to buy it
Marketplace:PromptGamePassPurchase(player, PASS_ID)
Check ownership on the server. Developer products are granted in MarketplaceService.ProcessReceipt, which both hands over the item and confirms the sale — never trust the client for purchases.
Guide complete ✓
That's the whole toolkit.
Setup, scripting, saving data, UI, animation, remotes, reusable code and monetization — enough to build and ship a real Roblox game. Now go make something and show a friend.