Reference Verse compiles

math Abs(): Distance Without Direction on the Cove

The sun is high over the cove, gulls circling the pirate ship anchored in the lagoon. A treasure chest sits on the dock — and you want a trigger to fire ONLY when a player's position lines up near it, no matter which side they approach from. That 'no matter which side' is exactly what `Abs()` gives you: distance from a target without caring about the sign.

Updated Examples verified on the live UEFN compiler

Overview

Abs() is one of Verse's intrinsic math functions. It returns the absolute value of a number — its distance from zero, with the sign thrown away. Abs(-7.0) is 7.0; Abs(7.0) is also 7.0.

Why does a game care? Because positions can be left or right, above or below, ahead or behind a target. When you subtract two coordinates you get a signed difference — negative if the player is on one side, positive on the other. If all you want is "how far apart are they?", you wrap the subtraction in Abs() and stop worrying about which direction.

On our sunny cove, that lets us fire a trigger_device when a player stands close to a treasure chest along the dock's X axis — approaching from the bow or the stern both count. Abs() works on both int and float; the vector components we read off positions are float, so we work in floats. Reach for Abs() whenever you need a magnitude — a gap, a delta, a tolerance — rather than a signed offset.

API Reference

vector3

3-dimensional vector with float components.

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

vector3<native><public> := struct<concrete><computes><persistable><uht_comparable>:

trigger_device

Used to relay events to other linked devices.

Full public surface, resolved verbatim from the live Epic digest (Fortnite.digest.verse). Inherited members are merged from trigger_base_device.

trigger_device<public> := class<concrete><final>(trigger_base_device):

Events (subscribe a handler to react):

Event Signature Description
TriggeredEvent TriggeredEvent<public>:listenable(?agent) Signaled when an agent triggers this device. Sends the agent that used this device. Returns false if no agent triggered the action (ex: it was triggered through code).

Methods (call these to make the device act):

Method Signature Description
Trigger Trigger<public>(Agent:agent):void Triggers this device with Agent being passed as the agent that triggered the action. Use an agent reference when this device is setup to require one (for instance, you want to trigger the device only with a particular agent.
Trigger Trigger<public>():void Triggers this device, causing it to activate its TriggeredEvent event.
Enable Enable<public>():void Enables this device.
Disable Disable<public>():void Disables this device.
SetMaxTriggerCount SetMaxTriggerCount<public>(MaxCount:int):void Sets the maximum amount of times this device can trigger. * 0 can be used to indicate no limit on trigger count. * MaxCount is clamped between [0,20].
GetMaxTriggerCount GetMaxTriggerCount<public>()<transacts>:int Gets the maximum amount of times this device can trigger. * 0 indicates no limit on trigger count.
GetTriggerCountRemaining GetTriggerCountRemaining<public>()<transacts>:int Returns the number of times that this device can still be triggered before hitting GetMaxTriggerCount. Returns 0 if GetMaxTriggerCount is unlimited.
SetResetDelay SetResetDelay<public>(Time:float):void Sets the time (in seconds) after triggering, before the device can be triggered again (if MaxTrigger count allows).
GetResetDelay GetResetDelay<public>()<transacts>:float Gets the time (in seconds) before the device can be triggered again (if MaxTrigger count allows).
SetTransmitDelay SetTransmitDelay<public>(Time:float):void Sets the time (in seconds) which must pass after triggering, before this device informs other external devices that it has been triggered.
GetTransmitDelay GetTransmitDelay<public>()<transacts>:float Gets the time (in seconds) which must pass after triggering, before this device informs other external devices that it has been triggered.

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):

Walkthrough

Here's the full cove scene. A vector3 marks where the treasure chest sits on the dock. Every tick we compare each player's X position against the chest's X. If the absolute difference is within our tolerance, we Trigger() the device — which might open a vault, play a jingle, or spawn loot via whatever you've wired to it. We also cap it with SetMaxTriggerCount so the chest only pops once, and give it a reset delay so it can re-arm.

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

