Quest Framework Verse API

We shipped the Quest Framework API with 41.10, in experimental. Here’s what you need to know to get a jumpstart on building quests, objectives, and rewards in Verse ahead of the upcoming full release.

One building block powers all of it — the same quest primitive scales to whatever you’re building:

  • Achievements (one-time milestones)

  • Tutorials (chained steps, each unlocking the next)

  • Battle passes (tiered quest rows)

  • Daily/weekly challenges (quests on a timer)

  • Community goals (one shared objective across all players)

  • Collection sets (gather them all + a bonus “set complete” quest)

What’s a quest?

A quest is simply a completable goal. Everything else — objectives, rewards, participants — layers on top of that core building block.

You’ll spend most of your time with basic_quest, which extends adds two core concepts:

  • An objective, which tracks progress toward the goal

  • Rewards, which are granted to participants once it’s completed

Add using { /UnrealEngine.com/Progression } at the top of your Verse file to access basic_quest.

A full (simple) quest in Verse

Here’s a complete example — land 50 headshots, earn a skin:

sharpshooter_quest<public> := class(basic_quest):
    var Objective<override>:quest_objective = progress_quest_objective:
        Icon := MyIcon
        Name := SharpshooterQuestName
        Description := SharpshooterQuestDescription
        ShortDescription := SharpshooterQuestShortDescription
        RequiredCount := 50.0


    var Rewards<override>:[]quest_reward = array:
        entitlement_quest_reward:
            Icon := MyIcon
            Name := SharpshooterRewardName
            Description := SharpshooterRewardDescription
            ShortDescription := SharpshooterRewardShortDescription
            Entitlement := SniperSkin
            Quantity := 1

Call SetProgress() on the objective as headshots land. When Progress reaches RequiredCount, the quest completes and grants the reward automatically to all participants.

Setting progress

SetProgress() is how you manipulate Progress on a progress_quest_objective — you can increase or decrease it at any time.

Once Progress is greater than or equal to RequiredCount, the objective is considered complete. Because basic_quest wires its objective to the quest, a complete objective automatically completes the quest and grants its rewards. You never need to call Complete() yourself.

Self-driving quests

Instead of calling SetProgress() from outside,you can subclass progress_quest_objective and have the objective listen to game events and update itself.

Here’s an elimination counter that watches EliminatedEvent and bumps its own progress:

elimination_quest_objective := class(progress_quest_objective):
    Watch<public>(EliminationManager:elimination_manager_device, Collection:quest_collection, Quest:quest)<suspends>:void =
        loop:
            MaybeEliminator := EliminationManager.EliminationEvent.Await()
            if:
                Eliminator := MaybeEliminator?
                Memberships := Collection.Quests[Quest]
                Contributors := GetContributors(Memberships)
                Eliminator.IsContributor[Contributors]
            then:
                SetProgress(Progress + 1.0)


EliminationQuestName<localizes> : message = "Eliminate 50 enemies"
EliminationQuestDescription<localizes> : message = "Eliminate enemies tracked by the elimination manager"
EliminationQuestShortDescription<localizes> : message = "Eliminations"


elimination_quest<public> := class(basic_quest):
    var Icon<override>:texture = MyIcon
    var Name<override>:message = EliminationQuestName
    var Description<override>:message = EliminationQuestDescription
    var ShortDescription<override>:message = EliminationQuestShortDescription
    var Objective<override>:quest_objective = elimination_quest_objective:
        Icon := MyIcon
        Name := EliminationQuestName
        Description := EliminationQuestDescription
        ShortDescription := EliminationQuestShortDescription
        RequiredCount := 50.0


StartEliminationQuest(Collection:quest_collection, Player:agent, EliminationManager:elimination_manager_device):void =
    Quest := elimination_quest{}
    Participant := agent_quest_participant{ Agent := Player }
    Collection.JoinQuest(Quest, Participant, quest_participant_info{})
    if (Objective := elimination_quest_objective[Quest.Objective]):
        spawn { Objective.Watch(EliminationManager, Collection, Quest) }


(Eliminator:agent).IsContributor<public>(Contributors:[]quest_participant)<decides><reads>:void =
    first:
        Contributor : Contributors
        P := agent_quest_participant[Contributor]
        P.Agent = Eliminator
    do { P }

Because the objective manages its own progress, you don’t need any external code calling SetProgress() — and no polling loop either.

The three participation flags

Once a quest is set up, you control who takes part in it through three flags on quest_participant_info:

  • Contributes — which players can make progress

  • Observes — which players can see the quest UI

  • Receives — which players get the reward

They default to true, so quest_participant_info{} just works. Mix them for shared quests, observer modes, assisted objectives.

Joining players to quests

Join a player to a quest through the quest_collection — it’s the source of truth for who’s participating in what:

Info := quest_participant_info{}

Collection.JoinQuest(Quest, Participant, Info)

Every join returns a quest_membership — an immutable record pairing the quest, participant, and their flags.

React to quest changes instantly with events

Every object in the API — quests, objectives, rewards, collections, and participants — exposes listenable events you can subscribe to directly.

Quest.CompleteEvent.Subscribe(OnQuestComplete)

Quest.JoinEvent.Subscribe(OnPlayerJoined)

ProgressEvent, GrantEvent, AbandonEvent — they’re all there, on quests, objectives, rewards, and collections.

Toasts, banners, HUD counters — wire them directly.

Build today, ship soon

Every type in this API is experimental, so the surface may change as it stabilizes. Build with it, but plan to revisit as updates roll out. And of course, please let us know what you think of it.

For more information on the API, check out the API documentation.

Two imports, one subclass, and you’ve got a working quest. Go build!

/Verse.org/Progression + /UnrealEngine.com/Progression

This is very exciting! Thank you for working on it!

For story-driven games, some achievements may be considered spoilers. It would be nice if an Is Hidden field is added which causes the quest icon, name, and description to be hidden or something similar. When we are ready to show the achievement, we call Show() on the quest.

It’s not uncommon to want to reward XP when a quest is completed. It would be nice if quest_reward had an XP Award field with possible enum values None, Very Small, Small, Medium, Large, and Very Large matching Accolades devices. It would be even better if the XP amount causes the color of the quest cards to be different, something like how the Fortnite items have different background colors based on rarity.

These features are not critical as it is possible to implement something similar on our end.

1 Like

this is so good