This page guides you through creating a new Scene Graph component named trigger_component. This component triggers actions on entities that have a Scene Graph component that implements the triggerable interface. The trigger_component provides agents a way to interact with Scene Graph through a volume_device. When an agent enters or exits the volume_device, the corresponding AgentEnteredEvent and AgentExitedEvent is signaled and corresponding callbacks are called to perform actions on Scene Graph components and entities.
In the following image, the white pedestal on the ground is a Scene Graph entity with the trigger_component and an invisible volume_device around the pedestal.
When the player steps on the pedestal, the trigger_component triggers the two lights farthest to the right.
When the player steps off the pedestal and leaves the volume_device, the lights remain in the on state.
For more information about the Verse language features used on this page, see:
Define the Trigger Component
Navigate to the Verse Explorer, right-click the project name, and choose Add new Verse file to project.
Choose the template Scene Graph Component and change the Component Name to trigger_component.
Define the
trigger_componentclass and add fields that reference the associatedvolume_device, whether or not this component only triggers children or all triggerable descendants, and an array referencing all agents interacting with this component.Versetrigger_component := class<final_super>(component): # volume_device associated with this trigger_component @editable InteractionVolume<private>:volume_device = volume_device{} # whether or not this triggers on components on child entities @editable OnlyTriggerChildren:logic = falseAdd functions named
OnAgentEntersVolumeandOnAgentExitsVolumethat define what happens when an agent enters and exits this component's associatedvolume_device. When an agent enters the volume, record the interacting agent and look for descendant entities that have a component that implements thetriggerableinterface then trigger the component on those entities. When an agent enters the volume, remove the interacting agent from the list of agents interacting with this volume.Verse# Event that is triggered when an Agent enters the InteractionVolume OnAgentEntersVolume(Agent:agent):void= for: ChosenEntity : Entity.FindDescendantEntities(entity) ChosenEntityComponent : ChosenEntity.GetComponents() CastToInterface := triggerable[ChosenEntityComponent] do: if (OnlyTriggerChildren?): if (Entity = ChosenEntity.GetParent[]): CastToInterface.Trigger()Connect the
OnAgentEntersVolumeandOnAgentExitsVolumefunctions to thevolume_deviceby registering them as the callback functions for theAgentEntersEventandAgentExitsEventin this component'sOnSimulatefunction.Verse# Runs when the component should start simulating in a running game. OnBeginSimulation<override>():void = # Run OnBeginSimulation from the parent class before # running this component's OnBeginSimulation logic (super:)OnBeginSimulation() # Runs when the component should start simulating in a running game. # Can be suspended throughout the lifetime of the component. Suspensions # will be automatically cancelled when the component is disposed or the # game ends.
Define the Triggerable Components
Next, define components that implement the previously created triggerable interface. These are components that are added to Scene Graph entities that are descendants of an entity with a puzzle_component and are triggered by an ancestor entity with a trigger_component. This tutorial defines three different triggerable components:
triggerable_mesh_component: Toggles the visibility of the entity'smesh_component.triggerable_light_component: Toggles the entity'ssphere_light_componenton and off and exchanges the entity`smesh_componentfor one with or without emissive color, depending on whether the light is on or off.triggerable_movement_component: Triggers a transformation of the entity`smesh_componentusing thekeyframed_movement_component.
These three different components share a large amount of functionality, and many of the functions are defined the same way. To begin, create a new, empty Verse file named TriggerableComponents.verse.
Include the following modules:
/Verse.org/SceneGraph since this file uses Scene Graph components and Scene Events.
/Verse.org/SceneGraph/KeyframedMovement since this file uses keyframed_movement_delta.
/Verse.org/Simulation since this file uses the @editable attribute.
/Verse.org/SpatialMath since this file uses Verse transforms.
Verseusing { /Verse.org/SceneGraph } using { /Verse.org/SceneGraph/KeyframedMovement } using { /Verse.org/Simulation } using { /Verse.org/SpatialMath }Define the components
triggerable_mesh_component,triggerable_light_component, andtriggerable_movement_component. All of these components inherit from the basecomponentclass and implement the triggerable interface.All three components implement the same three events:
OnReceive: Called when this component receives a Scene Event.OnBeginSimulation: Called when the component should start simulating.OnSimulate: Called when the component starts simulating.
Add the following implementations to all three components.
Verse# Runs when this component receives a scene event OnReceive<override>(SceneEvent:scene_event):logic= if (PuzzleSolvedEvent := puzzle_solved_event[SceneEvent], not PuzzlePiece?): Trigger() true false # Runs when the component should start simulating in a running game. OnBeginSimulation<override>():void = # Run OnBeginSimulation from the parent class beforeAll three components also implement the
triggerableinterfacePostTriggeroverride in the same way by sending up atriggered_eventif thetriggerablecomponent is a puzzle piece. You cannot accomplish this inside thetriggerableinterface itself because Verse needs to know whichentityis triggered, and theEntityfield is part of thecomponentclass and not available to thetriggerableinterface without the additional dependence on thecomponentclass.VersePostTrigger<override>():void = if (PuzzlePiece?): Event := triggered_event: Triggered := Triggered TriggeredEntity := option{Entity} Entity.SendUp(Event)
Triggerable Mesh Component
The triggerable_mesh_component toggles the visibility of a mesh_component on an entity. This requires providing overrides for the following triggerable interface functions:
SetInitialTriggeredStatePerformActionPerformReverseAction
The initial triggered state is determined by checking whether or not the mesh starts as visible in the game world as set in the mesh component’s details panel.
The action for the
triggerable_mesh_componentis to make themesh_componentmesh visible. To do this, retrieve themesh_componentfor the corresponding entity, set the mesh to visible, then set thetriggerable_mesh_componentto the triggered state.Verse# Determine the initial triggered state of this component SetInitialTriggeredState<override>():void= if (MeshComponent := Entity.GetComponent[mesh_component], MeshComponent.Visible?): set Triggered = trueThe reverse action for the
triggerable_mesh_componentis to make themesh_componentmesh invisible. To do this, retrieve themesh_componentfor the corresponding entity, set the mesh to not visible, then set thetriggerable_mesh_componentto the not-triggered state.VersePerformReverseAction<override>():void = if (MeshComponent := Entity.GetComponent[mesh_component]): if (MeshComponent.Visible?): set MeshComponent.Visible = false set Triggered = false
Triggerable Movement Component
The triggerable_movement_component triggers an entity to move from one location to a series of other locations in sequence using the keyframed_movement_component. This requires providing overrides for the following triggerable interface functions:
SetInitialTriggeredStatePerformActionPerformReverseAction
In addition, this component uses a few helper functions:
GetMovementStates: Retrieve transforms of child entities defining the series of transformations for this entity.MakeDeltaTransform: Create a delta transform between two input transforms.ConstructKeyframeDeltas: Create an array ofkeyframed_movement_deltaobjects for input to thekeyframed_movement_component.SetAndPlayAnimation: Assign the keyframes and play the movement animation.
Add an editable float to this class that determines how long the keyframed animation lasts.
Verse@editable Duration:float = 2.0This class implements the
triggerableinterface, so overrides need to be provided for thePerformActionandPerformReverseActionfunctions since they have no default implementations. Since there is no initial triggered state and the action is a movement between locations, the default initial triggered state is false. This is what the base implementation does in thetriggerableinterface, so this function does not require an override in this component class.The action for the
triggerable_movement_componentis to move the entity along the series of transforms defined by child entities.VersePerformAction<override>():void = MovementStates := GetMovementStates() AnimationFrames:[]keyframed_movement_delta = ConstructKeyframeDeltas(MovementStates) SetAndPlayAnimation(AnimationFrames)The reverse action for the
triggerable_movement_componentis to move the entity along the series of transforms defined by child entities in reverse order. This function is similar to thePerformActionexcept that the transform array is in reverse order.VersePerformReverseAction<override>():void = MovementStates := GetMovementStates() ReversedMovementStates := for: Index := 0..MovementStates.Length - 1 ReversedElement := MovementStates[MovementStates.Length - 1 - Index] do: ReversedElement AnimationFrames := ConstructKeyframeDeltas(ReversedMovementStates) SetAndPlayAnimation(AnimationFrames)Define the helper functions that this component uses. First, create a function named
GetMovementStatesto retrieve the transforms that this movement moves to.VerseGetMovementStates():[]transform= for (ChildEntity : Entity.FindDescendantEntities(entity), Entity = ChildEntity.GetParent[]): ChildEntity.GetGlobalTransform()Define a helper function named
MakeDeltaTransformthat constructs a delta transform between two transforms. A delta transform, D, from transform A to transform B is the transform such that when transform D is applied to transform A, the result is transform B.To expand upon this, suppose that you have a Scene Graph entity with current transform A, but you want to scale, rotate, and translate your entity to have transform B. The delta transform D is the transform you need for a
keyframed_movement_deltato animate the motion of your entity from transform A to transform B.VerseMakeDeltaTransform(InTransformOne:transform, InTransformTwo:transform):transform = transform: Translation := InTransformTwo.Translation - InTransformOne.Translation Rotation := MakeComponentWiseDeltaRotation(InTransformOne.Rotation, InTransformTwo.Rotation) Scale := vector3: Forward := InTransformTwo.Scale.Forward / InTransformOne.Scale.Forward Right := InTransformTwo.Scale.Right / InTransformOne.Scale.Right Up := InTransformTwo.Scale.Up / InTransformTwo.Scale.UpDefine a helper named
ConstructKeyframeDeltathat creates an array ofkeyframed_movement_deltaobjects for input to thekeyframed_movement_componentanimation.VerseConstructKeyframeDeltas<public>(MovementStates:[]transform):[]keyframed_movement_delta = var LastTransform:transform = Entity.GetGlobalTransform() for (EntityIndex -> CurrentTransform : MovementStates): DeltaTransform := MakeDeltaTransform(LastTransform, CurrentTransform) set LastTransform = CurrentTransform keyframed_movement_delta: Transform := DeltaTransform Duration := Duration Easing := if (MovementStates.Length > 1): if (EntityIndex = 0):Create a function named
SetAndPlayAnimationthat assigns thekeyframed_movement_deltaarray to thekeyframed_movement_componentanimation and plays it.VerseSetAndPlayAnimation<private>(Frames:[]keyframed_movement_delta):void = if (KeyframedMovementComponent := Entity.GetComponent[keyframed_movement_component]): KeyframedMovementComponent.SetKeyframes(Frames, oneshot_keyframed_movement_playback_mode{}) KeyframedMovementComponent.Play()
Triggerable Light Component
The triggerable_light_component turns a light on and off. This requires providing overrides for the following triggerable interface functions:
SetInitialTriggeredStatePerformActionPerformReverseAction
The initial triggered state of the light is determined by whether or not the light is on, so the
SetInitialTriggeredStatechecks if the light is already on, and if it is, sets it to initially triggered.Verse# Determine the initial triggered state of this light SetInitialTriggeredState<override>():void= for: DescendantEntity : Entity.FindDescendantEntities(entity) Entity = DescendantEntity.GetParent[] MeshComponent := DescendantEntity.GetComponent[mesh_component] LightComponent := DescendantEntity.GetComponent[light_component] MeshComponent.IsEnabled[] LightComponent.IsEnabled[] do:The action performed by the
triggerable_light_componentis to turn the light on and switch to a mesh indicating that the light is on.VersePerformAction<override>():void= if (not IsLightOn?): for (DescendantEntity:Entity.FindDescendantEntities(entity), Entity = DescendantEntity.GetParent[]): if (MeshComponent := DescendantEntity.GetComponent[mesh_component]): if (LightComponent := DescendantEntity.GetComponent[light_component]): MeshComponent.Enable() LightComponent.Enable() else: MeshComponent.Disable() set IsLightOn = trueThe action performed by the
triggerable_light_componentis to turn the light off and set the non-emissivemesh_componentto visible.VersePerformReverseAction<override>():void= if (IsLightOn?): for (DescendantEntity:Entity.FindDescendantEntities(entity), Entity = DescendantEntity.GetParent[]): if (MeshComponent := DescendantEntity.GetComponent[mesh_component]): if (LightComponent := DescendantEntity.GetComponent[light_component]): MeshComponent.Disable() LightComponent.Disable() else: MeshComponent.Enable() set IsLightOn = false
Next Steps
Now that all of the Verse code has been written, build your Verse code. Next, construct prefabs with base entities and components that you can reuse in UEFN to create your puzzle experience.
Final Code
TriggerComponent.verse
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/SceneGraph }
using { Logging }
trigger_component<public> := class<final_super>(component):
@editable
InteractionVolume<private>:volume_device = volume_device{}
TriggerableComponents.verse
using { /Verse.org/SceneGraph }
using { /Verse.org/SceneGraph/KeyframedMovement }
using { /Verse.org/Simulation }
using { /Verse.org/SpatialMath }
EnabledToolTip<public><localizes>:message = "Whether or not this component is enabled."
triggerable_mesh_component<public> := class<final_super>(component, triggerable):
<# Triggerable interface #>