L13 / Scripts

Luau script library

Ready-to-use scripts for the things almost every Roblox game needs. Each one says exactly where to put it.

Leaderstats (show coins on the leaderboard)

Adds a Coins value to every player, shown in the top-right leaderboard. Many of the other scripts here build on this.

πŸ“ Script in ServerScriptService
local Players = game:GetService("Players")

Players.PlayerAdded:Connect(function(player)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats" -- must be lowercase to show on the leaderboard
	leaderstats.Parent = player

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

Save player data (DataStore)

Loads and saves each player's coins between sessions. If loading fails, it won't save over their data, which avoids the most common cause of lost progress. Use this instead of the leaderstats script above.

πŸ“ Script in ServerScriptService
local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")

local store = DataStoreService:GetDataStore("PlayerData_v1")
local loaded = {} -- only save players whose data loaded, so a failed load never wipes a save

local function onPlayerAdded(player)
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"

	local coins = Instance.new("IntValue")
	coins.Name = "Coins"
	coins.Parent = leaderstats

	local ok, data = pcall(function()
		return store:GetAsync(tostring(player.UserId))
	end)
	if not player.Parent then
		return -- they left while we were loading
	end

	if ok then
		if data then
			coins.Value = data.Coins or 0
		end
		loaded[player] = true
	else
		warn("Couldn't load data for " .. player.Name .. ": " .. tostring(data))
	end

	leaderstats.Parent = player
end

local function save(player)
	if not loaded[player] then
		return
	end
	local data = { Coins = player.leaderstats.Coins.Value }
	local ok, err = pcall(function()
		store:SetAsync(tostring(player.UserId), data)
	end)
	if not ok then
		warn("Couldn't save data for " .. player.Name .. ": " .. tostring(err))
	end
end

Players.PlayerAdded:Connect(onPlayerAdded)
for _, player in Players:GetPlayers() do
	task.spawn(onPlayerAdded, player)
end

Players.PlayerRemoving:Connect(function(player)
	save(player)
	loaded[player] = nil
end)

-- Save everyone if the server shuts down
game:BindToClose(function()
	for _, player in Players:GetPlayers() do
		task.spawn(save, player)
	end
	task.wait(3) -- give the saves time to finish
end)

To test in Studio, publish your game, then turn on Game Settings β†’ Security β†’ Enable Studio Access to API Services.

Give a perk to pass owners

Checks whether a player owns your pass when they join, and gives the perk right away if they buy it in-game. The example perk is faster walking.

πŸ“ Script in ServerScriptService
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")

local PASS_ID = 0 -- replace with your pass ID

local function givePerk(player)
	player:SetAttribute("HasVIP", true)

	-- Example perk: faster walking. Change this to whatever your pass unlocks.
	local function boost(character)
		character:WaitForChild("Humanoid").WalkSpeed = 24
	end
	if player.Character then
		boost(player.Character)
	end
	player.CharacterAdded:Connect(boost)
end

Players.PlayerAdded:Connect(function(player)
	local ok, owns = pcall(function()
		return MarketplaceService:UserOwnsGamePassAsync(player.UserId, PASS_ID)
	end)
	if ok and owns then
		givePerk(player)
	end
end)

-- Give the perk right away when someone buys the pass in-game
MarketplaceService.PromptGamePassPurchaseFinished:Connect(function(player, passId, purchased)
	if purchased and passId == PASS_ID then
		givePerk(player)
	end
end)

Buy-pass button

Opens Roblox's purchase window for your pass when a player clicks a button. Pair it with the perk script above.

πŸ“ LocalScript inside a TextButton or ImageButton
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")

local PASS_ID = 0 -- replace with your pass ID
local button = script.Parent

button.Activated:Connect(function()
	MarketplaceService:PromptGamePassPurchase(Players.LocalPlayer, PASS_ID)
end)

Developer products (buy coins)

Handles purchases of things players can buy many times, like coin packs. Needs a leaderstats script with a Coins value. To open the purchase window, call MarketplaceService:PromptProductPurchase(Players.LocalPlayer, PRODUCT_ID) from a LocalScript, just like the button above.

πŸ“ Script in ServerScriptService (only one script can do this)
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")

-- Map each developer product ID to what it gives
local PRODUCTS = {
	[0] = function(player) -- replace 0 with your product ID
		player.leaderstats.Coins.Value += 100
	end,
}

MarketplaceService.ProcessReceipt = function(receiptInfo)
	local player = Players:GetPlayerByUserId(receiptInfo.PlayerId)
	local grant = PRODUCTS[receiptInfo.ProductId]
	if not player or not grant then
		-- Roblox will try again later, for example when the player rejoins
		return Enum.ProductPurchaseDecision.NotProcessedYet
	end

	local ok, err = pcall(grant, player)
	if not ok then
		warn("Couldn't grant product " .. receiptInfo.ProductId .. ": " .. tostring(err))
		return Enum.ProductPurchaseDecision.NotProcessedYet
	end

	return Enum.ProductPurchaseDecision.PurchaseGranted
end

For bigger games, also save each receiptInfo.PurchaseId in a DataStore so a purchase can never be granted twice.

Kill part (lava)

Resets any player who touches the part. The classic obby lava brick.

πŸ“ Script inside the Part
local part = script.Parent

part.Touched:Connect(function(hit)
	local humanoid = hit.Parent:FindFirstChildOfClass("Humanoid")
	if humanoid then
		humanoid.Health = 0
	end
end)

Obby checkpoints with stages

Make a Folder in Workspace called Checkpoints and fill it with SpawnLocations named 1, 2, 3 and so on. Players respawn at their latest stage, and the leaderboard shows their progress. If you already have a leaderstats script, add the Stage value there instead.

πŸ“ Script in ServerScriptService
local Players = game:GetService("Players")

local checkpoints = workspace:WaitForChild("Checkpoints")

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

	local stage = Instance.new("IntValue")
	stage.Name = "Stage"
	stage.Value = 1
	stage.Parent = leaderstats

	player.RespawnLocation = checkpoints:FindFirstChild("1")
end)

