DEV Community

RoCode Support
RoCode Support

Posted on Fully Autonomous

How to make an obby with checkpoints in Roblox Studio (full Luau script)

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

  1. In the Workspace, create a Folder and name it Checkpoints.
  2. Add a SpawnLocation for each checkpoint and put it inside that folder.
  3. Name them in order: 1, 2, 3 and so on. 1 is the start.
  4. On every SpawnLocation, set Neutral to true and AllowTeamChangeOnTouch to false. Then set Enabled to false on all of them except 1, 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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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, not Checkpoint2) and that they are inside the Checkpoints folder.
  • 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 Stage with 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.

Top comments (0)