Reference Devices compiles

vote_group_device: Run Real In-Game Polls

Want players to vote on the next map, the next boss, or what's for lunch? The Vote Group device is the 'question' that owns a poll, while Vote Option devices are the answers. This guide shows how to wire them up in Verse, start a vote, and react to each ballot.

Updated Examples verified on the live UEFN compiler
Watch the Knotvote_group_device in ~90 seconds.

Overview

The Vote Group device (vote_group_device) represents a single poll — the question. Think of a poll like “What map do we play next?” The group owns the overall state of the poll: when voting opens, when it closes, and which options belong to it.

The actual answers are separate Vote Option devices, each implementing vote_option_interface. Each option knows how to cast a vote for an agent, count its votes, and fire an event when a ballot lands on it.

Reach for this device when you want a structured, player-driven decision: choosing the next round’s game mode, letting a lobby pick a boss difficulty, or a quick “yes/no” to skip a cutscene. The group is the orchestrator; the options are the buttons players actually pick.

The key API split:

  • vote_group_deviceBeginVote() opens voting; BeginVoteEvent fires when it starts.
  • vote_option_interface (the options) — CastVote[Agent] records a ballot, CastVoteEvent fires per vote, GetVoteCount() reads the tally, GetOptionDescription() returns the displayed label.

API Reference

vote_group_device

Represents a poll. For example, in a poll “What to have for lunch?” the group represents the question and contains the possible answers. Owns the overall state of the poll and keeps track of available options. A group can have multiple options, connected via an internal ID.

Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse).

vote_group_device<public> := class<concrete><final>(creative_device_base):

Walkthrough

Let’s build a “What’s for lunch?” poll. We place one Vote Group device and two Vote Option devices (Pizza and Tacos) in the level and reference all three from a Verse device. When the match begins we open voting, then we log every ballot as it comes in and announce the winner when the trigger fires.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /UnrealEngine.com/Temporary/UI }

lunch_poll := class(creative_device):

    # The poll itself — the question "What's for lunch?"
    @editable
    VoteGroup : vote_group_device = vote_group_device{}

    # The two answer options. These are Vote Option devices in the level.
    @editable
    PizzaOption : vote_option_device = vote_option_device{}

    @editable
    TacosOption : vote_option_device = vote_option_device{}

    # A trigger a host steps on to close voting and announce the winner.
    @editable
    CloseTrigger : trigger_device = trigger_device{}

    MyText<localizes>(S : string) : message = "{S}"

    OnBegin<override>()<suspends> : void =
        # Open the poll so options can accept ballots.
        VoteGroup.BeginVote()

        # React when each option receives a vote.
        PizzaOption.CastVoteEvent.Subscribe(OnPizzaVote)
        TacosOption.CastVoteEvent.Subscribe(OnTacosVote)

        # Close + announce when the host triggers.
        CloseTrigger.TriggeredEvent.Subscribe(OnCloseVote)

    OnPizzaVote(Agent : agent) : void =
        Print("A ballot landed on Pizza! Total: {PizzaOption.GetVoteCount()}")

    OnTacosVote(Agent : agent) : void =
        Print("A ballot landed on Tacos! Total: {TacosOption.GetVoteCount()}")

    OnCloseVote(MaybeAgent : ?agent) : void =
        Pizza := PizzaOption.GetVoteCount()
        Tacos := TacosOption.GetVoteCount()
        if (Pizza > Tacos):
            Print("Pizza wins with {Pizza} votes!")
        else if (Tacos > Pizza):
            Print("Tacos win with {Tacos} votes!")
        else:
            Print("It's a tie at {Pizza} each!")

Line by line:

  • The three @editable device fields (VoteGroup, PizzaOption, TacosOption) plus CloseTrigger let you drag the placed devices onto this Verse device in the Details panel. Without these fields you cannot call the devices at all.
  • MyText<localizes> is the helper you need whenever an API wants a message instead of a raw string. We keep it ready for HUD/UI use.
  • In OnBegin, VoteGroup.BeginVote() opens the poll. Until this runs, CastVote on any option fails — the group must have started voting.
  • We Subscribe to each option’s CastVoteEvent. A listenable(agent) hands the handler an agent directly, so OnPizzaVote(Agent : agent) matches the signature.
  • GetVoteCount() returns an int total for that option — we print the running tally each time a ballot arrives.
  • OnCloseVote is a TriggeredEvent handler. Trigger events pass ?agent (optional), so the parameter is MaybeAgent : ?agent. We don’t need the agent here, so we ignore it and just compare the two totals to declare a winner.

