An obby (obstacle course) is the classic first Roblox game. The part that trips most beginners up is checkpoints: the player should respawn at the last checkpoint they touched, not back at the start. Here is a complete, working setup you can paste into Roblox Studio.
What you need
- Roblox Studio, with a Baseplate place open
- A few platforms to jump across
- One part per checkpoint
1. Make the checkpoints
- In the Workspace, create a Folder and name it
Checkpoints. - Add a SpawnLocation for each checkpoint and put it inside that folder.
- Name them in order:
1,2,3and so on.1is the start. - On every SpawnLocation, set
Neutralto true andAllowTeamChangeOnTouchto false. Then setEnabledto false on all of them except1, so new players always start at the beginning.
Tip: give each checkpoint a bright colour and set Material to Neon so players can see where they are going.
2. Save which checkpoint each player reached
Create a Script in ServerScriptService and name it CheckpointService:
--!strict -- CheckpointService -- Remembers the last checkpoint each player touched and respawns them there. local Players = game:GetService("Players") local checkpointsFolder = workspace:WaitForChild("Checkpoints") local function getCheckpoint(stage: number): SpawnLocation? local found = checkpointsFolder:FindFirstChild(tostring(stage)) if found and found:IsA("SpawnLocation") then return found end return nil end local function onPlayerAdded(player: Player) -- Track the stage on the leaderboard so players can see their progress. 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 -- Respawn at the last checkpoint reached. player.CharacterAdded:Connect(function(character) local checkpoint = getCheckpoint(stage.Value) if not checkpoint then return end local root = character:WaitForChild("HumanoidRootPart") :: BasePart -- Wait one frame so the default spawn finishes first. task.wait() root.CFrame = checkpoint.CFrame + Vector3.new(0, 3, 0) end) end -- Move a player's stage forward when they touch the next checkpoint. for _, checkpoint in checkpointsFolder:GetChildren() do if not checkpoint:IsA("SpawnLocation") then continue end local number = tonumber(checkpoint.Name) if not number then continue end checkpoint.Touched:Connect(function(hit) local character = hit.Parent if not character or not character:IsA("Model") then return end local player = Players:GetPlayerFromCharacter(character) if not player then return end local leaderstats = player:FindFirstChild("leaderstats") local stage = leaderstats and leaderstats:FindFirstChild("Stage") -- Only count the next checkpoint in order, so players can't skip ahead. if stage and stage:IsA("IntValue") and number == stage.Value + 1 then stage.Value = number end end) end Players.PlayerAdded:Connect(onPlayerAdded) for _, player in Players:GetPlayers() do onPlayerAdded(player) end 3. Add something that kills the player
An obby needs danger. Put a Part under your course, name it Lava, colour it orange, set Material to Neon, and add this Script inside it:
--!strict -- Kills any character that touches this part. local lava = script.Parent :: BasePart lava.Touched:Connect(function(hit) local humanoid = hit.Parent and hit.Parent:FindFirstChildOfClass("Humanoid") if humanoid then humanoid.Health = 0 end end) When a player falls in, they respawn at their last checkpoint, because CharacterAdded runs again.
4. Test it
Press Play, touch checkpoint 2, then jump into the lava. You should come back at checkpoint 2 and see Stage: 2 on the leaderboard in the top right.
Common problems
- You always respawn at the start. Check the checkpoint names are plain numbers (
2, notCheckpoint2) and that they are inside theCheckpointsfolder. - Stage jumps around. That happens if two checkpoints share a number. Each one needs its own.
- Progress is lost when you leave. This script keeps progress for the current session only. To keep it between visits, save
Stagewith DataStoreService when the player leaves and load it when they join.
Going further
Once this works, you can add a timer, moving platforms, or a finish line that gives a badge. If you'd rather describe the course and have it built for you, I'm building RoCode, an AI agent that builds obbies like this inside Roblox Studio and playtests them.
Drafted with AI help. Both scripts pass Luau strict type checking.