In short
Tag every coin part Coin. One Script in ServerScriptService creates a leaderstats folder with a Coins IntValue for each player, connects Touched on every tagged part, adds to Coins when a living player touches it, then hides the coin (Transparency = 1, CanTouch = false) and brings it back after a delay.
- The server adds the coins. A LocalScript that changes Coins only changes that player's own screen.
- A
collectedflag per coin stops one touch from paying several times: Touched fires once per touching body part. - Spin the coins in a LocalScript, so the server does not move dozens of parts every frame.
Get the script
Coin pickup script with respawn
One Script in ServerScriptService: every part tagged Coin adds to the Coins leaderstat when touched, hides, and comes back after 10 seconds.
-- Script in ServerScriptService named CoinPickups.
-- Every part tagged "Coin" gives coins when a player touches it, hides, then comes back.
local CollectionService = game:GetService("CollectionService")
local Players = game:GetService("Players")
local COIN_VALUE = 1 -- coins per pickup (a coin part can override it with a "Value" attribute)
local RESPAWN_SECONDS = 10 -- how long a collected coin stays hidden
-- leaderstats: the Coins column in the player list
Players.PlayerAdded:Connect(function(player: Player)
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
local coins = Instance.new("IntValue")
coins.Name = "Coins"
coins.Value = 0
coins.Parent = leaderstats
leaderstats.Parent = player
end)
local function addCoins(player: Player, amount: number)
local leaderstats = player:FindFirstChild("leaderstats")
local coins = leaderstats and leaderstats:FindFirstChild("Coins")
if coins and coins:IsA("IntValue") then
coins.Value += amount
end
end
local function setUpCoin(coin: Instance)
if not coin:IsA("BasePart") then
return
end
local collected = false
coin.Touched:Connect(function(hit: BasePart)
if collected then
return
end
local character = hit.Parent
if not (character and character:IsA("Model")) then
return
end
local player = Players:GetPlayerFromCharacter(character)
local humanoid = character:FindFirstChildOfClass("Humanoid")
if not (player and humanoid and humanoid.Health > 0) then
return
end
collected = true
local value = coin:GetAttribute("Value")
addCoins(player, if typeof(value) == "number" then value else COIN_VALUE)
-- Hide the coin for everyone, then bring it back
local oldTransparency = coin.Transparency
coin.Transparency = 1
coin.CanTouch = false
task.delay(RESPAWN_SECONDS, function()
coin.Transparency = oldTransparency
coin.CanTouch = true
collected = false
end)
end)
end
for _, coin in CollectionService:GetTagged("Coin") do
setUpCoin(coin)
end
CollectionService:GetInstanceAddedSignal("Coin"):Connect(setUpCoin)
RoCode builds it in the place you have open in Roblox Studio, on Windows or macOS with a desktop browser; on a phone the button sends the prompt and the link to your computer. Free to start, with free credits every day. More Roblox Studio scripts
Where to put it
ServerScriptService
CoinPickups Script new
Workspace
Coin Part (tag: Coin)
Settings you can change
COIN_VALUEIn the script- Coins per pickup. A coin part with a number attribute named
Valuepays that instead. Default:1. RESPAWN_SECONDSIn the script- How long a collected coin stays hidden before it can be collected again. Default:
10.
If another script already creates leaderstats (the leaderboard guide, for example), delete the PlayerAdded part of this one and keep a single source of the folder.
What a cheater can and cannot doA cheater can teleport their character onto every coin, so they collect faster than walking allows, but they cannot add coins without touching one: the server adds them. For a big economy, also rate-limit pickups per player on the server.
Type-checked with luau-lsp strict against Roblox's API definitions on 23 October 2026. Not playtested in Studio yet: press Play and watch the Output window before you publish.
Prompt for RoCode
Add collectable coins to my game: one Script in ServerScriptService that gives every player a Coins leaderstat, makes every part tagged Coin add 1 coin when a living player touches it, hides the coin for 10 seconds and then brings it back, plus a LocalScript that spins the coins.
On this page
How coin pickups work
A coin is any part with the Coin tag. The server watches each one for Touched. When a body part of a living player touches it, the server adds to that player's Coins and hides the coin for everyone. Changes the server makes, to the Coins value and to the coin's transparency, reach every player automatically.
| Piece | Where | Job |
|---|---|---|
| Coin parts | Workspace, tagged Coin | Anything round and gold: a Cylinder part turned on its side works. |
CoinPickups Script | ServerScriptService | Creates leaderstats, pays out, hides and respawns coins. |
CoinSpin LocalScript | StarterPlayerScripts | Spins every coin on each player's screen. |
Step 1: Make a coin and tag it
- Insert a Part, set its Shape to Cylinder, its size to about
0.5, 3, 3, its colour to gold and its Material to Neon or Metal. - Tick Anchored, and untick CanCollide so players walk through it instead of bumping into it. Leave CanTouch ticked.
- In the Properties window, under Tags, click + and type
Coin. - Duplicate it (Ctrl+D) and spread copies around the map. Tags are copied too.
- For a big coin worth more, add an attribute: Attributes, +, name
Value, type Number, value10.
Step 2: Add the pickup script
Hover over ServerScriptService, click +, insert a Script named CoinPickups and paste this:
-- Script in ServerScriptService named CoinPickups.
-- Every part tagged "Coin" gives coins when a player touches it, hides, then comes back.
local CollectionService = game:GetService("CollectionService")
local Players = game:GetService("Players")
local COIN_VALUE = 1 -- coins per pickup (a coin part can override it with a "Value" attribute)
local RESPAWN_SECONDS = 10 -- how long a collected coin stays hidden
-- leaderstats: the Coins column in the player list
Players.PlayerAdded:Connect(function(player: Player)
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
local coins = Instance.new("IntValue")
coins.Name = "Coins"
coins.Value = 0
coins.Parent = leaderstats
leaderstats.Parent = player
end)
local function addCoins(player: Player, amount: number)
local leaderstats = player:FindFirstChild("leaderstats")
local coins = leaderstats and leaderstats:FindFirstChild("Coins")
if coins and coins:IsA("IntValue") then
coins.Value += amount
end
end
local function setUpCoin(coin: Instance)
if not coin:IsA("BasePart") then
return
end
local collected = false
coin.Touched:Connect(function(hit: BasePart)
if collected then
return
end
local character = hit.Parent
if not (character and character:IsA("Model")) then
return
end
local player = Players:GetPlayerFromCharacter(character)
local humanoid = character:FindFirstChildOfClass("Humanoid")
if not (player and humanoid and humanoid.Health > 0) then
return
end
collected = true
local value = coin:GetAttribute("Value")
addCoins(player, if typeof(value) == "number" then value else COIN_VALUE)
-- Hide the coin for everyone, then bring it back
local oldTransparency = coin.Transparency
coin.Transparency = 1
coin.CanTouch = false
task.delay(RESPAWN_SECONDS, function()
coin.Transparency = oldTransparency
coin.CanTouch = true
collected = false
end)
end)
end
for _, coin in CollectionService:GetTagged("Coin") do
setUpCoin(coin)
end
CollectionService:GetInstanceAddedSignal("Coin"):Connect(setUpCoin)
A folder named exactly leaderstats inside a player makes Roblox show its values as columns in the player list, so Coins appears in the top-right corner. When a coin is touched, the script checks the part belongs to a living player's character, sets collected so the other body parts touching the same frame do not pay again, and adds the coin's value.
Hiding uses Transparency = 1 and CanTouch = false, which keeps the coin in place for its return. task.delay runs the respawn later without stopping the script.
Step 3: Spin the coins
A spinning coin is easier to spot. Spinning dozens of parts on the server every frame would send all those moves to every player, so this LocalScript spins them on each player's own screen instead. Insert it in StarterPlayer > StarterPlayerScripts.
-- LocalScript in StarterPlayerScripts named CoinSpin.
-- Spins every coin on each player's own screen, so the server does no work for it.
local CollectionService = game:GetService("CollectionService")
local RunService = game:GetService("RunService")
local SPIN_DEGREES_PER_SECOND = 90
RunService.RenderStepped:Connect(function(deltaTime: number)
local turn = CFrame.Angles(0, math.rad(SPIN_DEGREES_PER_SECOND * deltaTime), 0)
for _, coin in CollectionService:GetTagged("Coin") do
if coin:IsA("BasePart") and coin.Anchored then
coin.CFrame = coin.CFrame * turn
end
end
end)
A rotation a LocalScript makes stays on that device, which is fine here: the coin's spot is the same for everyone, only its turn differs.
Step 4: Save the coins
Coins reset when the player leaves until you save them. The DataStore guide saves leaderstats between sessions, and the leaderboard guide's saving step does the same for a Coins column. Once coins save, spend them in a shop or on eggs.
Test it
- Press Play. The player list shows Coins: 0.
- Walk through a coin. Coins goes up by 1 (or the coin's Value) and the coin disappears.
- Wait 10 seconds: the coin is back. Walk through it again.
- Start a 2-player test (Test tab, Clients and Servers, 2 players). A coin one player takes disappears for the other too.
Common errors and fixes
Most problems show in the Output window (Window menu, then Output).
Coins does not show in the player list
The folder must be named exactly leaderstats, all lower case, and be a child of the Player. Check two scripts are not both creating it.
Touching a coin does nothing
Check the tag is Coin with a capital C, the part's CanTouch is ticked and the script is a Script in ServerScriptService.
One coin gives several coins
Without the collected flag, each body part that touches pays again. Use the script as written.
The coins do not spin
CoinSpin must be a LocalScript in StarterPlayerScripts, and it only spins anchored coins.
Build it with RoCode
If you would rather not set this up by hand, RoCode can build it in the place you have open. RoCode is an independent AI agent for Roblox Studio, not made by Roblox: you describe the change in a web chat, and its Studio plugin creates the parts and scripts in your Explorer.
Scatter 30 spinning gold coins around my map that add to a Coins leaderstat, disappear when collected and come back after 10 seconds, and save Coins between sessions.
- Builds the coin parts, tags them and writes the pickup and spin scripts
- Reads your place first, so it reuses a leaderstats script you already have instead of making a second one
- Is told to compile-check each script it writes with Check Script, and sets undo points around each batch
After a build, RoCode can run its test robot: it plays your game in Studio, walks into parts and reports PASS or FAIL, for example walking onto a coin and checking Coins went up. It checks only what it chose to test, so play it yourself too. How RoCode connects to Studio.
Questions
How do I make a coin you can collect in Roblox?
Tag a part Coin and use one server Script that adds to a Coins leaderstat on Touched, then hides the part. Step 2 has the script.
Why do my coins only disappear for one player?
The coin was hidden by a LocalScript. Hide and pay out on the server, as CoinPickups does, so every player sees the same thing.
How do I make coins respawn?
Hide the coin with Transparency and CanTouch instead of destroying it, then turn them back with task.delay after a few seconds.
How do I make coins worth different amounts?
Give a coin a Number attribute named Value. The script pays that instead of COIN_VALUE.
Sources and further reading
- Roblox Creator Docs: Leaderboards. the leaderstats folder and its values
- Roblox Creator Docs: CollectionService. tags
- Roblox Creator Docs: BasePart. Touched, CanTouch and Transparency
- Roblox Creator Docs: RunService. RenderStepped for the spin
How the code was checked: every script on this page passes Luau's strict type checker against Roblox's API definitions (luau-lsp, 23 October 2026). A type check catches misspelt APIs and wrong types, not game logic, so playtest in Studio before you publish.