Reference Verse compiles

default-value-fallback: Safe Defaults in Verse UI

Every island moment — a glittering cove, a clifftop vista, a wooden dock at golden hour — deserves a HUD that never crashes. Verse's default-value-fallback pattern (the `or` operator on option types, plus the `Default*` initialization fields on widgets like `color_block`) lets you write UI code that gracefully handles missing data instead of failing silently or panicking. Master this pattern once and your island's UI will be rock-solid no matter what the runtime throws at it.

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

Overview

In Verse, many values are optional (?T). When you try to unwrap an empty option without a fallback, your expression simply fails — and in a UI context that usually means a blank screen or a silent no-op. The default-value-fallback pattern solves this with two complementary tools:

  1. The or operatorMaybeValue? or FallbackValue — unwraps an option and substitutes a safe default when it is empty.
  2. Default* fields on widgetscolor_block, texture_block, and friends expose DefaultColor, DefaultOpacity, DefaultDesiredSize, etc. These are the values the widget is initialized with; you set them at construction time and then use SetColor / SetOpacity at runtime to change them.

Together these two tools let you write UI widgets that always display something sensible — a fallback colour, a safe size — even when your game data hasn't loaded yet or a player slot is empty. Reach for this pattern whenever you build HUD elements that depend on runtime state that might be absent: health bars, score panels, zone indicators on a clifftop, tide-level meters on a dock.

API Reference

(API surface could not be resolved for this device.)

Walkthrough

Scenario: You're building a sunny cove island where up to four players race to collect pearls. A small HUD panel shows each player's "zone colour" — Coral for zone 1, Aquamarine for zone 2, etc. — as a solid color_block. If a player slot is empty the panel falls back to a dim grey so the layout never breaks.

The device below:

  • Builds a color_block with safe Default* initialization values.
  • Uses SetColor to apply a zone colour at runtime.
  • Uses SetOpacity to dim the block when no player occupies that slot.
  • Demonstrates the or fallback operator to pick a colour from an optional map lookup.
using { /UnrealEngine.com/Temporary/UI }
using { /Verse.org/Colors }
using { /Verse.org/Colors/NamedColors }
using { /Verse.org/Simulation }
using { /Fortnite.com/Devices }
using { /Fortnite.com/UI }
using { /UnrealEngine.com/Temporary/SpatialMath }

