This tutorial shows you how to create a non-stacking, periodic healing ability that cancels when the player jumps.
You'll create the Verse script that defines the heal ability, all its supporting classes, and the gameplay systems that grant the ability.
Prerequisites
While this feature is experimental, you must enable Scene Graph Experimental Features in the Project Settings to use it.
Create the Verse Script
In this section, you'll create the Verse script that holds all the code for this tutorial.
On the menu bar, select Verse > Verse Explorer.
In the Verse Explorer, right-click on your project and select Create Verse File.
In the Create Verse Script window, name the script ability_example and select Create Empty.
On the menu bar, select Verse > Open Project in VS Code.
Input the following code to add required modules:
Verse# Verse Ability Example # A channeled heal-over-time ability that cancels if the player jumps. using { /Fortnite.com/Characters } using { /Fortnite.com/Input } using { /Fortnite.com/Playspaces } using { /UnrealEngine.com/Abilities } using { /Verse.org/Input } using { /Verse.org/SceneGraph } using { /Verse.org/Simulation }Add the utility function that returns a target entity's
fort_character. This will be used when assigning the effect to a target later.Verse######################################################### # UTILITY ######################################################### # Get an entity's fort_character, if available. (TargetEntity:entity).GetFortChar()<decides><transacts> : fort_character = first (Component : TargetEntity.GetComponents(), FortChar := fort_character[Component]) {FortChar}
Heal Context and Jump Cancel
Define two supporting classes: the jump cancel reason, and the ability context that holds targeting information and additional healing overrides.
Add the
jump_cancelclass. This is a simple typed marker that signals a specific cancellation reason.Verse######################################################### # CANCEL REASON - Why did the effect end? ######################################################### # A typed cancel reason so listeners can distinguish why the heal stopped. jump_cancel := class(cancel_reason){}Add the
heal_contextclass. The base class carries targeting information, but the new member variables provide optional value overrides so you can create effect variations based on context.Verse######################################################### # ABILITY CONTEXT - Data passed between layers ######################################################### # Custom context carrying heal-specific data. # NOTE: The base ability_context already provides Instigator, Participants, and Targets. heal_context := class(ability_context): # Optional heal effect value overrides. HealPerTickOverride : ?float = false TickIntervalOverride : ?float = false
Heal Effect Component
Create the effect component that contains the gameplay logic for the periodic heal.
Add the
heal_effect_componentand variables. The member variable values are editable in the Prefab Editor.Verse######################################################### # EFFECT COMPONENT - Lifetime & logic ######################################################### # The healing effect component that is attached to the heal effect prefab entity. # This class handles the effect's gameplay logic and lifetime. heal_effect_component := class(ability_effect_component): # The amount of healing to apply per TickInterval. @editableAdd the
OnJumpStateChanged()callback function. This attempts to cancel the effect when the player jumps.Verse# If the player starts jumping, cancel the channel. OnJumpStateChanged(FortCharacter : fort_character) : void = if (Cancel[jump_cancel{}]): returnAdd the
OnBeginUse()callback function override. This applies any value overrides specified in the ability context.Verse# Called immediately after the effect is added to the scene graph. # Use this for setup, caching references, etc. OnBeginUse<override>()<transacts> : void = # Grab the context data and store what we need. if (HealContext := heal_context[Context?]): if (HealPerTickOverride := float[HealContext.HealPerTickOverride?]): set HealPerTick = HealPerTickOverride if (TickIntervalOverride := float[HealContext.TickIntervalOverride?]):Add the
OnSimulate()callback function override. This binds the jumped event and contains the main update loop that runs until the effect ends.Verse# Main async logic update function. Runs after OnBeginUse completes. OnSimulate<override>()<suspends> : void = var ElapsedTime : float = 0.0 # Bind the JumpedEvent that cancels the heal. if: InstigatorAgent := Context?.Instigator FortChar := InstigatorAgent?.GetFortCharacter[] then: FortChar.JumpedEvent().Subscribe(OnJumpStateChanged)Save the Verse file.
In the Editor, select Compile Verse on the toolbar.
Create the Prefab
Construct the prefab that contains the heal_effect_component you made.
In the Content Browser, select the Add (+) button then select Entity Prefab.
In the Create Entity Prefab Definition window, select ability_effect.
Name the prefab PF_HealEffect.
Double-click the prefab to open the Prefab Editor.
In the Details panel:
Right-click the ability_effect_component and select Delete.
Select the Add Component (+) button, search for heal_effect_component, and add it.
Save the prefab.
Heal Ability
Define the heal_ability class that validates its use and binds the context and effect together.
Add the
heal_abilityclass andActiveEffectslist member variable.Verse######################################################### # ABILITY - Declaration & activation logic ######################################################### # Define the healing ability, its requirements, and factory overrides. heal_ability := class(ability(heal_context, ability_effect)): # Tracks live effect instances. var ActiveEffects<override> : []ability_effect = array{}Add the
CanUse()function override. This runs afterUse()is called to check if the use is permitted. This specific implementation ensures there is only one heal effect at a time.Verse# Gate ability activation. Current implementation restricts stacking and only allows one active heal at a time. CanUse<override>(AbilityEffectParent : entity, AbilityContext : ability_context)<reads><decides> : void = (ActiveEffects.Length = 0)Add the
MakeContext()factory function override. Once the ability is validated, it creates the context for binding to the effect.Verse# Context factory override. Called internally by Use(). MakeContext<override>()<transacts> : heal_context = heal_context{}Add the
MakeAbility()factory function override. This creates the effect entity. Specifically, the heal effect prefab created in an earlier section.Verse# Effect prefab factory override. Called internally by Use(). MakeAbility<override>()<transacts> : ability_effect = HealEffectPrefab := PF_HealEffect{} if (ValidAbility := ability_effect[HealEffectPrefab]): return ValidAbility return ability_effect{}
Player Ability Manager Component
Create the component that grants a player the heal ability, binds its input, and handles effect cleanup.
Add the
player_ability_manager_componentclass and member variables.Verse######################################################### # PLAYER COMPONENT - Granting Abilities and Wiring Input ######################################################### # Grants the owning player the heal ability and processes the related input. # This component is attached to the manager entity constructed by the ability_system_watcher. player_ability_manager_component := class<final_super>(component): # The base heal ability. HealAbility : heal_ability = heal_ability{}Add the
OnEffectEnded()callback function. This removes the effect from the player and scene when it finishes.Verse# Called by EndUseEvent subscription when the ability is finished (naturally or cancelled). OnEffectEnded(EffectEntity : entity) : void = # Remove the effect from the parent and the scene. EffectEntity.RemoveFromParent()Add the
OnSecondaryFire()callback function. This triggers the effect on player input, once bound to secondary fire.Verse# Triggered when the player inputs secondary fire. OnSecondaryFire(Args : tuple(player, logic)) : void = Player := Args(0) IsSecondaryFirePressed := Args(1) # Test if secondary fire was pressed (instead of released) and get the player agent and entity. if: IsSecondaryFirePressed? PlayerAgent := agent[Player] PlayerEntity := PlayerAgent.GetFortCharacter[].GetEntity[]Add the
OnSimulate()callback function override. This initializes the secondary fire input mapping and event callback.Verse# Called when the component begins simulating within the scene. OnSimulate<override>()<suspends> : void = # Get the owning player's input system. if: PlayerInput := GetPlayerInput[OwningPlayer] then: # Enable the ranged weapon input mapping (contains WeaponSecondary). PlayerInput.AddInputMapping(Character.RangedWeaponMapping) # Bind ADS / secondary fire to activate our heal.
Ability System Watcher
Create a watcher class that attaches the player_ability_manager_component to every player in the session.
Add the
ability_system_watcherclass.Verse############################################################## # ABILITY SYSTEM WATCHER - Attach Ability Managers to Players ############################################################## # A component that grants player_ability_manager_components to all players. ability_system_watcher := class<final_super>(component):Add the
AttachAbilityManagerEntity()function. This creates the manager entity and attaches it to the given player.Verse# Creates a new entity with the manager component attached, then attaches it to the player's fort character entity. AttachAbilityManagerEntity(Player : player) : void = if: Agent := agent[Player] FortCharEntity := Agent.GetFortCharacter[].GetEntity[] then: # Create an empty manager entity. ManagerEntity := entity{} # Create the manager component.Add the
OnSimulate()callback function override. This attaches the manager entity to all active and joining players.Verse# Main async update loop. OnSimulate<override>()<suspends> : void = loop: # Get all players in the playspace and attach ability manager entities to them. if (Playspace := Entity.GetPlayspaceForEntity[]): for (Player : Playspace.GetPlayers()): AttachAbilityManagerEntity(Player) # If any players are added later, this event subscription will trigger and attach an ability manager entity. Playspace.PlayerAddedEvent().Subscribe(AttachAbilityManagerEntity)Save the Verse file.
In the Editor, select Compile Verse on the toolbar.
Create the Prefab
Create the ability system watcher prefab and place it in the level so it starts running when the session begins.
In the Content Browser, select the Add (+) button then select Entity Prefab.
In the Create Entity Prefab Definition window, select the New Prefab (+) button.
Name the prefab PF_AbilitySystemWatcher.
Double-click the prefab to open the Prefab Editor.
In the Details panel, select the Add Component (+) button, search for ability_system_watcher_component, and add it.
Save the prefab.
Drag the PF_AbilitySystemWatcher prefab from the Content Browser into the Level, then save the level.
Damage Zone
Add a damage zone to your Level so you can test your heal ability.
In the Content Browser, select the All folder.
Search for Damage Volume.
Drag the Damage Volume into your Level.
In the Details panel, enable Zone Visible During Game.
Playtest
Launch a session and playtest to verify everything is working.
Once damaged, right-click to activate the ability and watch your health increase periodically.
While healing, jump to cancel the heal.
Verify that the ability ends on its own after a set duration.
Verify the ability doesn't stack when rapidly right-clicking.
On Your Own
Want to take this further? Here are a few ideas to explore on your own:
Add visual and audio elements to your effect prefab.
Experiment with override values in your ability context.
Make the heal target other players.
Complete Code
# Verse Ability Example
# A channeled heal-over-time ability that cancels if the player jumps.
using { /Fortnite.com/Characters }
using { /Fortnite.com/Input }
using { /Fortnite.com/Playspaces }
using { /UnrealEngine.com/Abilities }
using { /Verse.org/Input }
using { /Verse.org/SceneGraph }
using { /Verse.org/Simulation }