Overview
A type annotation is the : TypeName you write after an identifier to tell Verse (and every future reader) exactly what kind of value lives there. Because Verse is strongly typed, the compiler rejects any mismatch — you cannot accidentally pass a float where an int is expected, or hand a raw string to a function that wants a message.
Three of the most important built-in types you'll annotate constantly are:
| Type | What it is | Typical use |
|---|---|---|
player |
A unique, persistent handle to one human in the session | Tracking who stepped on a plate, awarding XP, etc. |
vector3 |
A 3-D point or direction with float X, Y, Z components |
Spawn positions, velocities, distances |
color |
An RGB triple in the ACES 2065-1 linear color space | Tinting lights, UI, VFX |
Reach for explicit type annotations when:
- A variable's type would be ambiguous from context alone.
- You want self-documenting code that reads like a design doc.
- You're defining function parameters and return types (always annotate these).
- You're storing values in arrays, maps, or class fields.
API Reference
player
Full public surface, resolved verbatim from the live Epic digest (Verse.digest.verse). Inherited members are merged from agent.
player<native><public> := class<unique><persistent><module_scoped_var_weak_map_key><epic_internal>(agent):
vector3
3-dimensional vector with
floatcomponents.
Full public surface, resolved verbatim from the live Epic digest (Verse.digest.verse).
vector3<native><public> := struct<concrete><computes><persistable><uht_comparable>:
color
Represents colors as RGB triples in the ACES 2065-1 color space. Component values are linear (i.e.
*gamma* = 1.0).
Full public surface, resolved verbatim from the live Epic digest (Verse.digest.verse).
color<native><public> := struct<concrete><computes><persistable><uht_comparable>:
Walkthrough
Scene: The Pirate Cove Lookout
A cel-shaded pirate cove has a lookout tower. When the round begins the device:
- Records the spawn position of the tower as a
vector3. - Picks a dawn color (
color) to describe the sky. - Stores the first player who joins as a
playerreference. - Computes the horizontal distance from the tower to that player's character.
Every variable is explicitly annotated so the article can point at each annotation and explain it.
using { /Fortnite.com/Characters }
using { /Fortnite.com/Devices }
using { /Fortnite.com/Game }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /Verse.org/Colors }
using { /Fortnite.com/Playspaces }
using { /Verse.org/Verse }
# ─────────────────────────────────────────────
# cove_lookout_device
# Demonstrates type annotations with player,
# vector3, and color in a pirate-cove scenario.
# ─────────────────────────────────────────────
cove_lookout_device := class(creative_device):
# @editable fields — placed in the UEFN details panel.
# The type annotation `: player_spawner_device` tells Verse
# (and the editor) exactly what kind of device to expect.
@editable
TowerSpawner : player_spawner_device = player_spawner_device{}
# ── Localised message helper ──────────────────────────────
# `message` is a distinct type from `string`; we need this
# wrapper to pass text to functions that accept `message`.
WelcomeText<localizes>(Name : string) : message = "Ahoy, {Name}! The cove is yours."
# ── Lifecycle ─────────────────────────────────────────────
OnBegin<override>()<suspends> : void =
# ── vector3 annotation ───────────────────────────────
# vector3 holds three float components: X, Y, Z.
# We annotate TowerPos explicitly so readers know the
# coordinate type at a glance.
TowerPos : vector3 = vector3{X := 0.0, Y := 0.0, Z := 800.0}
# ── color annotation ─────────────────────────────────
# color stores linear RGB floats (ACES 2065-1 space).
# DawnSky is a warm orange — R>G>B, all in [0..1+].
DawnSky : color = color{R := 1.0, G := 0.45, B := 0.1}
# Print the dawn color components to the log so we can
# verify the values during playtesting.
# Removed the ambiguous `Log` variable to avoid compiler error 3588
# and the `Channel` initialization requirement.
Print("Dawn R={DawnSky.R} G={DawnSky.G} B={DawnSky.B}")
# ── Waiting for a player ──────────────────────────────
# GetPlayspace() returns the island's playspace; we ask
# it for the current player list.
Playspace : fort_playspace = GetPlayspace()
Players : []player = Playspace.GetPlayers()
if (Players.Length > 0):
# ── player annotation ────────────────────────────
# `player` is a class (extends `agent`) that
# represents exactly one human in the session.
# Array indexing is fallible, so bind it inside an `if`.
if (FirstSailor : player := Players[0]):
# Resolve the player to a fort_character so we can
# read their world position.
if (Character : fort_character := FirstSailor.GetFortCharacter[]):
# GetTransform() returns a transform; .Translation
# is a vector3 — we annotate it explicitly.
SailorPos : vector3 = Character.GetTransform().Translation
# ── Arithmetic on vector3 components ─────────
# vector3 components are floats; we compute the
# 2-D (XY) distance from the tower to the sailor.
DX : float = SailorPos.X - TowerPos.X
DY : float = SailorPos.Y - TowerPos.Y
# Verse has no auto int↔float conversion, so we
# keep everything as float throughout.
DistXY : float = Sqrt(DX * DX + DY * DY)
Print("Sailor is {DistXY} cm from the tower.")
# Greet the sailor with a localised message.
# GetFortCharacter() gives us a display name via
# the agent interface.
AgentName : string = "Sailor"
# player has no SendMessage method; use Print which accepts `message`.
Print(WelcomeText(AgentName))```
### Line-by-line highlights
| Line | Annotation | Why it matters |
|---|---|---|
| `TowerPos : vector3` | `vector3` | Locks the type; you can't accidentally assign a scalar. |
| `DawnSky : color` | `color` | Makes it clear this is a linear-RGB struct, not a hex string. |
| `FirstSailor : player` | `player` | Distinguishes a human player from a generic `agent` or NPC. |
| `SailorPos : vector3` | `vector3` | Documents that `GetTransform().Translation` is positional. |
| `DX : float` | `float` | Verse won't silently coerce; explicit float keeps math correct. |
| `AgentName : string` | `string` | Needed before passing to the `<localizes>` wrapper. |
## Common patterns
### Pattern 1 — Annotating a function that returns `vector3`
A helper that computes the midpoint between two cove landmarks. Explicit parameter and return-type annotations make the contract crystal-clear.
```verse
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
cove_midpoint_device := class(creative_device):
# Returns the midpoint vector3 between two positions.
# Both params and the return type are annotated.
Midpoint(A : vector3, B : vector3) : vector3 =
vector3{
X := (A.X + B.X) / 2.0
Y := (A.Y + B.Y) / 2.0
Z := (A.Z + B.Z) / 2.0
}
OnBegin<override>()<suspends> : void =
# Dock and cliff positions on the cove.
DockPos : vector3 = vector3{X := 500.0, Y := 0.0, Z := 0.0}
CliffPos : vector3 = vector3{X := -300.0, Y := 1200.0, Z := 400.0}
# The return type is vector3 — inferred here, but
# annotating it makes refactoring safer.
MeetPoint : vector3 = Midpoint(DockPos, CliffPos)
Log := log{}
Log.Print("Meet at X={MeetPoint.X} Y={MeetPoint.Y} Z={MeetPoint.Z}")
Pattern 2 — Annotating a color field and using it as a tint
A device that stores named color constants as class fields. Annotating them as color prevents accidental reassignment to an incompatible type.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
cove_palette_device := class(creative_device):
# Named color constants — annotated as `color` so the
# compiler rejects any non-color assignment.
OceanBlue : color = color{R := 0.05, G := 0.35, B := 0.8}
SandGold : color = color{R := 0.9, G := 0.75, B := 0.3}
CoralRed : color = color{R := 1.0, G := 0.2, B := 0.15}
# Picks a color by time-of-day index (0=dawn,1=noon,2=dusk).
GetSkyColor(TimeIndex : int) : color =
if (TimeIndex = 0):
color{R := 1.0, G := 0.45, B := 0.1} # dawn orange
else if (TimeIndex = 1):
color{R := 0.6, G := 0.8, B := 1.0} # noon sky
else:
color{R := 0.5, G := 0.1, B := 0.4} # dusk purple
OnBegin<override>()<suspends> : void =
NoonSky : color = GetSkyColor(1)
Log := log{}
Log.Print("Noon sky R={NoonSky.R} G={NoonSky.G} B={NoonSky.B}")
Log.Print("Ocean R={OceanBlue.R} Sand R={SandGold.R} Coral R={CoralRed.R}")
Pattern 3 — Annotating an array of player and iterating it
A device that counts how many players are standing on the cove dock and logs each one. Annotating []player makes the intent unambiguous.
using { /Fortnite.com/Characters }
using { /Fortnite.com/Devices }
using { /Fortnite.com/Game }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
cove_roster_device := class(creative_device):
OnBegin<override>()<suspends> : void =
Playspace : fort_playspace = GetPlayspace()
# Explicit []player annotation — an ordered list of
# player references, one per human in the session.
Sailors : []player = Playspace.GetPlayers()
Log := log{}
Log.Print("Sailors at the cove: {Sailors.Length}")
# Iterate with a for-expression; Index and Sailor are
# inferred, but we could annotate them too.
for (Index -> Sailor : Sailors):
# Annotating CharOpt as ?fort_character documents
# that GetFortCharacter[] is failable.
if (Character : fort_character = Sailor.GetFortCharacter[]):
Pos : vector3 = Character.GetTransform().Translation
Log.Print("Sailor {Index} is at Z={Pos.Z}")
Gotchas
1. int and float are never auto-converted
Verse will not silently promote an int to a float or vice-versa. If you write DX : float = SomeInt the compiler errors. Use Float(SomeInt) to convert, or keep your literals consistent (0.0 not 0).
# ✗ Compile error — int literal where float expected
# BadZ : float = 800
# ✓ Correct
GoodZ : float = 800.0
2. string ≠ message — always wrap for localised text
Functions that display text to players (like SendMessage) require a message, not a bare string. Declare a <localizes> wrapper:
Greeting<localizes>(Name : string) : message = "Welcome, {Name}!"
There is no StringToMessage function — the wrapper is the only way.
3. player is a subtype of agent — don't confuse them
player extends agent, so a player can go anywhere an agent is expected, but not the reverse. If a function returns agent you must cast down with if (P : player = SomeAgent) before using player-specific APIs.
4. color components are linear floats, not 0–255 bytes
color{R := 1.0, G := 1.0, B := 1.0} is white. Values above 1.0 are valid (HDR). Never pass integer byte values — color{R := 255.0, ...} will produce a blindingly over-exposed result.
5. vector3 components are float — arithmetic must stay float
All three fields (X, Y, Z) are float. Mixed-type expressions like Pos.X + 5 (where 5 is an int literal) will fail. Write Pos.X + 5.0.
6. Annotating @editable fields requires a default value
Every @editable class field must have a default initialiser even when the creator will override it in the details panel. Omitting the default causes a compile error:
# ✓ Correct — default provided
@editable
MySpawner : player_spawner_device = player_spawner_device{}