# Localised helper — color_block doesn't need message, but kept for future text widgets
cove_hud_device := class(creative_device):

    # ----------------------------------------------------------------
    # The zone colours for up to 4 player slots on the cove island.
    # Slot 0 = Coral (zone 1), Slot 1 = Aquamarine (zone 2),
    # Slot 2 = CornflowerBlue (zone 3), Slot 3 = Crimson (zone 4).
    # ----------------------------------------------------------------

    # Fallback colour shown when a slot has no player.
    FallbackColor : color = color{R:=0.15, G:=0.15, B:=0.15}

    # Dimmed opacity for empty slots; full opacity for occupied slots.
    DimOpacity  : type{_X:float where 0.0 <= _X, _X <= 1.0} = 0.25
    FullOpacity : type{_X:float where 0.0 <= _X, _X <= 1.0} = 1.0

    # The four zone colours indexed 0-3.
    ZoneColors : []color = array:
        NamedColors.Coral
        NamedColors.Aquamarine
        NamedColors.CornflowerBlue
        NamedColors.Crimson

    # We keep a reference to each block so we can update it later.
    var SlotBlocks : []color_block = array{}

    OnBegin<override>()<suspends> : void =
        # Build one color_block per slot.
        var Built : []color_block = array{}
        for (SlotIndex := 0..3):
            # --- DEFAULT-VALUE FALLBACK: pick zone colour or grey ---
            # ZoneColors[SlotIndex] can fail if the array is shorter
            # than expected; `or FallbackColor` guarantees a safe value.
            SlotColor := if (C := ZoneColors[SlotIndex]) then C else FallbackColor

            Block := color_block:
                # Default* fields set the INITIAL state at construction.
                DefaultColor       := SlotColor
                DefaultOpacity     := DimOpacity          # start dimmed
                DefaultDesiredSize := vector2{X:=64.0, Y:=64.0}

            set Built = Built + array{Block}

        set SlotBlocks = Built

        # Simulate slot 0 and slot 2 being occupied at game start.
        ActivateSlot(0)
        ActivateSlot(2)

        # After 5 seconds a third player arrives at the dock — slot 1 lights up.
        Sleep(5.0)
        ActivateSlot(1)

        # After another 5 seconds slot 3 activates (player swims in from the cove).
        Sleep(5.0)
        ActivateSlot(3)

    # Called when a player occupies a slot.
    # Uses SetColor + SetOpacity — the runtime mutators (not Default* fields).
    ActivateSlot(SlotIndex : int) : void =
        if (Block := SlotBlocks[SlotIndex]):
            # Pick the zone colour; fall back to FallbackColor with `or`.
            ZoneColor := ZoneColors[SlotIndex] or FallbackColor
            Block.SetColor(ZoneColor)
            Block.SetOpacity(FullOpacity)

    # Called when a player leaves — dims the slot back to grey.
    DeactivateSlot(SlotIndex : int) : void =
        if (Block := SlotBlocks[SlotIndex]):
            Block.SetColor(FallbackColor)
            Block.SetOpacity(DimOpacity)```

### Line-by-line highlights

| Lines | What's happening |
|---|---|
| `DefaultColor := SlotColor` | Sets the widget's **initial** colour at construction. This field is only read once  use `SetColor` for all later changes. |
| `DefaultOpacity := DimOpacity` | All slots start dimmed (`0.25`). The `Default*` field bakes this in at init time. |
| `ZoneColors[SlotIndex] or FallbackColor` | Classic `or` fallback: if the array lookup fails (index out of range), the expression yields `FallbackColor` instead of failing. |
| `Block.SetColor(ZoneColor)` | Runtime colour change  `Default*` fields are ignored after construction; always call the setter. |
| `Block.SetOpacity(FullOpacity)` | Brings the slot to full brightness when a player arrives. |

## Common patterns

### Pattern 1 — `GetColor` + `or` to read-then-modify

Read the current colour, nudge it slightly, and write it back. The `or` guards against any unexpected failure path.

```verse
using { /UnrealEngine.com/Temporary/UI }
using { /Verse.org/Colors }
using { /Verse.org/Colors/NamedColors }
using { /Verse.org/Simulation }
using { /Fortnite.com/Devices }

# Pulses a dock-warning color_block between its current colour and red.
dock_warning_pulse_device := class(creative_device):

    # The color_block that acts as a warning light on the dock.
    WarningBlock : color_block = color_block:
        DefaultColor       := NamedColors.Aquamarine
        DefaultOpacity     := 1.0
        DefaultDesiredSize := vector2{X:=32.0, Y:=32.0}

    OnBegin<override>()<suspends> : void =
        # Store the original colour so we can restore it after each pulse.
        OriginalColor := WarningBlock.GetColor()

        # Pulse red three times to warn players the tide is coming in.
        var PulseCount : int = 0
        loop:
            if (PulseCount >= 3):
                break
            WarningBlock.SetColor(NamedColors.Crimson)
            Sleep(0.4)
            WarningBlock.SetColor(OriginalColor)
            Sleep(0.4)
            set PulseCount = PulseCount + 1

Pattern 2 — GetDesiredSize + SetDesiredSize for responsive layout

Read the current size, double it for a "pearl collected" pop animation, then restore it.

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

