Nothing makes players quit faster than replaying five minutes of a level because they missed one jump. Checkpoints fix that. This guide builds a Unity checkpoint and respawn system: checkpoint triggers that activate as you pass, a respawn manager that remembers the latest one, safe respawning for Rigidbody and CharacterController players, resetting enemies and hazards, and saving the checkpoint so it survives quitting.
How the Pieces Fit
- Checkpoint: a trigger in the level. When the player enters, it tells the manager “I’m the latest checkpoint” and plays an effect.
- RespawnManager: one per scene. Stores the current checkpoint and handles the respawn: fade out, move the player, reset things, fade in.
- Death sources: kill zones, hazards and your health system call
RespawnManager.Respawn().
Keeping respawn logic in one place means every way of dying behaves the same, and you change the flow once.
The Checkpoint Trigger
using UnityEngine;
public class Checkpoint : MonoBehaviour
{
[SerializeField] string id; // unique per checkpoint, used for saving
[SerializeField] Transform spawnPoint; // where the player appears
[SerializeField] int order; // higher = further into the level
[SerializeField] GameObject activeVisual;
[SerializeField] ParticleSystem activateFx;
public string Id => id;
public int Order => order;
public Transform SpawnPoint => spawnPoint ? spawnPoint : transform;
void OnTriggerEnter(Collider other) => Touch(other);
void OnTriggerEnter2D(Collider2D other) => Touch(other);
void Touch(Component other)
{
if (!other.CompareTag("Player")) return;
RespawnManager.Instance.Reach(this);
}
public void SetActiveVisual(bool on)
{
if (activeVisual) activeVisual.SetActive(on);
if (on && activateFx) activateFx.Play();
}
void OnValidate()
{
if (string.IsNullOrEmpty(id)) id = System.Guid.NewGuid().ToString("N").Substring(0, 8);
}
}
A separate spawn point child lets you put the trigger across a corridor while the player respawns safely on solid ground, facing the right way. The order stops a player who runs backwards from moving their checkpoint back to an earlier one. OnValidate fills in a random ID once so you don’t have to type them; if you duplicate a checkpoint, clear the copy’s ID so it gets a new one.
The Respawn Manager
using System.Collections;
using UnityEngine;
using UnityEngine.Events;
public class RespawnManager : MonoBehaviour
{
public static RespawnManager Instance { get; private set; }
[SerializeField] Transform player;
[SerializeField] Checkpoint startCheckpoint;
[SerializeField] CanvasGroup fader;
[SerializeField] float fadeTime = 0.25f;
public UnityEvent onRespawn;
Checkpoint current;
bool respawning;
void Awake()
{
Instance = this;
current = startCheckpoint;
}
public void Reach(Checkpoint cp)
{
if (current != null && cp.Order <= current.Order) return;
if (current) current.SetActiveVisual(false);
current = cp;
cp.SetActiveVisual(true);
PlayerPrefs.SetString(SaveKey, cp.Id);
}
public void Respawn()
{
if (!respawning) StartCoroutine(RespawnRoutine());
}
IEnumerator RespawnRoutine()
{
respawning = true;
yield return Fade(0f, 1f);
PlayerTeleport.MoveTo(player, current.SpawnPoint);
onRespawn.Invoke();
yield return new WaitForSeconds(0.1f);
yield return Fade(1f, 0f);
respawning = false;
}
IEnumerator Fade(float from, float to)
{
for (float t = 0; t < fadeTime; t += Time.unscaledDeltaTime)
{
fader.alpha = Mathf.Lerp(from, to, t / fadeTime);
yield return null;
}
fader.alpha = to;
}
string SaveKey => "checkpoint_" + gameObject.scene.name;
}
The respawning flag stops double deaths (falling into a pit while touching spikes) from starting two respawns. The fader is a full-screen black Image with a CanvasGroup; the scene transition fade guide shows how to set one up. A simple static Instance is enough here because the manager lives in the scene; see the singleton patterns guide if you want something sturdier.
Teleporting the Player Safely
Moving a player to a spawn point sounds trivial, but each controller type has a trap:
using UnityEngine;
public static class PlayerTeleport
{
public static void MoveTo(Transform player, Transform target)
{
if (player.TryGetComponent(out CharacterController cc))
{
cc.enabled = false; // otherwise it snaps back
player.SetPositionAndRotation(target.position, target.rotation);
cc.enabled = true;
}
else if (player.TryGetComponent(out Rigidbody rb))
{
rb.linearVelocity = Vector3.zero;
rb.angularVelocity = Vector3.zero;
rb.position = target.position; // skips interpolation from the old spot
rb.rotation = target.rotation;
player.SetPositionAndRotation(target.position, target.rotation);
}
else if (player.TryGetComponent(out Rigidbody2D rb2))
{
rb2.linearVelocity = Vector2.zero;
rb2.angularVelocity = 0f;
rb2.position = target.position;
player.position = target.position;
}
else
{
player.SetPositionAndRotation(target.position, target.rotation);
}
}
}
- CharacterController keeps its own position. Setting the transform while it’s enabled often gets overwritten on the next Move. Disable, move, enable.
- Rigidbody keeps its velocity. Without zeroing it, the player respawns still falling at full speed and may go straight through the floor.
- If a Cinemachine camera follows the player, it will glide across the level after the teleport. Call
OnTargetObjectWarpedon the Cinemachine Camera with the position delta, or cut during the fade so the glide is hidden.
Death Zones and the Respawn Flow
A kill zone below the level catches falls:
using UnityEngine;
public class KillZone : MonoBehaviour
{
void OnTriggerEnter(Collider other) { if (other.CompareTag("Player")) RespawnManager.Instance.Respawn(); }
void OnTriggerEnter2D(Collider2D other) { if (other.CompareTag("Player")) RespawnManager.Instance.Respawn(); }
}
Make it a huge flat trigger well below the lowest floor. From your health script, call the same Respawn() when health reaches zero, and refill health in the onRespawn event. Disable player input during the fade so the player can’t move mid-teleport.
Resetting the Level on Respawn
Decide what resets. Common choices:
- Enemies return to their start positions and full health. Give each one a
ResetState()method and hook them toonRespawn, or let them subscribe in code. - Moving platforms and traps restart their cycles, so the section plays the same way every attempt. That predictability is what makes hard platforming fair.
- Collected coins usually stay collected, but keys and switches might reset if collected after the checkpoint.
A tidy pattern: an IResettable interface with ResetToCheckpoint(), found once at startup with FindObjectsByType<MonoBehaviour>(FindObjectsSortMode.None) and filtered for the interface. The manager calls it on all of them during the fade.
If the reset is complex, reloading the scene is a valid shortcut: store the checkpoint ID, reload, and spawn the player at that checkpoint on load. It’s slower but resets everything perfectly.
Saving the Checkpoint
The manager already writes the checkpoint ID to PlayerPrefs. On level start, read it and pick the matching checkpoint:
void Start()
{
string saved = PlayerPrefs.GetString(SaveKey, "");
if (saved == "") return;
foreach (var cp in FindObjectsByType<Checkpoint>(FindObjectsSortMode.None))
if (cp.Id == saved) { current = cp; cp.SetActiveVisual(true); PlayerTeleport.MoveTo(player, cp.SpawnPoint); break; }
}
Add that to RespawnManager. PlayerPrefs is fine for one value per level. If your game already has a save file, store it there instead; the PlayerPrefs vs JSON guide helps you choose.
📚 Design fair levels
Where you place checkpoints is level design as much as code. Some of our favourite game design books cover pacing and difficulty in depth.
Troubleshooting
Player respawns at the wrong checkpoint
Order values are missing or equal. Give each checkpoint a higher order than the one before it.
CharacterController player doesn’t move on respawn
Disable the CharacterController before setting the position, then enable it again.
Player falls through the floor after respawning
Velocity wasn’t reset, or the spawn point is inside the floor. Zero velocity and lift the spawn point slightly.
Camera slides across the map
Tell Cinemachine the target warped, or move the player while the screen is faded out.
Respawn triggered twice
Two death sources fired. Guard with a respawning flag as shown.
FAQ
Should checkpoints be triggers or interactables?
Triggers for automatic checkpoints, which most action games use. Interactables for save points the player chooses to use, like bonfires.
How do I reset checkpoints for a new game?
Delete the saved key when starting a fresh run, or store checkpoints inside a per-slot save file.
Can I respawn by reloading the scene?
Yes. It’s the simplest full reset. Store the checkpoint ID first, then spawn there on load.
Does this work for 2D?
Yes. The scripts include 2D trigger handlers and a Rigidbody2D branch in the teleport.
How do I show a “Checkpoint reached” message?
Raise an event in Reach and have a UI script listen and play a short popup.
Reader questions
Asked by readers of this article and answered by AI. Behavior can differ between Unity versions, so test in yours.




Leave a Reply