Overview
The race_checkpoint_device is a single gate in a larger racetrack. You place several of them along a route, hook them up to a race_manager_device, and the manager handles ordering — telling each runner which checkpoint is current (the one they need to reach next). Each checkpoint knows when it becomes the current target for a player, and when a player actually passes through it.
Reach for this device whenever you need ordered progress through space: a parkour course, a Rocket Racing-style lap track, a relay where every gate must be hit in sequence, or a scavenger trail. The Verse API lets you do far more than the manager alone — grant points the instant a player clears a gate, play a sound the first time anyone reaches it, disable a shortcut gate until a condition is met, or teleport a respawning player back to their last checkpoint with SetAsCurrentCheckpoint.
The three events give you a clean progress signal:
- CheckpointBecomesCurrentForTheFirstTimeEvent — fires once, the very first time this gate becomes someone's target. Great for one-shot effects (lighting up a gate, opening a barrier).
- CheckpointBecomesCurrentEvent — fires every time the gate becomes a player's current target. Use for per-player UI hints ("head this way!").
- CheckpointCompletedEvent — fires when a player passes through. This is your reward / scoring hook.
And three methods to control it: Enable, Disable, and SetAsCurrentCheckpoint(Agent).
API Reference
race_checkpoint_device
Used in tandem with
race_manager_deviceto define the route players must traverse.
Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse).
race_checkpoint_device<public> := class<concrete><final>(creative_device_base):
Events (subscribe a handler to react):
| Event | Signature | Description |
|---|---|---|
CheckpointBecomesCurrentForTheFirstTimeEvent |
CheckpointBecomesCurrentForTheFirstTimeEvent<public>:listenable(agent) |
Signaled when this checkpoint becomes the next checkpoint that agents need to pass for the first time. Sends the first agent who is now targeting this checkpoint. |
CheckpointBecomesCurrentEvent |
CheckpointBecomesCurrentEvent<public>:listenable(agent) |
Signaled when this checkpoint becomes the current checkpoint for agent. Sends the agent who is now targeting this checkpoint. |
CheckpointCompletedEvent |
CheckpointCompletedEvent<public>:listenable(agent) |
Signaled when an agent passes this checkpoint. Sends the agent that passed this checkpoint. |
Methods (call these to make the device act):
| Method | Signature | Description |
|---|---|---|
Enable |
Enable<public>():void |
Enables this device. |
Disable |
Disable<public>():void |
Disables this device. |
SetAsCurrentCheckpoint |
SetAsCurrentCheckpoint<public>(Agent:agent):void |
Sets this checkpoint as the current checkpoint for Agent. This only functions if Agent has not already passed this checkpoint. |
Walkthrough
Let's build a three-gate sprint course. When a player passes a checkpoint we give them feedback via a HUD message; the first time a gate becomes active anywhere we trigger a one-time VFX burst to "light up" the gate; and when the player clears the final checkpoint we disable it so it can't be re-triggered.
Place three race_checkpoint_devices wired to a race_manager_device in your level, plus one vfx_spawner_device near the start gate, then drop this Verse device in and assign the references.
race_course_device := class(creative_device):
# The first gate runners must reach.
@editable
StartGate : race_checkpoint_device = race_checkpoint_device{}
# The middle gate.
@editable
MidGate : race_checkpoint_device = race_checkpoint_device{}
# The final gate — the finish line.
@editable
FinishGate : race_checkpoint_device = race_checkpoint_device{}
# A VFX burst to light up the first gate.
@editable
StartFx : vfx_spawner_device = vfx_spawner_device{}
# Localized message helper — `message` params need a localized value, not a raw string.
Hud<localizes>(S:string):message = "{S}"
OnBegin<override>()<suspends>:void =
# Light the start gate the FIRST time it becomes someone's target.
StartGate.CheckpointBecomesCurrentForTheFirstTimeEvent.Subscribe(OnStartLitUp)
# Per-player feedback whenever a gate becomes the current target.
MidGate.CheckpointBecomesCurrentEvent.Subscribe(OnMidIsNext)
# Score / react when a runner passes each gate.
StartGate.CheckpointCompletedEvent.Subscribe(OnStartPassed)
MidGate.CheckpointCompletedEvent.Subscribe(OnMidPassed)
FinishGate.CheckpointCompletedEvent.Subscribe(OnFinished)
# Fires ONCE — the first runner to target the start gate triggers the VFX.
OnStartLitUp(Agent:agent):void =
StartFx.Enable()
StartFx.Restart()
# Fires for each player when the mid gate becomes their next target.
OnMidIsNext(Agent:agent):void =
if (Player := player[Agent]):
Print("A runner is now heading to the mid gate")
OnStartPassed(Agent:agent):void =
Print("Start gate cleared!")
OnMidPassed(Agent:agent):void =
Print("Mid gate cleared!")
# The runner crossed the finish line — disable the gate so it stops reacting.
OnFinished(Agent:agent):void =
FinishGate.Disable()
Print("Finish line reached — course complete!")
Line by line:
- The four
@editablefields let you assign your placed devices in the UEFN Details panel. Without@editablefields you cannot call a device's methods — a bareStartGate.Enable()on an unbound device fails. Hud<localizes>(S:string):messageis a helper that turns a plain string into themessagetype the HUD/UI APIs require. There is noStringToMessagebuilt-in — you declare your own localizer like this.- In
OnBeginwe subscribe handlers to the events. Subscription happens once at startup; the handlers are plain methods at class scope. CheckpointBecomesCurrentForTheFirstTimeEventonly ever fires once per device, soOnStartLitUpis the perfect place to enable andRestart()the VFX burst — it won't re-fire on later runners.- Each event hands us an
agent. We unwrap it withplayer[Agent]when we need a player specifically (player[...]fails gracefully if the agent isn't a player). OnFinishedcallsFinishGate.Disable()so the finish gate stops responding once cleared.
Common patterns
Reset a respawning player to their last checkpoint
When a player wipes out, send them back to a known gate by making it their current target again. SetAsCurrentCheckpoint only works if the agent hasn't already passed that checkpoint — so use it for backward resets within the route.
checkpoint_reset_device := class(creative_device):
@editable
RecoveryGate : race_checkpoint_device = race_checkpoint_device{}
OnBegin<override>()<suspends>:void =
# When a player reaches the recovery gate, remember it as their anchor.
RecoveryGate.CheckpointCompletedEvent.Subscribe(OnReached)
OnReached(Agent:agent):void =
# Re-target this gate for the agent (e.g. after a fall) so the
# manager routes them from here again.
RecoveryGate.SetAsCurrentCheckpoint(Agent)
Enable a shortcut gate only after a condition
Keep a hidden shortcut checkpoint Disabled at start, then Enable it once the lead gate is cleared — opening an alternate route mid-race.
shortcut_gate_device := class(creative_device):
@editable
LeadGate : race_checkpoint_device = race_checkpoint_device{}
@editable
ShortcutGate : race_checkpoint_device = race_checkpoint_device{}
OnBegin<override>()<suspends>:void =
# Start with the shortcut closed.
ShortcutGate.Disable()
LeadGate.CheckpointCompletedEvent.Subscribe(OnLeadCleared)
OnLeadCleared(Agent:agent):void =
# Open the shortcut once any runner clears the lead gate.
ShortcutGate.Enable()
Per-player navigation hint when a gate becomes current
CheckpointBecomesCurrentEvent fires every time the gate becomes a player's next target — ideal for telling that specific player where to go.
nav_hint_device := class(creative_device):
@editable
NextGate : race_checkpoint_device = race_checkpoint_device{}
OnBegin<override>()<suspends>:void =
NextGate.CheckpointBecomesCurrentEvent.Subscribe(OnBecameCurrent)
OnBecameCurrent(Agent:agent):void =
if (Player := player[Agent]):
# This player should now head to NextGate.
Print("Hint shown: head to the next gate")
Gotchas
- You must declare the device as an
@editablefield. CallingRaceCheckpoint.Enable()on a device you never referenced inside yourclass(creative_device)produces anUnknown identifiererror. Add the field, then assign the placed device in the Details panel. SetAsCurrentCheckpointis silently no-op once passed. The API only re-targets an agent to a checkpoint the agent hasn't already passed. You can't use it to skip a player forward past a gate they've completed — it's for backward resets (respawn recovery)....ForTheFirstTimeEventfires exactly once for the whole device, not once per player. If you want per-player reactions, subscribe toCheckpointBecomesCurrentEventinstead.- Events hand you a raw
agent, not a player. If you need player-specific data, unwrap withif (Player := player[Agent]):. Skipping the unwrap and treating the agent as a player will fail to compile. messageparams need a localized value. Don't pass a bare"text"to UI/HUD APIs — declare a<localizes>helper likeHud<localizes>(S:string):message = "{S}"and callHud("..."). There is noStringToMessage.- Race checkpoints rely on a
race_manager_devicefor ordering. The checkpoint device defines a gate, but the manager decides sequence and laps. Wire them together in the editor — Verse controls behavior, not the route topology. Restart()on the VFX spawner only re-triggers burst-type effects. For looping effects,Enable/Disableis what toggles them.