By completing this step in the Tagged Lights Puzzle tutorial, you'll learn how to use Gameplay Tags to find actors marked with a specific tag while the game is running. Gameplay Tags let you work with multiple devices without having to set up their references in the editor. This can open up interesting gameplay opportunities where, for example, your code dynamically changes which devices are active as the player progresses through the game.
Follow these steps to create a new Gameplay Tag and assign it to all the lights in the level for the puzzle:
Open Verse Explorer and double-click tagged_lights_puzzle.verse to open the script in Visual Studio Code.
At the top of the code file:
Add
using { /Verse.org/Simulation/Tags }to reference thetagclass and and use theGetCreativeObjectsWithTag()function.Add
using { /Verse.org/Simulation }to be able to make editable properties.Verseusing { /Fortnite.com/Devices } using { /Verse.org/Native } using { /UnrealEngine.com/Temporary/Diagnostics } using { /Verse.org/Simulation/Tags } using { /Verse.org/Simulation } log_tagged_lights_puzzle := class(log_channel){}
Above the
log_tagged_lights_puzzleclass, add a new subclass namedpuzzle_lightthat inherits from thetagclass. The inherited class name becomes a custom Gameplay Tag for you to use on any creative device.Verse# Derive from the `tag` class in the Verse.org/Simulation/Tags module to create a new Gameplay Tag. puzzle_light := class(tag){} log_tagged_lights_puzzle := class(log_channel){}In the UEFN toolbar, click Build Verse Scripts to compile your code and your new
puzzle_lightGameplay Tag to your project.In the UEFN Outliner, select a Customizable Light Device to open its Details panel.
In the Details panel:
Click Add New Component and choose Verse Tag Markup.
Select the VerseTagMarkup Component to view its settings in the Details panel.
Under Gameplay Tags edit the Tags property and add the
puzzle_lighttag.Multiple tags can be added to the same device, so each device can belong to multiple groups at the same time. For example, a device with
tag1andtag2will be found when calling eitherGetCreativeObjectsWithTag(tag1{})orGetCreativeObjectsWithTag(tag2{}).
In the
tagged_lights_puzzleclass definition, add two variable array fields:An editable
logicvariable array namedLightsStateto represent the current state of all lights (whether they’re turned off or on). It's also used to set the initial state of the lights, so its number of elements should match the number of lights tagged with thepuzzle_lighttag. In this example, all lights are turned off by default so the starting value for all lights isfalse.Verse@editable var LightsState : []logic = array{false, false, false, false}An editable
customizable_light_devicevariable array namedLightsto store all the Customizable Light devices tagged with thepuzzle_lightGameplay Tag.Verse@editable var Lights : []customizable_light_device = array{}
When the game begins, the device should set up the lights to match the initial configuration specified in the
LightsStatearray, and save the references in theLightsarray so they can be updated when the game state changes. This work is going to be done in a method namedSetupPuzzleLights() : voidand called in theOnBegin()method so that the lights are set up when the game starts.VerseSetupPuzzleLights() : void = Logger.Print("Setting up in-game lights") OnBegin<override>()<suspends> : void = SetupPuzzleLights()In
SetupPuzzleLights(), find all devices with thepuzzle_lighttag by callingGetCreativeObjectsWithTag(puzzle_light{})and save them in an array namedTaggedActors. SinceTaggedActorsis a constant array whose scope is local to the methodSetupPuzzleLights(), you don’t need to explicitly specify a type for the array because it can be inferred in this context.VerseSetupPuzzleLights() : void = Logger.Print("Setting up in-game lights") TaggedActors := GetCreativeObjectsWithTag(puzzle_light{})Different calls of the function
GetCreativeObjectsWithTag()may place the devices in different orders in the array result, because there's no guaranteed order when retrieving actors with Gameplay Tags.Now that you’ve collected all the devices that have the
puzzle_lighttag, make sure each light matches the initial state specified by theLightsStatearray. You can use aforloop to iterate through all the tagged devices.Versefor: ActorIndex -> TaggedActor : TaggedActors do: TaggedActorThe function
GetCreativeObjectsWithTag()returns an array of typecreative_object_interface. In this example, you’ll want to interact with eachTaggedActoras acustomizable_light_deviceso you can turn the light on or off.You can convert a class to one of its subclasses (called type casting) using the syntax
NewDeviceReference := device_type_to_cast_to[DeviceReference], wheredevice_type_to_cast_tois the device type you want, which in this example iscustomizable_light_device. This is a failable expression because the type conversion will fail if the device can’t be converted to that type (for example if it’s a different type of device).VerseLightDevice := customizable_light_device[TaggedActor]The function
GetCreativeObjectsWithTag()has the return type[]creative_object_interfacebecause the function can return different types of actors, so its return type is the interface all actors must implement to be returned byGetCreativeObjectsWithTag(). See gameplay tags to learn more.With
forexpressions, you can use failable expressions as a filter and create new variables that you can then use in theforcode block. In this case, add the type conversion tocustomizable_light_devicefrom the previous step to the iteration expression.Versefor: ActorIndex -> TaggedActor : TaggedActors LightDevice := customizable_light_device[TaggedActor] do: LightDeviceThe last expression in a code block is the code block’s result. The
forexpression returns the result of the code block from each iteration in an array, so the result of thisforexpression is an array ofcustomize_light_devicereferences that were tagged withpuzzle_light. This means you can update theLightsarray with the result of theforexpression directly.Verseset Lights = for: ActorIndex -> TaggedActor : TaggedActors LightDevice := customizable_light_device[TaggedActor] do: LightDeviceThis
forloop should also callTurnOn()/TurnOff()on the lights to match their initialLightsStatesetup in the editor. Theforexpression can return the index used to get the current tagged device (ActorIndexin the example), which you can use to index into theLightsStatearray to see whether the light should be on or off.Verseset Lights = for: ActorIndex -> TaggedActor : TaggedActors LightDevice := customizable_light_device[TaggedActor] ShouldLightBeOn:= LightsState[ActorIndex] do: LightDeviceNext, call
TurnOn()/TurnOff()depending on whetherShouldLightBeOnistrue/false. You can use anifexpression to execute different expressions based on a condition (specifically a failable expression). In this case, the failable expression can use the query operator?withIsLightOn, which will succeed ifShouldLightBeOnistrue(so callTurnOn()), and fail ifShouldLightBeOnisfalse(so callTurnOff()).Verseset Lights = for: ActorIndex -> TaggedActor : TaggedActors LightDevice := customizable_light_device[TaggedActor] ShouldLightBeOn := LightsState[ActorIndex] do: if (ShouldLightBeOn?) then LightDevice.TurnOn() else LightDevice.TurnOff() LightDeviceIt’s a good idea to also print the index of the light and its starting value so you can verify your code is working as expected and compare to what you see in the level.
When you use
{}in the middle of a string, the expression between the{}is evaluated first and its value is added to the string. So you can use anifexpression in the middle of a string to conditionally add values.Verseset Lights = for: ActorIndex -> TaggedActor : TaggedActors LightDevice := customizable_light_device[ActorIndex] ShouldLightBeOn := LightsState[ActorIndex] do: Logger.Print("Adding Light at index {ActorIndex} with State:{if (ShouldLightBeOn?) then "On" else "Off"}") if (ShouldLightBeOn?) then LightDevice.TurnOn() else LightDevice.TurnOff() LightDevice
Your
SetupPuzzleLights()method should now look like this:VerseSetupPuzzleLights() : void = TaggedActors := GetCreativeObjectsWithTag(puzzle_light{}) <# For each device with the puzzle_light tag, check if it's a customizable_light_device by trying to cast it to that type. If it is, get its initial LightState to TurnOn() or TurnOff() the LightDevice. Save all the tagged customizable_light_device in the Lights array. #> set Lights = for: ActorIndex -> TaggedActor : TaggedActors LightDevice := customizable_light_device[TaggedActor]Save the script in Visual Studio Code.
In the UEFN toolbar, click Build Verse Scripts to compile your code.
Click Play in the UEFN toolbar to playtest the level.
When you playtest your level, you should see each light that’s added to the Lights array, along with its initial state, printed to the output log.
Next Step
In the next step of this tutorial, you’ll learn how to toggle a specific set of lights when the player presses the buttons.