Polymorphic Powerup Classes with Player UI Feedback in Verse
Tutorialintermediatecompiles

Polymorphic Powerup Classes with Player UI Feedback in Verse

Updated intermediateCode verified

What you'll learn

You'll define a powerup_base class with a <virtual> Apply method, then derive freeze_powerup and poison_powerup that <override> it with their own behavior. Because every derived type shares the base type, you can store them all in one []powerup_base array and call Apply without caring which concrete class you're holding — that's polymorphism. We then surface the result to the player by building a real text_block, wrapping it in a canvas, and adding it through GetPlayerUI[] so feedback genuinely renders on screen.

How it works

Verse polymorphism lets a derived class stand in anywhere its base type is expected. The base declares Apply<virtual>(...); each subclass supplies Apply<override>(...). At runtime, the actual instance decides which body runs — so iterating one array and calling Pwr.Apply(...) dispatches to the correct freeze or poison logic automatically.

For the UI, Player.GetPlayerUI[] is a fallible call (note the []), so we bind it inside an if. We construct a text_block, set its text with SetText, place it on a canvas via a canvas_slot, and hand that canvas to UI.AddWidget(...). Because each powerup carries its own DisplayName : message, the same UI code produces different text per type — no branching required.

Let's build it

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

# Base class: every powerup IS-A powerup_base.
powerup_base := class<abstract>:
    # Shared data every powerup carries.
    var DisplayName : string = "Unknown Powerup"

    # Virtual method — derived classes override the behavior.
    Apply(Agent : agent) : void =
        Print("Base Apply (should be overridden)")

# Freeze variant overrides Apply with its own logic + name.
freeze_powerup := class(powerup_base):
    Apply<override>(Agent : agent) : void =
        set DisplayName = "Freeze!"
        Print("Freezing target")

# Poison variant — same interface, different behavior.
poison_powerup := class(powerup_base):
    Apply<override>(Agent : agent) : void =
        set DisplayName = "Poison!"
        Print("Poisoning target")

polymorphic_powerup_device := class<concrete>(creative_device):

    # One mixed list, treated uniformly through the base type.
    Powerups : []powerup_base = array{ freeze_powerup{}, poison_powerup{} }

    OnBegin<override>()<suspends>: void =
        # Reach the first player; GetPlayers[] is fallible, so guard it.
        for (Player : GetPlayspace().GetPlayers()):
            # Polymorphic dispatch: each Apply runs its own override.
            for (Pwr : Powerups):
                Pwr.Apply(Player)
                ShowFeedback(Player, Pwr.DisplayName)

    # Builds a real text widget and renders it via the Player UI.
    ShowFeedback(Player : player, Text : string) : void =
        if (UI := GetPlayerUI[Player]):
            TextWidget := text_block{ DefaultText := ToMessage(Text) }
            # Wrap the text in a canvas so the UI layer can position it.
            Canvas := 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}
                        SizeToContent := true
                        Widget := TextWidget
            # AddWidget makes the canvas (and our text) actually appear.
            UI.AddWidget(Canvas)

ToMessage<localizes>(Value : string) : message = "{Value}"```

## Try it yourself

- Add a third subclass, e.g. `heal_powerup := class(powerup_base):` with its own `Apply<override>` and `DisplayName`, then drop `heal_powerup{}` into the `Powerups` array — no other code changes needed. That's the payoff of polymorphism.
- Give `powerup_base` a second virtual method like `Duration<virtual>() : float` and override it per type; read it in `ShowFeedback` to vary how long text stays up.
- Pass `Player` through to a real gameplay device (e.g. damage or movement modifier) inside each `Apply` so the effect does more than print.

## Recap

You built a base `powerup_base` class with a `<virtual>` `Apply`, derived two specialized powerups that `<override>` it, and looped over a single `[]powerup_base` array to dispatch behavior polymorphically. You also wired each activation to a real on-screen `text_block` inside a `canvas`, added through `GetPlayerUI[]`/`AddWidget`, so players see distinct feedback per powerup. Adding new powerups now means one new subclass — not another copy of the same code.

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 Polymorphic Powerup Classes 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.

Comments

    Sign in to vote, comment, or suggest an edit.Sign in