Roblox checkpoint script (obby stages that save)
Numbered checkpoints, a Stage counter on the leaderboard and respawning at the last one reached — saved between visits.
ScriptUpdated November 2, 2026
Where to put it
Script — ServerScriptService > Checkpoints (Script). It runs on the server, so players can't change what it does.
- Put flat anchored parts named
1,2,3… in a Folder namedCheckpointsin Workspace. Keep a SpawnLocation next to checkpoint1. - Publish the place and turn on Studio API access if you want saving to work in Studio.
The script
local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local store = DataStoreService:GetDataStore("ObbyStage_v1")
local checkpoints = workspace:WaitForChild("Checkpoints")
local function moveToCheckpoint(player, character)
local checkpoint = checkpoints:FindFirstChild(tostring(player.leaderstats.Stage.Value))
if checkpoint then
character:WaitForChild("HumanoidRootPart")
task.wait()
character:PivotTo(checkpoint.CFrame + Vector3.new(0, 4, 0))
end
end
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
local ok, saved = pcall(function()
return store:GetAsync("player_" .. player.UserId)
end)
if ok then
stage.Value = saved or 1
player:SetAttribute("DataLoaded", true)
end
player.CharacterAdded:Connect(function(character)
moveToCheckpoint(player, character)
end)
if player.Character then
moveToCheckpoint(player, player.Character)
end
end)
Players.PlayerRemoving:Connect(function(player)
if not player:GetAttribute("DataLoaded") then return end
pcall(function()
store:SetAsync("player_" .. player.UserId, player.leaderstats.Stage.Value)
end)
end)
for _, checkpoint in checkpoints:GetChildren() do
local number = tonumber(checkpoint.Name)
checkpoint.Touched:Connect(function(hit)
local player = Players:GetPlayerFromCharacter(hit.Parent)
if player and number and number == player.leaderstats.Stage.Value + 1 then
player.leaderstats.Stage.Value = number
end
end)
endHow it works
- Only the next checkpoint counts, so players can't skip ahead by touching a later one.
- After every respawn the character is moved to the checkpoint matching its Stage.
- Stage is saved on leave and loaded on join; it isn't saved if loading failed, so progress is never wiped.
Variations
Skip stage product
Sell a developer product that adds 1 to Stage and calls player:LoadCharacter(). Grant it in MarketplaceService.ProcessReceipt and return PurchaseGranted.
Checkpoint feedback
On a new stage, play a sound and briefly turn the checkpoint green so players know it saved.
Common mistakes
- Checkpoints named “Checkpoint1” instead of
1—tonumberreturns nil and nothing saves. - Two parts with the same number, or gaps in numbering: the stage can't advance past the gap.
- Moving the character in the same frame it spawns — wait for
HumanoidRootPartand one frame, as above.
FAQ
How do I make checkpoints save in a Roblox obby?
Save the Stage value with DataStoreService when the player leaves and load it when they join, as the script on this page does. Data stores work only in published places.
Can I use SpawnLocations as checkpoints?
You can, using Player.RespawnLocation, but many enabled SpawnLocations make new players spawn at random ones. Plain parts plus moving the character, as here, avoids that.