cove_treasure_device := class(creative_device):

    # The trigger wired to your chest / loot / jingle in the editor.
    @editable
    ChestTrigger : trigger_device = trigger_device{}

    # Where the treasure sits on the dock (set in Details panel).
    @editable
    ChestSpot : vector3 = vector3{ X := 0.0, Y := 0.0, Z := 0.0 }

    # How close (in cm along X) counts as "at the chest".
    @editable
    Tolerance : float = 200.0

    OnBegin<override>()<suspends>:void =
        # Only let the chest pop a single time, then re-arm after 3s.
        ChestTrigger.SetMaxTriggerCount(1)
        ChestTrigger.SetResetDelay(3.0)
        ChestTrigger.Enable()

        # Watch the shore forever, checking each player every tick.
        loop:
            CheckPlayers()
            Sleep(0.0)

    CheckPlayers()<suspends>:void =
        for (Player : GetPlayspace().GetPlayers()):
            if (Char := Player.GetFortCharacter[]):
                Pos := Char.GetTransform().Translation
                # Signed gap could be negative (player west of chest)
                # or positive (east of chest). Abs() ignores the side.
                GapX := Abs(Pos.X - ChestSpot.X)
                if (GapX <= Tolerance):
                    # Fire the trigger, crediting this player.
                    ChestTrigger.Trigger(Player)```

Line by line:

- The `@editable` fields let you drop the real `trigger_device` onto the field in the Details panel, and set the chest's spot and tolerance without touching code.
- `SetMaxTriggerCount(1)` means the chest can only be looted once; `SetResetDelay(3.0)` lets it re-arm after three seconds so a second player can loot it.
- In `OnBegin` the `loop` with `Sleep(0.0)` runs our check once per frame without hogging the CPU.
- `GetFortCharacter[]` is a failable lookup — if the player isn't spawned yet, the `if` simply skips them.
- `Pos.X - ChestSpot.X` is the *signed* difference. Wrapping it in `Abs()` turns "5 metres west" and "5 metres east" into the same `500.0`.
- `ChestTrigger.Trigger(Player)` activates the device and passes the player as the triggering `agent`, so anything listening on `TriggeredEvent` knows who did it.

## Common patterns

**Pattern 1  React on TriggeredEvent and unwrap the agent.** Subscribe to the trigger's event to run loot logic, using `Abs()` to log how far off-centre the looter stood.

```verse
using { /Fortnite.com/Devices }
using { /Fortnite.com/Characters }
using { /Verse.org/Simulation }
using { /Verse.org/SpatialMath }

cove_loot_listener := class(creative_device):

    @editable
    ChestTrigger : trigger_device = trigger_device{}

    @editable
    ChestSpot : vector3 = vector3{ X := 0.0, Y := 0.0, Z := 0.0 }

    OnBegin<override>()<suspends>:void =
        ChestTrigger.TriggeredEvent.Subscribe(OnLooted)

    OnLooted(Agent : ?agent):void =
        if (A := Agent?):
            if (Player := player[A], Char := A.GetFortCharacter[]):
                Pos := Char.GetTransform().Translation
                # How far off-centre (any direction) the looter stood.
                OffCentre := Abs(Pos.Y - ChestSpot.Y)
                if (OffCentre < 100.0):
                    # Dead-centre bonus loot could go here.
                    ChestTrigger.Trigger()

Pattern 2 — Tune re-arm timing and read remaining triggers. Use the getter/setter pair around trigger counts so a limited-supply chest slows down as it empties.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }

cove_supply_chest := class(creative_device):

    @editable
    ChestTrigger : trigger_device = trigger_device{}

    OnBegin<override>()<suspends>:void =
        ChestTrigger.SetMaxTriggerCount(5)
        ChestTrigger.Enable()

        loop:
            Remaining := ChestTrigger.GetTriggerCountRemaining()
            # Fewer supplies left => longer cooldown, using Abs()
            # as a tidy magnitude so the delay never goes negative.
            NewDelay := Abs(5.0 - Int[Remaining] * 1.0)
            ChestTrigger.SetResetDelay(NewDelay)
            Sleep(2.0)

Pattern 3 — Disable the chest when it's empty. Combine GetMaxTriggerCount, GetTriggerCountRemaining, and Disable() so an exhausted chest goes dark.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }

cove_chest_watcher := class(creative_device):

    @editable
    ChestTrigger : trigger_device = trigger_device{}

    OnBegin<override>()<suspends>:void =
        ChestTrigger.SetMaxTriggerCount(3)
        ChestTrigger.Enable()

        loop:
            Max := ChestTrigger.GetMaxTriggerCount()
            Left := ChestTrigger.GetTriggerCountRemaining()
            # Distance from empty. When 0, the chest is spent.
            Used := Abs(Max - Left)
            if (Left <= 0 && Max > 0):
                ChestTrigger.Disable()
            Sleep(1.0)

Gotchas

  • No int↔float auto-convert. vector3 components are float, so subtract floats and compare against a float tolerance. In Pattern 2, GetTriggerCountRemaining() returns an int, so we bridge it with Int[Remaining] * 1.0 before mixing with a float — you can't add an int to a float directly.
  • Abs() doesn't change the type. Abs(anInt) returns an int; Abs(aFloat) returns a float. Keep your types consistent on both sides of a comparison.
  • Trigger() vs Trigger(Agent). The no-arg overload fires the device but TriggeredEvent may report no agent. Pass the player when you need TriggeredEvent handlers to know who triggered it — then unwrap with if (A := Agent?):.
  • The event payload is ?agent, not agent. Your handler signature must be (Agent : ?agent) and you must unwrap it before use. To get a player, narrow with player[A].
  • GetFortCharacter[] can fail. A player who just joined or respawned may not have a character yet — always guard the lookup with if so the loop skips them instead of crashing.
  • SetMaxTriggerCount is clamped to [0,20]. Ask for 100 and you'll silently get 20; 0 means unlimited.
  • SetResetDelay must be non-negative. Feeding it a raw signed subtraction can go negative — wrapping the value in Abs(), as in Pattern 2, keeps the delay sane.

Guides & scripts that use vector3

Step-by-step tutorials that put this object to work.

Build your own lesson with vector3

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 →