What you'll learn
- How Verse expresses "multiple return values" with tuples like
tuple(logic, logic)and destructuring via.0/.1. - How
<decides>calls such asTracker.IsActive[Agent]andTracker.HasReachedTarget[Agent]"return" a success/failure that you read in anif— they do NOT returnlogic, so you never call them bare. - How overloaded device methods (
GetValue()vsGetValue(Agent)) let you pick which value to "return". - How to wire a trigger event to read all of these at once.
How it works
Verse gives you two complementary tools:
-
Tuples pack several values into one. A function typed
tuple(logic, logic)returns two values; you build one by stating(A, B)as the final expression and read it back withResult(0)/Result(1). -
<decides>functions are fallible.CoinTracker.IsActive[Agent]uses[]brackets because it may fail. It never hands back alogic— it either succeeds (thethen:branch runs) or fails (theelse:runs). To turn that success/failure INTO alogicyou can pass around, wrap it in anifand set a flag.
⚠️ Common mistake: writing
CoinTracker.IsActive[A]as a bare statement.[]fallible calls may ONLY appear in a failure context — anif/forcondition, a<decides>body, or anot/oroperand.
- Overloads:
GetValue()returns the whole-match value,GetValue(Agent:agent)returns that player's value, andGetValue(TeamIndex:int)returns the team's value. These use()parens because they simply return anint— they are not fallible. Choosing the overload is how you "return" the value you actually want.
We combine IsActive and HasReachedTarget into ONE helper that returns both as a tuple(logic, logic), so the caller gets two answers from one call, then reads the numeric value with a GetValue overload.
Let's build it
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
# Finish-line challenge: read three answers from one trigger check-in.
finish_line_challenge := class<concrete>(creative_device):
# The plate a player steps on to "check in" at the finish line.
@editable
CheckPlate : trigger_device = trigger_device{}
# The Tracker counting each player's coins.
@editable
CoinTracker : tracker_device = tracker_device{}
# Returns TWO answers at once as a tuple:
# result(0) = is the tracker active for this agent?
# result(1) = has this agent reached the target?
# Both come from <decides> calls folded into plain logic values.
ReadStatus(A : agent) : tuple(logic, logic) =
var WasActive : logic = false
# IsActive[A] is fallible — read its success inside an if.
if (CoinTracker.IsActive[A]):
set WasActive = true
var WasDone : logic = false
if (CoinTracker.HasReachedTarget[A]):
set WasDone = true
# State the tuple as the final expression to "return" both.
(WasActive, WasDone)
OnBegin<override>()<suspends> : void =
# Device events subscribe WITHOUT () after the event name.
CheckPlate.TriggeredEvent.Subscribe(OnCheckIn)
OnCheckIn(Agent : ?agent) : void =
# TriggeredEvent hands us ?agent — unwrap it before use.
if (A := Agent?):
# One call, two answers via tuple destructuring.
Result := ReadStatus(A)
WasActive := Result(0)
WasDone := Result(1)
# Overloaded GetValue(Agent) returns THIS player's value (int).
PlayerValue := CoinTracker.GetValue(A)
# GetValue() with no args returns the whole-match value.
MatchValue := CoinTracker.GetValue()
if (WasDone?):
Print("Finished! Player coins = {PlayerValue}, match total = {MatchValue}")
else if (WasActive?):
Print("Still tracking. Player coins = {PlayerValue}")
else:
Print("Tracker not active for this player yet.")
Try it yourself
- Place a Tracker device and a Trigger device in your level.
- Add this Verse device, then assign
CoinTrackerandCheckPlatein its Details panel. - Configure the Tracker to count a stat and set a Target Value.
- Step on the trigger before and after reaching the target — watch the log print the correct branch each time.
- Extend it: add a
GetValue(TeamIndex:int)read for the player's team total, or useSetValue(A, 0)to reset a player's counter after they finish.
Recap
- Verse has no
return a, b, c; use a tuple return (tuple(logic, logic)) and read fields withResult(0)/Result(1). <decides>calls likeIsActive[A]andHasReachedTarget[A]use[]and may ONLY appear in a failure context — read their success in anif, never call them bare.- Overloaded
GetValue()/GetValue(Agent)/GetValue(TeamIndex)use()and return anintdirectly — pick the overload to choose which value you "return". - Device events (
TriggeredEvent) subscribe without(); unwrap?agentwithif (A := Agent?):before using it.