Quest Framework: Join, Track, and Reward Players
What you'll learn
You'll learn how to use the real /Verse.org/Progression Quest Framework to:
- Define a concrete quest subclass (because
questis abstract). - Create a
quest_collectionand join players to a quest viaJoinQuest, inspecting theresultit returns. - Track progress from a
trigger_deviceand callComplete()when the goal is reached. - Display progress to each player on their HUD using the Player UI API (
GetPlayerUI[]+ a canvas with atext_blockwidget).
How it works
The framework has three moving parts:
quest_collection— the source of truth for participation. It isclass<unique>and is instantiable, so you create one and keep it on your device.quest— a completable goal. It isclass<abstract>, so you must subclass it (collect_items_quest := class(quest)) and instantiate the subclass. CallComplete()to finish it and fireCompleteEvent.agent_quest_participant— wraps anagent(aplayeris anagent) so it can join a quest.quest_participant_infocarries threelogicflags:Contributes,Observes,Receives.
Key correctness note: JoinQuest returns result(void, []join_quest_error). It is not a <decides> function — call it with parens and then inspect the result with GetSuccess[] inside an if. Calling JoinQuest[...] is the classic mistake that fails to compile.
For the HUD, GetPlayerUI[Player] is fallible (bind it in an if), then you build a canvas holding a text_block and call AddWidget. We track a per-player text widget so we can update its text as progress changes.
Let's build it
This device defines a 5-item collect quest, joins every player, draws a progress label on each player's HUD, advances progress on each ProgressTrigger pulse, and completes the quest when the target is hit.
using { /Fortnite.com/Devices }
using { /Fortnite.com/UI }
using { /Verse.org/Progression }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /UnrealEngine.com/Temporary/UI }
# `quest` is abstract — we MUST subclass it to get a concrete, instantiable quest.
collect_items_quest := class(quest):
Target : int = 5
# Main device: drives join / track / reward + live HUD.
quest_reward_device := class<concrete>(creative_device):
# Each trigger pulse = one unit of progress.
@editable
ProgressTrigger : trigger_device = trigger_device{}
# The source of truth for participation (unique, but instantiable).
Collection : quest_collection = quest_collection{}
# The shared quest every player works on.
Quest : collect_items_quest = collect_items_quest{ Target := 5 }
# Per-quest progress counter (a real game would key this per participant).
var Progress : int = 0
# Remember each player's HUD label so we can update it as progress changes.
var Labels : [player]text_block = map{}
OnBegin<override>()<suspends> : void =
# Observe completion so we can hand out rewards.
Quest.CompleteEvent.Subscribe(OnQuestCompleted)
# Enroll every current player and show their HUD.
for (Player : GetPlayspace().GetPlayers()):
JoinPlayerToQuest(Player)
# Track progress: each trigger pulse advances the quest by one.
loop:
ProgressTrigger.TriggeredEvent.Await()
AdvanceProgress()
# Wrap the player as a participant and JOIN it to the quest.
JoinPlayerToQuest(Player : player) : void =
Participant := agent_quest_participant{ Agent := Player }
Info := quest_participant_info:
Contributes := true
Observes := true
Receives := true
# JoinQuest returns result(...) — call with PARENS, then inspect GetSuccess[].
JoinResult := Collection.JoinQuest(Quest, Participant, Info)
if (JoinResult.GetSuccess[]):
ShowProgressHUD(Player)
# Build a HUD label on this player's screen using the Player UI API.
ShowProgressHUD(Player : player) : void =
if (PlayerUI := GetPlayerUI[Player]):
Label := text_block{ DefaultText := MakeProgressText() }
Canvas := canvas:
Slots := array:
canvas_slot:
Anchors := anchors{ Minimum := vector2{ X := 0.05, Y := 0.1 }, Maximum := vector2{ X := 0.05, Y := 0.1 } }
Widget := Label
PlayerUI.AddWidget(Canvas)
# Remember the label so AdvanceProgress can refresh it.
if (set Labels[Player] = Label) {}
# Add one unit of progress, refresh HUDs, and complete on target.
AdvanceProgress() : void =
set Progress = Progress + 1
for (Player -> Label : Labels):
Label.SetText(MakeProgressText())
if (Progress >= Quest.Target):
Quest.Complete()
# Compose the on-screen progress message via interpolation.
MakeProgressText() : message = StringToMessage("Collected {Progress} / {Quest.Target}")
# Helper: a string interpolation produces the message we display.
StringToMessage<localizes>(Value : string) : message = "{Value}"
# Fired when the quest completes.
OnQuestCompleted(CompletedQuest : quest) : void =
for (Player -> Label : Labels):
Label.SetText(StringToMessage("Quest complete! Rewards granted."))```
## Try it yourself
- Drag a **Trigger** device into your level and assign it to `ProgressTrigger`, then step on it five times to watch the HUD count up and the completion message appear.
- Change `Quest := collect_items_quest{ Target := 5 }` to a different target and re-test the count.
- Subscribe to `Collection.JoinEvent` in `OnBegin` to log every join, and call `GetPlayspace().PlayerAddedEvent().Subscribe(...)` to auto-enroll latecomers.
- In `OnQuestCompleted`, grant a real reward (e.g. trigger an Item Granter device) instead of just updating text.
## Recap
- `quest` is abstract — subclass it to get a concrete quest you can instantiate.
- `quest_collection{}` is instantiable and is the source of truth for participation.
- `JoinQuest` returns a `result` — call it with parens and check `GetSuccess[]`; it is **not** `<decides>`.
- A `player` is an `agent`, so it slots straight into `agent_quest_participant`.
- Use `GetPlayerUI[Player]` (fallible) + a `canvas` with a `text_block` to put live progress on the HUD, and `Quest.Complete()` to finish the goal.
Check your understanding
Test yourself with an interactive quiz and track your progress + earn XP — free for members.
Turn this into a guided course
Add Implementing Quest Logic with the Quest Framework API to your free study plan — we'll suggest related pages and stitch the lot into one compile-checked, self-guided lesson with worked examples and quizzes.
Original tutorial generated by Verse Island from the Verse/UEFN knowledge base, with references to the Epic Games sources above. Code is validated against the knowledge base.