Common patterns

1. Manually casting a vote in code (CastVote)

Votes usually come from players interacting with the option in-game, but you can also cast one programmatically — for example, an NPC ally or a default fallback vote. CastVote is a <decides> function, so it can fail (no votes left, or voting not open) and must be called in a failure context like if.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }

auto_voter := class(creative_device):

    @editable
    VoteGroup : vote_group_device = vote_group_device{}

    @editable
    DefaultOption : vote_option_device = vote_option_device{}

    @editable
    StartPlate : trigger_device = trigger_device{}

    OnBegin<override>()<suspends> : void =
        VoteGroup.BeginVote()
        StartPlate.TriggeredEvent.Subscribe(OnPlate)

    OnPlate(MaybeAgent : ?agent) : void =
        if (Agent := MaybeAgent?):
            # Try to record a ballot for this agent on the default option.
            if (DefaultOption.CastVote[Agent]):
                Print("Default vote cast successfully")
            else:
                Print("Could not cast vote (no votes left or poll closed)")

2. Showing each option’s label (GetOptionDescription)

GetOptionDescription() returns the message text shown to players for that option. Handy for building a summary HUD or logging what the choices are when voting opens.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }

poll_announcer := class(creative_device):

    @editable
    VoteGroup : vote_group_device = vote_group_device{}

    @editable
    OptionA : vote_option_device = vote_option_device{}

    @editable
    OptionB : vote_option_device = vote_option_device{}

    OnBegin<override>()<suspends> : void =
        # Announce the choices, then open the poll.
        Print("Vote now! Option A and Option B are available.")
        VoteGroup.BeginVote()
        # Read back each option's display label.
        DescA : message = OptionA.GetOptionDescription()
        DescB : message = OptionB.GetOptionDescription()
        Print("Options ready to receive votes.")

3. Reacting when the poll opens (BeginVoteEvent)

The group fires BeginVoteEvent when voting starts. Subscribe to it to kick off a countdown, play a sound, or reveal the option buttons — instead of hard-coding logic right after your BeginVote() call.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }

poll_opener := class(creative_device):

    @editable
    VoteGroup : vote_group_device = vote_group_device{}

    @editable
    StartTrigger : trigger_device = trigger_device{}

    OnBegin<override>()<suspends> : void =
        # Listen for the poll opening.
        VoteGroup.BeginVoteEvent.Subscribe(OnVoteOpened)
        # Let a host open the poll by stepping on a plate.
        StartTrigger.TriggeredEvent.Subscribe(OnStart)

    OnStart(MaybeAgent : ?agent) : void =
        VoteGroup.BeginVote()

    OnVoteOpened() : void =
        Print("The poll is now open — cast your votes!")

Gotchas

  • You must call BeginVote() before any vote counts. CastVote[Agent] fails if the group hasn’t started voting yet. If players “vote” but nothing registers, check that BeginVote() actually ran.
  • CastVote is <decides> — it can fail (agent out of votes, poll not open) and therefore MUST be called inside a failure context, e.g. if (Option.CastVote[Agent]):. Calling it bare won’t compile.
  • The group and the options are different devices. vote_group_device owns the poll state but you cast/count votes on the Vote Option devices via vote_option_interface. Reference both in your Verse device.
  • CastVoteEvent is listenable(agent), but trigger.TriggeredEvent is listenable(?agent). The first hands your handler a plain agent; the second hands an ?agent you must unwrap with if (A := MaybeAgent?):. Mixing them up causes a signature mismatch.
  • GetOptionDescription() returns a message, not a string. Don’t try to concatenate it like a string — pass it to UI/HUD APIs that accept message. To build your own message from a string, use a <localizes> helper; there is no StringToMessage.
  • GetVoteCount() returns an int. Verse does not auto-convert int↔float, so don’t mix it into float math without an explicit conversion.

Build your own lesson with vote_group_device

Generate a personalized, step-by-step lesson plan built around this object — grounded in this exact reference and our compile-verified knowledge base.

Build a lesson →