# Pops a pearl-counter block larger when a pearl is collected.
pearl_pop_device := class(creative_device):

    PearlBlock : color_block = color_block:
        DefaultColor       := NamedColors.Cornsilk
        DefaultOpacity     := 1.0
        DefaultDesiredSize := vector2{X:=48.0, Y:=48.0}

    OnBegin<override>()<suspends> : void =
        # Simulate three pearl pickups at the cove shore.
        var Collected : int = 0
        loop:
            if (Collected >= 3):
                break
            Sleep(2.0)
            PopBlock()
            set Collected = Collected + 1

    PopBlock() : void =
        # Read current size, scale up, then restore — all via the getter/setter pair.
        CurrentSize := PearlBlock.GetDesiredSize()
        BigSize := vector2{X:=CurrentSize.X * 2.0, Y:=CurrentSize.Y * 2.0}
        PearlBlock.SetDesiredSize(BigSize)
        # In a real device you'd Sleep then restore; shown inline for clarity.
        PearlBlock.SetDesiredSize(CurrentSize)

Pattern 3 — or operator on ?int game data

The or fallback isn't limited to widgets — it's the universal Verse tool for any optional value. Here it guards a pearl-count lookup.

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

# Reads a player's pearl count from a map; falls back to 0 if not found.
pearl_score_reader_device := class(creative_device):

    # Simulated pearl counts keyed by slot index.
    PearlCounts : [int]int = map{0 => 12, 2 => 7}

    # A colour block whose opacity reflects the score fraction (0-20 pearls max).
    ScoreBlock : color_block = color_block:
        DefaultColor       := NamedColors.Aquamarine
        DefaultOpacity     := 0.1
        DefaultDesiredSize := vector2{X:=80.0, Y:=16.0}

    OnBegin<override>()<suspends> : void =
        # Slot 1 has no entry — `or 0` provides the safe fallback.
        SlotOnePearls  := PearlCounts[1] or 0
        SlotZeroPearls := PearlCounts[0] or 0

        # Drive opacity from score fraction; clamp to [0.1, 1.0].
        MaxPearls : float = 20.0
        Fraction  : float = SlotZeroPearls / MaxPearls
        SafeFraction := if (Fraction > 1.0) then 1.0 else if (Fraction < 0.1) then 0.1 else Fraction
        ScoreBlock.SetOpacity(SafeFraction)
        # Slot 1 would show 0.1 opacity (the fallback minimum) — never crashes.
        _ = SlotOnePearls  # acknowledged; would drive a second block in a full device

Gotchas

Default* fields are init-only — always use setters at runtime

DefaultColor, DefaultOpacity, and DefaultDesiredSize are read once when the widget is constructed. Mutating them after construction has no effect. Use SetColor, SetOpacity, and SetDesiredSize for every runtime change. Think of Default* as the widget's "blueprint" and the setters as its "remote control".

or only works on failable expressions

The or operator requires the left-hand side to be a failable expression (one that can fail, like an option unwrap ? or an array index). Writing SomeNonFailableValue or Fallback is a compile error. Wrap your risky lookup first: ZoneColors[Index] or FallbackColor — the array index is failable, so or is valid.

DefaultOpacity is a constrained float, not a plain float

The type is type {_X:float where 0.000000 <= _X, _X <= 1.000000}. Passing a literal like 1.0 or 0.25 works fine, but passing a computed float variable requires you to ensure it is in range first — the compiler enforces the constraint. Use an explicit clamp (if (F > 1.0) then 1.0 else ...) before assigning to SetOpacity.

GetColor / GetOpacity return current runtime values, not Default* values

If you call GetColor() before any SetColor() call, you get the value that was set via DefaultColor at construction — which is correct. But if another system has called SetColor in between, GetColor reflects that change, not the original default. Cache your intended defaults in Verse fields if you need to restore them.

int and float do not auto-convert

When computing sizes or opacities from integer game data (pearl counts, scores), you must cast explicitly: SlotZeroPearls / MaxPearls only works if both sides are float. Divide an int pearl count by a float max by writing SlotZeroPearls * 1.0 / MaxPearls or storing the count as float from the start.

Build your own lesson with default_value_fallback

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 →