Table of Contents
Why Checkpoints Matter in an Obby
In an obby, checkpoints decide where a player respawns after they fall or die. Without checkpoints, players always go back to the very beginning, which quickly becomes frustrating. With a good checkpoint system, players feel that their progress is respected, and they can focus on mastering one section at a time instead of replaying everything.
A checkpoint in Roblox is usually a part the player touches that records their progress. When the player’s character dies, the game uses that recorded checkpoint to decide where the player should spawn next. Checkpoints are closely related to spawn points, but they have different jobs. Spawn points tell Roblox where a new or resetting character appears. Checkpoints control which spawn point is currently active for that player.
Visual Design of Checkpoints
Before you script anything, it is helpful to decide how your checkpoints look. Many obbies use colorful pads on the ground, often arranged at the start of each new section. You can create a checkpoint using a simple Part scaled into a flat platform. Make it stand out from the rest of the level with bright colors or neon material so players can instantly recognize it.
You can also give checkpoints two visual states. One state is for checkpoints that have not been activated yet, and another state is for the current active checkpoint. For example, inactive checkpoints might be gray, and the one a player has just activated might turn green. This change can be as simple as setting a different BrickColor using a script when the player touches the checkpoint.
You should also consider the size and position. A checkpoint that is too small or floating in an odd place can be easy to miss. Place the checkpoint along the main path, large enough that a normal jump or walk will almost always hit it.
Basic Checkpoint Logic
The core idea of a checkpoint in Roblox is: when a player touches the checkpoint part, the game records that this is the player’s latest checkpoint. When they die or reset, they respawn at the position that was last recorded.
There are two very common approaches. One approach uses SpawnLocation objects that are enabled and disabled. The other approach ignores SpawnLocation behavior and instead stores a position and manually moves the character to that position when they respawn. Both approaches use the same concept, which is to listen for a touch, identify the player, and update that player’s saved checkpoint.
In used code, you detect a touch with the Touched event on a Part or SpawnLocation. Inside that event, you find the Humanoid in the thing that touched the part, then find the player object, then update some value that stores the checkpoint. That value might be the TeamColor that controls spawn behavior, or a custom value in leaderstats, or a specific object reference like a Part or CFrame.
Key rule: When a character touches a checkpoint, always make sure you link the checkpoint to the correct player, not just to the character model. Use Players:GetPlayerFromCharacter(character) to find the player from the character.
Using Touch Events for Checkpoints
A typical checkpoint part needs a script that reacts to touches. That script does three things. First, it checks that the thing touching the part is a player character, not a random physics object. Second, it updates the player’s stored checkpoint. Third, it optionally updates any visuals, like color changes, to show that the checkpoint is active.
Here is a simplified pattern that is often used in checkpoint scripts. You do not need to fully understand all the details yet. Focus on how the flow works.
local checkpointPart = script.Parent
local Players = game:GetService("Players")
local function onTouched(otherPart)
local character = otherPart.Parent
if not character then return end
local humanoid = character:FindFirstChild("Humanoid")
if not humanoid then return end
local player = Players:GetPlayerFromCharacter(character)
if not player then return end
-- Here you record that this checkpoint belongs to this player
-- For example, you might store a reference in a value
end
checkpointPart.Touched:Connect(onTouched)
The important part is that you do not assume every touch comes from a player. You always verify there is a Humanoid and then use Players:GetPlayerFromCharacter to safely get the player. This pattern avoids many common bugs, like checkpoints being triggered by loose parts or tools sliding across the floor.
Tracking Player Progress with Checkpoints
To make checkpoints meaningful, you must choose where to store progress for each player. The basic requirement is that the information is per player and can be read when the player respawns. A common beginner approach is to store a StringValue or ObjectValue inside the player that points to the latest checkpoint part.
For example, you might add an ObjectValue named CurrentCheckpoint to each player when they join. The touch script on each checkpoint then sets this value to its own part when touched. Later, when the character respawns, another script looks at CurrentCheckpoint and moves the character to that part’s position.
In simple obbies, you can place all checkpoint parts inside a folder, and give them names like "Checkpoint1", "Checkpoint2", and so on. Then you can compare or print these names to debug progress. This organization also helps avoid confusion when you have many stages.
You must decide how precise the checkpoint should be. Some designers store only a position using CFrame. Others store a reference to a specific part. Using a part is easier to edit visually. If you move the part in Studio, the checkpoint moves automatically without changing any script.
Important principle: Store checkpoint progress on the player, not in random places in the Workspace. This keeps each player’s progress separate and avoids mixing up checkpoints between different players.
Making Checkpoints Respawn Players Correctly
Once you have stored which checkpoint is active for a player, you must connect it to the moment the player respawns. When a Roblox character dies, a new character model is created for that player. If you rely on character position alone, the character will appear at the default spawn until your script moves it.
To control this, you can use the CharacterAdded event on the Player object. When the character is created, check the player’s stored checkpoint. If a checkpoint is stored, set the character’s HumanoidRootPart.CFrame to that checkpoint’s CFrame. This effectively teleports the new character to the correct checkpoint right after spawn.
A simple pattern looks like this in concept. It is not the full system, but it shows when you would use the checkpoint data.
local Players = game:GetService("Players")
local function onCharacterAdded(character, player)
local checkpoint = player:FindFirstChild("CurrentCheckpoint")
if checkpoint and checkpoint.Value then
local root = character:WaitForChild("HumanoidRootPart")
root.CFrame = checkpoint.Value.CFrame + Vector3.new(0, 3, 0)
end
end
local function onPlayerAdded(player)
local checkpointValue = Instance.new("ObjectValue")
checkpointValue.Name = "CurrentCheckpoint"
checkpointValue.Parent = player
player.CharacterAdded:Connect(function(character)
onCharacterAdded(character, player)
end)
end
Players.PlayerAdded:Connect(onPlayerAdded)Notice that this logic uses the checkpoint only after the player already has a character. It also adds a small offset upward so that the character does not get stuck inside the part. The teleport happens quickly enough that the player often does not see the original spawn point at all.
Preventing Repeated Activation Issues
In an obby, a player might stand on a checkpoint for a while or walk back and forth across it. If your script is not careful, the Touched event might fire many times. This does not always break anything, but it can cause unnecessary work or confusing visual changes.
To avoid this, you can add a simple guard that stops the script from redoing work if the player already has this checkpoint active. You can check the player’s current checkpoint value and only update it if the new checkpoint is different from the stored one.
Here is an idea of how that might look in the touch script. It assumes there is an ObjectValue named CurrentCheckpoint on the player.
local checkpointPart = script.Parent
local Players = game:GetService("Players")
local function onTouched(otherPart)
local character = otherPart.Parent
if not character then return end
local humanoid = character:FindFirstChild("Humanoid")
if not humanoid then return end
local player = Players:GetPlayerFromCharacter(character)
if not player then return end
local checkpointValue = player:FindFirstChild("CurrentCheckpoint")
if not checkpointValue then return end
if checkpointValue.Value == checkpointPart then
return
end
checkpointValue.Value = checkpointPart
-- This is where you can add visual or sound feedback once
end
checkpointPart.Touched:Connect(onTouched)This small check prevents constant reassignments and makes it easier to add effects that should only play when the checkpoint is newly reached, such as a sound or a color change.
Feedback and Clarity for Players
Good checkpoints are not just functional, they also clearly communicate to the player that they have been activated. If a player is not sure a checkpoint worked, they may intentionally die to test it, which interrupts the flow of your obby.
Visual feedback is the easiest way to fix this. When the checkpoint is activated, you can change the color, material, or transparency of the part. You might also turn on a glow part, a particle effect, or a small light. Even a simple color change is usually enough to show that progress is saved.
Sound is another helpful channel. A short chime or click can give instant confirmation that a checkpoint was triggered. You can place a Sound object inside the checkpoint part and play it in your touch script when the player successfully activates the checkpoint for the first time.
Text feedback is possible too. For example, you might have a UI text label that briefly shows "Checkpoint reached!" when a new checkpoint is activated. This uses UI topics covered elsewhere in the course, so in this chapter the point is just that UI can also help confirm checkpoint activation.
Rule for good feedback: When a player touches a checkpoint, they should immediately see or hear at least one clear change that tells them progress is saved.
Organizing Multiple Checkpoints in Your Obby
As your obby grows, you will likely have many checkpoints, one at almost every stage or section. Good organization keeps your scripts simple and avoids mistakes where two different parts accidentally share the same logic in a confusing way.
You can place all checkpoint parts in a folder, for example Workspace.Checkpoints. Then you can drop a copy of the same script into each checkpoint part, or use a single script that goes through the folder and connects to the Touched event of each part it finds.
The order of checkpoints is also important. In many obbies, each checkpoint corresponds to a stage number. You can reflect this number in the checkpoint part’s name, like "Stage1", "Stage2", and so on. If you track the stage number on the player, for example in a leaderstats value, you can make sure players cannot cheat by touching a far checkpoint that is not supposed to be reachable yet.
Finally, remember to test checkpoints regularly as you add new obstacles. Every time you build a new section, play your game, reach the new checkpoint, then intentionally fall and confirm that you respawn at the right place. This simple habit prevents most checkpoint related bugs from sneaking into the final game.