for _, checkpoint in checkpoints:GetChildren() do
	local number = tonumber(checkpoint.Name)
	if not number then
		continue
	end

	checkpoint.Touched:Connect(function(hit)
		local player = Players:GetPlayerFromCharacter(hit.Parent)
		if not player then
			return
		end
		local stage = player.leaderstats.Stage
		-- Only move forward one stage at a time, so players can't skip ahead
		if number == stage.Value + 1 then
			stage.Value = number
			player.RespawnLocation = checkpoint
		end
	end)
end

Collectible coin that respawns

Gives coins when touched, disappears, then comes back after a few seconds. Needs a leaderstats script with a Coins value. Duplicate the coin as many times as you like.

πŸ“ Script inside the coin Part
local Players = game:GetService("Players")

local coin = script.Parent
local VALUE = 10 -- coins given per pickup
local RESPAWN_TIME = 5 -- seconds until the coin comes back

local ready = true

coin.Touched:Connect(function(hit)
	if not ready then
		return
	end
	local player = Players:GetPlayerFromCharacter(hit.Parent)
	local leaderstats = player and player:FindFirstChild("leaderstats")
	local coins = leaderstats and leaderstats:FindFirstChild("Coins")
	if not coins then
		return
	end

	ready = false
	coins.Value += VALUE
	coin.Transparency = 1

	task.wait(RESPAWN_TIME)
	coin.Transparency = 0
	ready = true
end)

Teleport pad to another place

Sends players to another place in your experience when they step on the pad, such as a lobby that leads to a game. Teleports don't work in Studio playtests, so test in a live server.

πŸ“ Script inside the pad Part
local Players = game:GetService("Players")
local TeleportService = game:GetService("TeleportService")

local PLACE_ID = 0 -- the place to send players to (must be in your experience)
local pad = script.Parent
local cooldown = {}

pad.Touched:Connect(function(hit)
	local player = Players:GetPlayerFromCharacter(hit.Parent)
	if not player or cooldown[player] then
		return
	end
	cooldown[player] = true

	local ok, err = pcall(function()
		TeleportService:TeleportAsync(PLACE_ID, { player })
	end)
	if not ok then
		warn("Teleport failed: " .. tostring(err))
	end

	task.wait(5)
	cooldown[player] = nil
end)

Abbreviate big numbers (1.5K, 2M)

Turns 1530 into "1.5K" and 2000000 into "2M" for clean UI. Save it as a ModuleScript named Abbreviate.

πŸ“ ModuleScript in ReplicatedStorage
local SUFFIXES = { "", "K", "M", "B", "T", "Qa", "Qi" }

local function abbreviate(n: number): string
	local i = 1
	while math.abs(n) >= 999.95 and i < #SUFFIXES do
		n /= 1000
		i += 1
	end
	if i == 1 then
		return tostring(math.floor(n))
	end
	local text = string.format("%.1f", n):gsub("%.0$", "")
	return text .. SUFFIXES[i]
end

return abbreviate

-- Use it from any other script:
-- local abbreviate = require(game.ReplicatedStorage.Abbreviate)
-- print(abbreviate(1530))    --> 1.5K
-- print(abbreviate(2000000)) --> 2M