Reference Devices compiles

grind_powerup_device: Let Players Slide Anywhere

The Grind Powerup Device gives players the slick ability to slide on any surface, complete with sparks and sound. From Verse you can spawn it, grant it on demand, tune its duration, and react when a player grabs it — perfect for parkour courses, escape rooms, and movement-puzzle islands.

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

Overview

The grind_powerup_device lets agents slide along any surface — walls, floors, ramps — with built-in visual and audio flair. Mechanically it is a powerup_device: a pickup that applies a timed effect to whoever grabs it.

Reach for it whenever movement is the gameplay: a lava-floor escape where sliding is the only way to cross, a speed-parkour course, or a reward you grant from a button when a player clears a checkpoint. Because it inherits from powerup_device, you get a clean toolkit: Spawn/Despawn to control whether the pickup exists in the world, Pickup(Agent) to hand the effect directly to a player (no walking over it required), SetDuration/GetDuration to tune how long the slide lasts, and GetRemainingTime/HasEffect to query a player's current state. The ItemPickedUpEvent fires whenever someone collects it, so you can react with score, sounds, or chained logic.

The big advantage over the device-settings-only approach: in Verse you can grant the powerup conditionally — only to the player who finished a lap, only if they don't already have it, with a duration that scales to difficulty.

API Reference

grind_powerup_device

Used to let agents slide on any surface with accompanying visual and audio effects.

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

grind_powerup_device<public> := class<concrete><final>(powerup_device):

Events (subscribe a handler to react):

Event Signature Description
ItemPickedUpEvent ItemPickedUpEvent<public>:listenable(agent) Signaled when the powerup is picked up by an agent. Sends the agent that picked up the powerup.

Methods (call these to make the device act):

Method Signature Description
Spawn Spawn<public>():void Spawns the powerup into the experience so users can interact with it.
Despawn Despawn<public>():void Despawns this powerup from the experience.
SetDuration SetDuration<public>(Time:float):void Updates the Duration for this powerup, clamped to the Min and Max defined in the device. Will not apply to any currently applied effects.
GetDuration GetDuration<public>()<transacts>:float Returns the Duration that this powerup will be active for on any player it is applied to.
GetRemainingTime GetRemainingTime<public>(Agent:agent)<transacts>:float If the Agent has the effect applied to them, this will return the remaining time the effect has. Returns -1.0 if the effect has an infinite duration. Returns 0.0 if the Agent does not have the effect applied.
HasEffect HasEffect<public>(Agent:agent)<transacts><decides>:void Returns the Agent has the powerup's effect (or another of the same type) applied to them.
IsSpawned IsSpawned<public>()<transacts><decides>:void Succeeds if the powerup is currently spawned.
Pickup Pickup<public>(Agent:agent):void Grants this powerup to Agent.
Pickup Pickup<public>():void Grants this powerup without an agent reference. Requires Apply To set to All Players.

powerup_device

Base class for various powerup devices offering common events like ItemPickedUpEvent.

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

powerup_device<public> := class<abstract><epic_internal>(creative_device_base):

Events (subscribe a handler to react):

Event Signature Description
ItemPickedUpEvent ItemPickedUpEvent<public>:listenable(agent) Signaled when the powerup is picked up by an agent. Sends the agent that picked up the powerup.

Methods (call these to make the device act):

Method Signature Description
Spawn Spawn<public>():void Spawns the powerup into the experience so users can interact with it.
Despawn Despawn<public>():void Despawns this powerup from the experience.
SetDuration SetDuration<public>(Time:float):void Updates the Duration for this powerup, clamped to the Min and Max defined in the device. Will not apply to any currently applied effects.
GetDuration GetDuration<public>()<transacts>:float Returns the Duration that this powerup will be active for on any player it is applied to.
GetRemainingTime GetRemainingTime<public>(Agent:agent)<transacts>:float If the Agent has the effect applied to them, this will return the remaining time the effect has. Returns -1.0 if the effect has an infinite duration. Returns 0.0 if the Agent does not have the effect applied.
HasEffect HasEffect<public>(Agent:agent)<transacts><decides>:void Returns the Agent has the powerup's effect (or another of the same type) applied to them.
IsSpawned IsSpawned<public>()<transacts><decides>:void Succeeds if the powerup is currently spawned.
Pickup Pickup<public>(Agent:agent):void Grants this powerup to Agent.
Pickup Pickup<public>():void Grants this powerup without an agent reference. Requires Apply To set to All Players.

Walkthrough

Let's build a lava-floor crossing. A trigger sits at the entrance. When a player steps on it, we grant them the grind powerup directly (so they can slide across), but only if they don't already have it — and we lengthen the slide for the challenge. We also listen for the natural pickup event to play feedback, and we make sure the world pickup is actually spawned at game start.

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

