Objective markers are used in many games to guide a player to the next goal or point of interest. In this tutorial, you will learn how to make a reusable objective marker with the Map Indicator device and Verse.
Verse Language Features Used
-
struct
: You can group variables of different types in a struct. -
Extension method: A special type of function that acts like a member of an existing class or type, but does not require the creation of a new type or subclass. In this guide, you will be creating an extension method for struct.
-
named argument: An argument that is passed to a function call with its parameter name specified.
Verse APIs Used
-
Prop API: The Prop API provides methods for prop movement.
-
Editable Properties: Several properties are used to both reference devices and update variable values for quick testing.
Instructions
Follow these steps to learn how to set up a single objective marker device that can move to multiple objectives or points of interest. The complete scripts are included at the end of this guide for reference.
Setting Up the Level
This example uses the following props and devices.
-
1 x Building Prop: A prop that will be used to move the Map Indicator device.
-
1 x Map Indicator device: A device that will display custom markers on the minimap and overview map.
-
1 x Player Spawn Pad device: Add this close to the prop so the player will spawn near it.
Using the Prop API
The first step to getting a device moving with Verse is moving a prop with the Prop API. Follow these steps to move a prop around your level.
-
Create a new Verse device named objective_coordinator_device.
-
Under the default
using
expressions at the top of the Verse file, add ausing
expression for theSpatialMath
module. This module contains code you will reference to move props.using { /UnrealEngine.com/Temporary/SpatialMath }
-
Add two editable properties:
-
A
creative_prop
constant namedRootProp
to store a reference to the moving prop. -
A
transform
constant namedDestination
to store the location the prop will be moving to.objective_coordinator_device<public> := class<concrete>(creative_device): @editable RootProp<public> : creative_prop = creative_prop{} @editable Destination<public> : transform = transform{}
-
-
If you run this code and drag your objective_coordinator_device into your level, you will see the two properties in the Details panel.
-
The
TeleportTo[]
method is what actually moves the prop. Call it within anif
expression and use square brackets instead of parentheses becauseTeleportTo[]
is a failable expression. Theif
creates a failure context.if(RootProp.TeleportTo[Destination.Translation, Destination.Rotation]): Print("Prop move successful") else: Print("Prop move failed")
-
The arguments for
TeleportTo[]
are Translation and Rotation. Both of these come from your Destination property. -
Back in the editor, drag in a prop from Fortnite > Galleries > Props in the Content Browser. The one used in this guide is called Coastal Buoy 02B, but anything from the Props folder should work.
-
Select your objective coordinator device in the Outliner. In the Details panel, set RootProp to your prop. In this example, RootProp is set to Coastal Buoy 02B.
-
In the Details panel, expand Destination. Because Destination is of type
transform
, it is made up of a Scale, Rotation, and Translation. To move the prop, you only need to change Translation, so expand that. Set the field that ends with X to 5000.0.When testing code, it is a good idea to make big changes to values so that the effects are obvious. Small changes can make it hard to tell if your code is doing what you expect.
using { /Verse.org/Simulation } using { /Fortnite.com/Devices } using { /UnrealEngine.com/Temporary/SpatialMath } objective_coordinator_device<public> := class<concrete>(creative_device): @editable RootProp<public> : creative_prop = creative_prop{} # Where the marker will be moved to @editable Destination<public> : transform = transform{} OnBegin<override>()<suspends> : void = if(RootProp.TeleportTo[Destination.Translation, Destination.Rotation]): Print("Prop move successful") else: Print("Prop move failed")
-
Click Verse, then Build Verse Code, then click Launch Session. Finally, click Start Game. You should see your prop move.
Parent and Structs
You now have a prop moving around in your level, but the real goal is moving a Map Indicator device so players can use it as a waypoint. Follow these steps to add a Building Prop and Map Indicator device to your level and attach it to the Building Prop.
-
Right-click inside the Content Browser to open the context menu.
-
Select Blueprint Class from the context menu.
-
In the Pick Parent Class window, click Building Prop.
-
A new Blueprint Class will appear in your Content Browser. Rename it to BuildingProp.
-
Drag the Building Prop into your level. This prop has no mesh, so you will only see its transform gizmo.
-
In the Outliner, drag the Map Indicator Device onto the Building Prop. This makes the Building Prop the parent of the Map Indicator Device. Now when the Building Prop moves, the Map Indicator Device moves with it.
You already know how to create a device using Verse, but you can also create Verse files that do not have their own devices.
-
Create a new Verse file and name it objective_marker. This file will not create a device. Instead it will contain the definition of a
struct
to be exposed to the Verse device you created earlier. -
Start by declaring a
struct
named objective_marker. It will have two members:RootProp
andMapIndicator
. Both of these should have the@editable
specifier.objective_marker<public> := struct<concrete>: @editable RootProp<public> : creative_prop = creative_prop{} @editable MapIndicator<public> : map_indicator_device = map_indicator_device{}
Extension Methods and Named Arguments
Declare a single method, MoveMarker
, that will move the RootProp
member and its attached Map Indicator device. This method introduces two language features: extension methods and named arguments.
(Marker : objective_marker).MoveMarker<public>(Transform : transform, ?OverTime : float)<suspends> : void =
-
Extension methods: You are adding the
MoveMarker()
method to theobjective_marker
struct. An extension method is declared using parentheses surrounding an identifier and type separated by a colon. In this case:(Marker : objective_marker)
. -
Named arguments: The second argument
?OverTime
uses the?
to indicate that it must be named in theMoveMarker
function call. This helps any developer reading or writing a call toMoveMarker
understand what thefloat
argument is doing.
MoveMarker()
will call one of two methods from the Prop API: TeleportTo[]
, which you used earlier, or MoveTo()
. Create an if..else
block to test if the parameter OverTime
is greater than 0.0
. If it is, call MoveTo()
. This will make your objective move to its next location over period of time you specify, instead of instantly teleporting.
(Marker : objective_marker).MoveMarker<public>(Transform : transform, ?OverTime : float)<suspends> : void =
if (OverTime > 0.0):
Marker.RootProp.MoveTo(Transform.Translation, Transform.Rotation, OverTime)
else:
if:
Marker.RootProp.TeleportTo[Transform.Translation, Transform.Rotation]
If you compile the code now, it should succeed, but you should not see a new device in the CreativeDevices folder in the Content Browser. This is because objective_marker is a struct
, not a class that inherits from creative_device
.
Updating Objective Coordinator Device
Now that you have a new type to reference, you need to update the objective_coordinator_device to reference it.
-
Delete the
RootProp
property and replace it with a property namedPickupMarker
of typeobjective_marker
. This is the type you created. -
MoveMarker()
requires an argument of typefloat
, so create that as an editable property namedMoveTime
. -
Delete the call to
TeleportTo[]
. Instead call theMoveMarker()
method you created forobjective_marker
. It requires the named argument?OverTime
.objective_coordinator_device<public> := class<concrete>(creative_device): @editable PickupMarker<public> : objective_marker = objective_marker{} # Where the marker will be moved to @editable Destination<public> : transform = transform{} # How much time the marker should take to reach its new location @editable MoveTime<public> : float = 0.0 OnBegin<override>()<suspends> : void = PickupMarker.MoveMarker(Destination, ?OverTime := MoveTime)
Compile this code and check the Details of the objective coordinator device. You should see the PickupMarker and MoveTime properties, and PickupMarker should contain RootProp and MapIndicator.
-
Set the RootProp field to the BuildingProp, and the MapIndicator field to the Map Indicator Device
-
Compile your code and click Launch Session. You should see a marker on your minimap that moves shortly after your game starts. Try it with
MoveTime
set to different values, including0.0
. Think about which movement would be best suited for different scenarios.
GetPlayers() and ActivateObjectivePulse()
There is a way to give your players a little extra help getting to their next objective. It’s called an objective pulse and when active it shows a dotted line that moves from the player toward the Map Indicator Device. Follow the instructions below to add an objective pulse to your objective coordinator device.

