atherhubather.hub
Back to Guides
Tutorial
9 min read
May 11, 2026

Your First Roblox Script

This walkthrough takes you from an empty Roblox Studio project to a published place with a real, working feature. We'll build a part that gives the player coins when they touch it — simple, but it covers every concept you'll use in every script after this one: instances, events, server scripts, and updating player state.

Ather
Ather
Lead developer at Atherhub. Writes about Roblox internals, Luau, script engineering, and platform security.Last updated May 11, 2026

Step 1: Open Studio and create a baseplate

Launch Roblox Studio and pick the Baseplate template. You'll see a flat grey ground (the baseplate), a sky, and a spawn point. The Explorer pane on the right shows the data model tree — Workspace,Players, ReplicatedStorage, and friends.

If the Explorer or Properties panes aren't visible, find them under the View tab of the ribbon. You'll use both constantly.

Step 2: Place a coin part

In the Home tab, click Part to spawn a brick in front of the camera. Rename it Coin in the Explorer.

In the Properties pane, set:

  • BrickColor → Bright yellow
  • Material → Neon
  • Size2, 2, 2
  • Anchored → true (so it doesn't fall)
  • CanCollide → false (the player can walk through it)

The coin is now a glowing yellow cube sitting in the world. You can drag it around with the Move tool to place it near the spawn.

Step 3: Add a Script to the coin

In the Explorer, hover over the Coin part and click the "+" that appears. Pick Script. A new script opens in the script editor with a one-line placeholder. Delete it and paste:

luau
local coin = script.Parent
local Players = game:GetService("Players")
local debounce = {}

coin.Touched:Connect(function(otherPart)
    local character = otherPart.Parent
    local humanoid = character:FindFirstChildOfClass("Humanoid")
    if not humanoid then return end

    local player = Players:GetPlayerFromCharacter(character)
    if not player then return end

    -- Debounce: only credit once per second per player
    if debounce[player] and tick() - debounce[player] < 1 then return end
    debounce[player] = tick()

    print(player.Name .. " picked up the coin!")
end)

Press Play in the ribbon. Your avatar spawns; walk into the coin and watch the Output pane (View → Output if it's hidden). You should see "YourName picked up the coin!".

That's your first script working. Now let's make it do something visible.

Step 4: Give the player a coin counter

Roblox players have a child folder called leaderstats that, by convention, appears in the top-right leaderboard. We'll create one and add a Coins counter.

In ServerScriptService, add a new Script (right- click the service → Insert Object → Script) called SetupLeaderstats with:

luau
local Players = game:GetService("Players")

Players.PlayerAdded:Connect(function(player)
    local stats = Instance.new("Folder")
    stats.Name = "leaderstats"
    stats.Parent = player

    local coins = Instance.new("IntValue")
    coins.Name = "Coins"
    coins.Value = 0
    coins.Parent = stats
end)

Now modify the coin script in Workspace to actually award the coin:

luau
local coin = script.Parent
local Players = game:GetService("Players")
local debounce = {}

coin.Touched:Connect(function(otherPart)
    local character = otherPart.Parent
    local humanoid = character:FindFirstChildOfClass("Humanoid")
    if not humanoid then return end

    local player = Players:GetPlayerFromCharacter(character)
    if not player then return end

    if debounce[player] and tick() - debounce[player] < 1 then return end
    debounce[player] = tick()

    local stats = player:FindFirstChild("leaderstats")
    local coinsValue = stats and stats:FindFirstChild("Coins")
    if coinsValue then
        coinsValue.Value += 1
    end
end)

Press Play again. Touch the coin — your Coins counter in the top-right increments to 1. Walk away, walk back, get another.

Step 5: Make the coin react

A coin that doesn't change after being picked up feels dead. Let's make it briefly hide and respawn.

luau
local coin = script.Parent
local Players = game:GetService("Players")
local debounce = false

coin.Touched:Connect(function(otherPart)
    if debounce then return end
    local player = Players:GetPlayerFromCharacter(otherPart.Parent)
    if not player then return end

    local stats = player:FindFirstChild("leaderstats")
    local coinsValue = stats and stats:FindFirstChild("Coins")
    if not coinsValue then return end

    debounce = true
    coinsValue.Value += 1

    coin.Transparency = 1
    coin.CanTouch = false

    task.wait(3)

    coin.Transparency = 0
    coin.CanTouch = true
    debounce = false
end)

A few things changed. The debounce flipped from a per-player table to a single boolean, because the whole coin is now unavailable while it's respawning. We use task.wait(3) instead of wait(3) — the new one is more predictable on the modern scheduler.

Press Play. Touch the coin, watch it vanish, wait three seconds, watch it return. That's a full feature loop.

Step 6: Save to a datastore (optional)

The coin count above only lives for one session — when the player leaves, it's gone. To persist it, add a DataStore. In ServerScriptService add another script:

luau
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local coinStore = DataStoreService:GetDataStore("CoinsV1")

Players.PlayerAdded:Connect(function(player)
    -- Wait for leaderstats setup above
    local stats = player:WaitForChild("leaderstats")
    local coins = stats:WaitForChild("Coins")

    local ok, saved = pcall(function() return coinStore:GetAsync(player.UserId) end)
    if ok and typeof(saved) == "number" then
        coins.Value = saved
    end
end)

Players.PlayerRemoving:Connect(function(player)
    local stats = player:FindFirstChild("leaderstats")
    local coins = stats and stats:FindFirstChild("Coins")
    if coins then
        pcall(function() coinStore:SetAsync(player.UserId, coins.Value) end)
    end
end)
DataStores need API enabled
DataStores don't work in Studio test sessions by default. Either enable Studio access in the Game Settings, or publish the game to test datastore code on the live server.

Step 7: Publish

File → Publish to Roblox. Pick a name and an icon, set the genre, mark it public when you're ready. Your game now has a roblox.com page and anyone with the link can play it.

You can keep editing in Studio and re-publishing as you go — changes go live within seconds of clicking publish. There's no separate "deploy" step.

Common beginner mistakes

  • Forgetting Anchored. Unanchored parts fall. You probably want most static parts to be anchored.
  • No debounce. .Touched can fire many times per second from multiple body parts. Always guard against re-entry.
  • LocalScript instead of Script. A LocalScript in Workspace doesn't run for any player. Use a regular Script for things you want to happen on the server.
  • Skipping nil checks. FindFirstChild can return nil. Always check before dereferencing.

What to try next

Concrete next steps once the coin works end-to-end:

  • Place ten coins by duplicating the part. The script lives on each part, so they all just work.
  • Add a sound. Drop a Sound instance into the coin and call :Play() on it when picked up.
  • Build a shop. Make a script that reads the player's coins and lets them buy something — a hat, a colour, a power.
  • Read the script types breakdown to understand server/client more deeply.

From here, every Roblox feature you build is just more of the same: place instances, connect events, update state. The patterns scale all the way to the largest games.

Ather
Written by Ather

Ather is the lead developer behind Atherhub. He's been writing Luau and Roblox tooling for the better part of a decade, with a focus on the messy interface between game-script internals and the platforms that host them. Have feedback on this article? Drop it in the Discord.