lava_floor_grind := class(creative_device):

    # The Grind Powerup placed in the level.
    @editable
    GrindPowerup : grind_powerup_device = grind_powerup_device{}

    # The trigger at the entrance to the lava floor.
    @editable
    EntranceTrigger : trigger_device = trigger_device{}

    OnBegin<override>()<suspends>:void =
        # Make sure the pickup actually exists in the world.
        if (not GrindPowerup.IsSpawned[]):
            GrindPowerup.Spawn()

        # A 12-second slide is plenty to cross the lava.
        GrindPowerup.SetDuration(12.0)
        Print("Grind lasts {GrindPowerup.GetDuration()} seconds")

        # React when a player steps on the entrance trigger.
        EntranceTrigger.TriggeredEvent.Subscribe(OnEntrance)

        # React when anyone naturally picks the powerup up.
        GrindPowerup.ItemPickedUpEvent.Subscribe(OnPickedUp)

    # trigger_device.TriggeredEvent hands us a (Agent : ?agent).
    OnEntrance(MaybeAgent : ?agent):void =
        if (Player := MaybeAgent?):
            # Don't double-grant if they already have the effect.
            if (not GrindPowerup.HasEffect[Player]):
                GrindPowerup.Pickup(Player)
                Print("Granted grind to a crosser!")
            else:
                Remaining := GrindPowerup.GetRemainingTime(Player)
                Print("Player still has {Remaining} seconds of grind")

    # ItemPickedUpEvent hands us a plain agent.
    OnPickedUp(Player : agent):void =
        Print("A player picked up the grind powerup!")

Line by line:

  • GrindPowerup : grind_powerup_device = grind_powerup_device{} and EntranceTrigger : trigger_device are @editable fields — that's what lets Verse talk to devices you placed in the level. You bind them in the Details panel.
  • In OnBegin, if (not GrindPowerup.IsSpawned[]) checks whether the pickup is already in the world; IsSpawned is a <decides> function, so it goes in [] and can succeed or fail. If it isn't spawned, we call Spawn().
  • SetDuration(12.0) sets how long the slide effect lasts. Note the float literal 12.0 — Verse won't accept 12. GetDuration() reads it back for our log.
  • We Subscribe two handlers in OnBegin: one to the trigger, one to the powerup's ItemPickedUpEvent.
  • OnEntrance receives ?agent from the trigger, so we unwrap with if (Player := MaybeAgent?). We then test HasEffect[Player] (also <decides>, so []) — if they don't have it, Pickup(Player) grants the powerup straight to them. Otherwise we call GetRemainingTime(Player) to see how long they have left.
  • OnPickedUp receives a plain agent (the powerup event is listenable(agent), not ?agent), so no unwrap is needed.

Common patterns

Despawn the world pickup once someone grabs it (single-use reward):

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

single_use_grind := class(creative_device):

    @editable
    GrindPowerup : grind_powerup_device = grind_powerup_device{}

    OnBegin<override>()<suspends>:void =
        GrindPowerup.Spawn()
        GrindPowerup.ItemPickedUpEvent.Subscribe(OnGrabbed)

    OnGrabbed(Player : agent):void =
        # Take the pickup out of the world so nobody else can use it.
        GrindPowerup.Despawn()
        Print("Grind claimed — pickup removed.")

Grant to all players at once (Apply To = All Players):

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

race_start_grind := class(creative_device):

    @editable
    GrindPowerup : grind_powerup_device = grind_powerup_device{}

    @editable
    StartButton : button_device = button_device{}

    OnBegin<override>()<suspends>:void =
        GrindPowerup.SetDuration(20.0)
        StartButton.InteractedWithEvent.Subscribe(OnStart)

    OnStart(Player : agent):void =
        # No agent argument: grants to everyone. Requires Apply To = All Players.
        GrindPowerup.Pickup()
        Print("Everyone gets the grind boost — race on!")

Poll remaining time and warn a player when it's almost gone:

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

grind_timer_watch := class(creative_device):

    @editable
    GrindPowerup : grind_powerup_device = grind_powerup_device{}

    OnBegin<override>()<suspends>:void =
        GrindPowerup.Spawn()
        GrindPowerup.ItemPickedUpEvent.Subscribe(OnGrabbed)

    OnGrabbed(Player : agent):void =
        spawn { WatchEffect(Player) }

    WatchEffect(Player : agent)<suspends>:void =
        loop:
            Sleep(1.0)
            if (not GrindPowerup.HasEffect[Player]):
                Print("Grind expired!")
                break
            Remaining := GrindPowerup.GetRemainingTime(Player)
            if (Remaining <= 3.0):
                Print("Grind ending in {Remaining}s!")

Gotchas

  • SetDuration doesn't touch active effects. Per the API, it only changes the duration applied to future pickups. If a player is already sliding, changing the duration won't extend them — grant the powerup again (after HasEffect checks out) if you need to refresh.
  • Duration is clamped. SetDuration clamps to the Min/Max set on the device in the editor. Calling SetDuration(999.0) won't give you 999 seconds unless the device's Max allows it. Read the real value back with GetDuration().
  • Pickup() with no agent needs the right setting. The argument-less overload only works when the device's Apply To option is set to All Players. Otherwise nothing happens — use Pickup(Agent) for a specific player.
  • <decides> functions go in square brackets. HasEffect[Agent] and IsSpawned[] succeed-or-fail; call them inside an if (...), and use not to invert. Calling them with () won't compile.
  • Two different event payloads. ItemPickedUpEvent is listenable(agent) — your handler gets a plain agent, no ? unwrap. The trigger_device.TriggeredEvent is listenable(?agent), so that one needs if (A := MaybeAgent?). Don't mix them up.
  • Float vs int. SetDuration and the magnitude helpers take float. Write 12.0, never 12 — Verse does not auto-convert.
  • GetRemainingTime return codes. It returns -1.0 for infinite-duration effects and 0.0 when the agent has no effect — so a value of 0.0 is not 'about to expire', it's 'doesn't have it'. Guard with HasEffect first.

Build your own lesson with grind_powerup_device

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 →