The method you need to activate the objective pulse is called ActivateObjectivePulse()
and it requires one argument of type agent
. Start by creating the method to get the instance of agent
representing your player character.
-
Declare a function called
FindPlayer()
set to<private>
, with a return value ofvoid
. -
Get an array of all the players in your level with
Self.GetPlayspace().GetPlayers()
. Store the array in a variable calledAllPlayers
.FindPlayer<private>() : void = AllPlayers := Self.GetPlayspace().GetPlayers()
-
To get the reference to the one and only player in your level, assign the first array element to its own variable. Accessing an array is a failable expression, so place it in an
if
expression.if (FirstPlayer := AllPlayers[0]):
-
Because assigning your
player
to a variable could fail, you want to use anoption
type variable when referencing the player in your code. Declare an optional player variable?player
. It should go with your other member variables.objective_coordinator_device<public> := class<concrete>(creative_device): var PlayerOpt<private> : ?player = false @editable PickupMarker<public> : objective_marker = objective_marker{} # Where the marker will be moved to @editable Destination<public> : transform = transform{} # How much time the marker should take to reach its new location @editable MoveTime<public> : float = 0.0
-
Set your new variable and create an
else
block with aPrint()
expression that will let you know if a player was not found. YourFindPlayer()
function is now complete.FindPlayer<private>() : void = # Since this is a single player experience, the first player [0] # should be the only one available. AllPlayers := Self.GetPlayspace().GetPlayers() if (FirstPlayer := AllPlayers[0]): set PlayerOpt = option{FirstPlayer} Print("Player found") else: # Log an error if we can't find a player. Print("Can't find valid player")
Back in the OnBegin()
function, you need to make two more changes:
-
Call your
FindPlayer()
function.OnBegin<override>()<suspends> : void = FindPlayer()
-
After your call to
MoveMarker()
, use anotherif
expression to set your optional player variable to a new variable, and pass that as an argument toPickupMarker.MapIndicator.ActivateObjectivePulse()
if (FoundPlayer := PlayerOpt?): PickupMarker.MapIndicator.ActivateObjectivePulse(FoundPlayer)
If you run your code now, you should see the objective pulse pointing from your character to the location of the objective marker in the level!
Complete Scripts
Objective_marker.verse
using { /Verse.org/Simulation }
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /Fortnite.com/Devices/CreativeAnimation }
objective_marker<public> := struct<concrete>:
# The prop that will be moved
@editable
RootProp<public> : creative_prop = creative_prop{}
# The child of the prop that will move with it
@editable
MapIndicator<public> : map_indicator_device = map_indicator_device{}
# An extension method for objective_marker
# The ? in front of OverTime specifies it as a named argument
(Marker : objective_marker).MoveMarker<public>(Transform : transform, ?OverTime : float)<suspends> : void =
if (OverTime > 0.0):
Marker.RootProp.MoveTo(Transform.Translation, Transform.Rotation, OverTime)
else:
if:
Marker.RootProp.TeleportTo[Transform.Translation, Transform.Rotation]
Objective_coordinator_device.verse
using { /Verse.org/Simulation }
using { /Fortnite.com/Devices }
using { /Fortnite.com/Playspaces }
using { /UnrealEngine.com/Temporary/SpatialMath }
objective_coordinator_device<public> := class<concrete>(creative_device):
var PlayerOpt<private> : ?player = false
@editable
PickupMarker<public> : objective_marker = objective_marker{}
# Where the marker will be moved to
@editable
Destination<public> : transform = transform{}
# How much time the marker should take to reach its new location
@editable
MoveTime<public> : float = 0.0
OnBegin<override>()<suspends> : void =
FindPlayer()
PickupMarker.MoveMarker(Destination, ?OverTime := MoveTime)
# if Player is not set to false, active the objective pulse for the found player
if (FoundPlayer := PlayerOpt?):
PickupMarker.MapIndicator.ActivateObjectivePulse(FoundPlayer)
FindPlayer<private>() : void =
# Since this is a single player experience, the first player [0]
# should be the only one available.
AllPlayers := Self.GetPlayspace().GetPlayers()
if (FirstPlayer := AllPlayers[0]):
set PlayerOpt = option{FirstPlayer}
Print("Player found")
else:
# Log an error if we can't find a player.
Print("Can't find valid player")
On Your Own
Remember that the movement code you wrote here works for any prop. If you can make a movable prop the parent of a device, that device will move with it. Try moving other props and devices, and see if you can think of other games that could make use of them.
Next Steps
If you are using this guide to build the Pickup / Delivery game, your next step is to learn how to create the Countdown Timer feature.