Quest Framework in Verse: Join and Reward with Live UI
What you'll learn
You'll learn how the experimental Quest Framework classes fit together and how to surface their results to players:
- A
quest_collection(from/Verse.org/Progression) is the source of truth for participation. It isclass<unique>and IS instantiable — soquest_collection{}is valid. questisclass<abstract>— you CANNOT writequest{}. You must declare a concrete subclass (herecollect_coins_quest := class(quest):) and instantiate that.- An
agent_quest_participant{ Agent := SomeAgent }wraps any agent. AplayerIS anagent, so you pass it straight in — no cast. Collection.JoinQuest(...)is a<transacts>call that returns aresult— NOT a<decides>function. Call it with parens and inspect the result.- Real player-UI calls (
GetPlayerUI[],AddWidget,canvas,text_block) turn invisible quest logic into visible feedback.
How it works
The Quest Framework lives in /Verse.org/Progression — there is no "quest device." You work with the API classes directly:
- Hold one
quest_collectionand one concrete quest subclass on your device. - On a button interaction, wrap the interacting agent as an
agent_quest_participant. - Call
Collection.JoinQuest(Quest, Participant, Info).
The #1 mistake people make here: JoinQuest returns result(void, []join_quest_error). It is not a <decides> function, so you must NOT call it as JoinQuest[...] inside an if condition. Instead, call it with parens and READ the result:
JoinResult := Collection.JoinQuest(Quest, Participant, Info)
if (JoinResult.GetSuccess[]): # GetSuccess[] IS the <decides> query on the result
# join committed
GetSuccess[] is the fallible accessor exposed by result, so it belongs in the failure context (the if), while JoinQuest(...) itself is a plain side-effecting call.
Throughout, we fetch the player's UI with GetPlayerUI[player[Agent]] and AddWidget a canvas holding a text_block so the player gets immediate on-screen feedback — that's the part that turns logic into a real experience.
Let's build it
This device subscribes to a button. On interaction it joins the interacting player to the quest and shows the result on screen; a CompleteEvent subscription also notifies players when the quest finishes.
using { /Fortnite.com/Devices }
using { /Verse.org/Progression }
using { /UnrealEngine.com/Temporary/UI }
using { /Verse.org/Simulation }
using { /Verse.org/Colors }
using { /Fortnite.com/UI }
using { /UnrealEngine.com/Temporary/SpatialMath }
# quest is abstract — declare a CONCRETE subclass so we can instantiate it.
collect_coins_quest := class(quest):
# Illustrative target shown in the join message.
Target : int = 3
quest_framework_device := class<concrete>(creative_device):
# Players interact with this to join the quest.
@editable StartButton : button_device = button_device{}
# The source of truth for participation. quest_collection IS instantiable.
Collection : quest_collection = quest_collection{}
# The shared concrete quest every joining player works on.
Quest : collect_coins_quest = collect_coins_quest{ Target := 3 }
OnBegin<override>()<suspends> : void =
# Observe completion; Subscribe returns a cancelable we can ignore here.
Collection.CompleteEvent.Subscribe(OnQuestCompleted)
# Wire the button to our join handler.
StartButton.InteractedWithEvent.Subscribe(OnStart)
OnStart(Agent : agent) : void =
# A player IS an agent, so it drops straight into a participant.
Participant := agent_quest_participant{ Agent := Agent }
# This participant can progress, observe, and earn rewards.
Info := quest_participant_info:
Contributes := true
Observes := true
Receives := true
# JoinQuest RETURNS result(void, array{}join_quest_error) — call with parens,
# then inspect with GetSuccess[] (the <decides> query on the result).
JoinResult := Collection.JoinQuest(Quest, Participant, Info)
if (JoinResult.GetSuccess[]):
Notify(Agent, "Quest joined! Collect {Quest.Target} coins.")
else:
Notify(Agent, "Could not join quest.")
# Fired by the collection when any quest in it completes.
OnQuestCompleted(CompletedQuest : quest) : void =
for (Player : GetPlayspace().GetPlayers()):
Notify(Player, "Quest complete — reward granted!")
# Push a text label onto the player's UI canvas (the real, visible feedback).
Notify(Agent : agent, Msg : string) : void =
if:
P := player[Agent] # narrow agent -> player (fallible)
PlayerUI := GetPlayerUI[P] # GetPlayerUI is <decides> — use []
then:
Label := text_block{ DefaultText := StringToMessage(Msg), DefaultTextColor := NamedColors.White }
Root := canvas:
Slots := array:
canvas_slot:
Anchors := anchors{ Minimum := vector2{ X := 0.5, Y := 0.1 }, Maximum := vector2{ X := 0.5, Y := 0.1 } }
Alignment := vector2{ X := 0.5, Y := 0.0 }
Widget := Label
PlayerUI.AddWidget(Root)
# Helper: text_block.DefaultText expects a message, so wrap the string.
StringToMessage<localizes>(Value : string) : message = "{Value}"```
## Try it yourself
- Add a second `button_device` wired to an `AbandonQuest(Quest, Participant)` call and show its result the same way (it returns `result(void, []abandon_quest_error)` — inspect with `GetSuccess[]`).
- Track real progress: give the device a `trigger_device`, advance a per-participant counter, and signal completion from a custom objective subclass.
- Subscribe to `Collection.JoinEvent` too and print the `quest_membership.Quest` payload so you can confirm joins in the log.
- Style the feedback: change `DefaultTextColor` or add multiple `canvas_slot`s for a layout.
## Recap
- The Quest Framework lives in `/Verse.org/Progression`; instantiate a `quest_collection`, and a **concrete** subclass of the abstract `quest`.
- Wrap agents with `agent_quest_participant{ Agent := ... }` — a player is an agent.
- `JoinQuest(...)` **returns a `result`** — call it with **parens** and branch on `JoinResult.GetSuccess[]`. Calling `JoinQuest[...]` as a `<decides>` function is wrong and won't compile.
- Use `GetPlayerUI[player[Agent]]` (it's `<decides>`, so `[]`) and `AddWidget` a `canvas`/`text_block` to give players real, visible feedback.
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 the Quest Framework API in Verse 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.