Medics are a common character archetype in many games. A medic's job is to heal nearby characters, and they help their teammates recover after sustaining damage. Medics serve different roles depending on the game, for instance, doctors that serve patients in a hospital, combat medics that help their team fight as well as heal, or neutral stations that heal anyone.
The medic character you'll create in this example follows a set of logic rules.
- Idle:
- Begin Healing Agents
- Healing Loop
- Navigate to Agent
The medic begins idle, and patrols until an agent enters the healing zone. That agent gets added to the medic's healing queue. The medic needs to track the agent it needs to heal next, and a queue provides a useful data structure for this purpose since queues are a first-in, first-out data structure. This means the character that enters the healing zone first will be the first to get healed.
Once the medic gets the agent it needs to heal next, it first checks if the agent's health is below the healing threshold. If so, it begins healing them at a specific rate until the agent's health reaches the threshold, or the agent exits the healing zone. While healing, the medic will attempt to stay close to the agent by continuously navigating to them. Once the agent's health is back up to the threshold, the medic gets the next agent to heal and starts the process over again. If there are no agents to heal, the medic goes back to being idle.
You can visualize the logic of the medic NPC using the Finite-State Machine below. For more information on finite-state machines, check out Understanding NPC Behaviors.
By completing this guide, you'll learn how to create a custom medic character using the NPC Behavior Script that heals other nearby characters when their health is under a certain threshold. The complete script is included at the end of this guide for reference.
Creating a new NPC Behavior Script
To start creating your own NPC Medic Character, create a new NPC Behavior script named medic_example. For more information on creating your own NPC Behavior script, see Create Your Own NPC Behavior. Open the Verse file in Visual Studio Code.
Follow these steps to create an NPC Behavior Script in UEFN that spawns a medic character that heals nearby players.
Implementing the Healing Queue
The NPC Behavior Script starts with several values used for character movement and debug visualization. You won't need all of them in this script, so you'll remove the unnecessary code now.
-
At the top of the
medic_example
class definition, remove the values beforeOnBegin()
. Your medic character will not wait to move to characters, and will instead follow them around when healing. You don't need the debug values for this example, and you'll use other objects to visualize when your medic heals characters. -
In
OnBegin()
, after getting the character's interfaces in the firstif
statement, remove the code inside thethen
statement. Your medic character does not need to loop between points after spawning, and will instead patrol around their spawn point waiting for characters to heal. -
Remove the
DrawDebugLocation()
andDrawDebugLookAt()
functions. You won't use the debug values in this example, so you don't need the associated functions that use them either.
After removing the unneeded code, you can start to build your medic character.
-
At the top of the
medic_example
class definition, add the following values:-
editable float
HealingThreshold
. This is the threshold of health characters must be under to receive healing.# The HP threshold a character must be at before healing them. @editable HealingThreshold:float = 50.0
-
Add an editable float
HealingDelay
. This is the amount of time to wait between each instance of healing while healing characters. Change this depending on whether you want your medic to heal slower or faster.# The HP threshold a character must be at before healing them. @editable HealingThreshold:float = 50.0 # How long to wait before healing characters @editable HealingDelay:float = 1.5
-
An editable float
HealingAmount
. This is the amount of health to heal characters per healing instance. When your medic NPC heals a character, they will heal the character by aHealingAmount
everyHealingDelay
seconds.# How long to wait before healing characters @editable HealingDelay:float = 1.5 # How much to heal characters per healing instance @editable HealingAmount:float = 5.0
-
An editable mutator zone
HealVolume
. This is the volume characters enter to receive healing. You'll use a mutator zone in this example because the mutator zone has anAgentEntersEvent
which your medic can subscribe to and check for characters that might need healing.# How much to heal characters per healing instance @editable HealingAmount:float = 5.0 # The volume characters enter to receive healing. @editable HealVolume:mutator_zone_device = mutator_zone_device{}
-
An editable VFX spawner
VFXSpawner
. Visual feedback is important to know your code is working, so you'll use a VFX spawner to spawn effects when a character is being healed.# The volume characters enter to receive healing. @editable HealVolume:mutator_zone_device = mutator_zone_device{} # The VFX spawner to play VFX as characters are being healed. @editable VFXSpawner:vfx_spawner_device = vfx_spawner_device {}
-
A variable optional
agent
namedAgentToFollow
. This stores a reference to the character the medic should follow while healing them.# The VFX spawner to play VFX as characters are being healed. @editable VFXSpawner:vfx_spawner_device = vfx_spawner_device {} # The agent to follow while they're being healed var AgentToFollow:?agent = false
-
A variable queue of agents named
AgentsToHeal
. If multiple characters need healing, your medic will heal characters based on the order they entered theHealVolume
. You'll set up the queue code in the next step. For more information on the queue data structure, see stacks and queues in verse.# The agent to follow while they're being healed var AgentToFollow:?agent = false # The queue of agents to heal in the case of multiple agents entering the heal volume. var AgentsToHeal<public>:queue(agent) = queue(agent){}
-
A variable float
UpdateRateSeconds
. This is the amount of time to wait between updating the position of theHealVolume
andVFXSpawner
.# The queue of agents to heal in the case of multiple agents entering the heal volume. var AgentsToHeal<public>:queue(agent) = queue(agent){} # Used to specify how quickly to update the position of the HealVolume and VFXSpawner UpdateRateSeconds<private>:float = 0.1
-
-
To implement the
AgentsToHeal
queue, you'll use the code provided at the end of this step.- Back In Verse Explorer, right-click on your project name and choose Add new Verse file to project to open the Create Verse Script window.
-
In the Create Verse Script window, click Verse Class to select it as your script.
-
Name your Verse class by changing the text in the Class Name field to
queue
. -
Click Create to create the Verse file.
-
In Verse Explorer, double-click the name of your Verse file to open it in Visual Studio Code.
-
Replace the code in your
queue
file with the following code. This code implements a generic queue of typetype
using a list data structure. This is an example of a parametric type since the queue implementation will work regardless of the type you create it from. In your example, you'll be using a queue of characters, so your queue definition inmedic_example
will bequeue(agent)
.list(t:type) := class: Data:t Next:?list(t) queue<public>(t:type) := class<internal>: Elements<internal>:?list(t) = false Size<public>:int = 0 Enqueue<public>(NewElement:t):queue(t) = queue(t): Elements := option: list(t): Data := NewElement Next := Elements Size := Size + 1 Dequeue<public>()<decides><transacts>:tuple(queue(t), t) = List := Elements? (queue(t){Elements := List.Next, Size := Size - 1}, List.Data) Front<public>()<decides><transacts>:t = Elements?.Data CreateQueue<public><constructor>(InData:t where t:type) := queue(t): Elements := option: list(t): Data := InData Next := false Size := 1
-
Organizing your Verse scripts into distinct folders can help with organization, as well as provide ways to easily reference commonly used files. As an example of this, you'll create a folder to store your NPC behaviors in this project. In Visual Studio Code, hit the New Folder button to create a new folder in your UEFN Project. Name the folder
npc_behaviors
. Then drag yourmedic_example
Verse file into the new folder.
Your code in medic_example
should now compile correctly.
Healing Characters Inside a Volume
When an injured character enters the HealVolume
, your medic character should begin healing them if their health is less than the HealingThreshold
. Once the character's health is above the HealingThreshold
, your medic should stop healing that character, and move to the next character that needs healing. In the case of multiple characters, your medic should heal characters in the order they entered the HealVolume
. Follow these steps to heal characters when they enter the HealVolume
.
-
Back in your
medic_example
file, inOnBegin()
after thethen
statement, start aloop
. Inside theloop
, get the result of theDequeue()
function from theAgentsToHeal
queue and save it in a variableDequeueResult
.then: loop: # Get the next agent in the queue to heal. If there is an agent to heal, heal them by calling AgentToHeal. # If there are no agents to heal, wait until an agent enters the HealVolume if: DequeueResult := AgentsToHeal.Dequeue[]
-
The
DequeueResult
variable is atuple
that returns both a copy of theAgentsToHeal
queue with the first element removed and the agent at the front of the queue. UpdateAgentsToHeal
by setting it to the first value in the tuple, and save the second value as theAgentToHeal
.if: DequeueResult := AgentsToHeal.Dequeue[] set AgentsToHeal = DequeueResult(0) AgentToHeal := DequeueResult(1)
-
Once you have the agent to heal, you need to start healing them while they're in the
HealVolume
. You'll define a new function namedHealCharacter()
to handle this. Add a new function namedHealCharacter()
to themedic_example
class definition. This function takes theAgentToHeal
both theNavigatable
andFocusable
interfaces of the medic characters as function arguments. Add the<suspends>
modifier to this function, since it needs to perform several asynchronous tasks when healing a character.# Heal the character, then wait a HealingDelayAmount of time. # Ends when the character's health reaches the HealingThreshold # or the character leaves the HealVolume. HealCharacter(AgentToHeal:agent, Navigatable:navigatable, Focusable:focus_interface)<suspends>:void=
-
In
HealCharacter
, check if theAgentToHeal
is in the volume by callingIsInVolume[]
, and passingAgentToHeal
as an argument. If the agent is in the volume, you can begin healing them. All healable agents implement thehealthful
interface, which is part of the agent'sfort_character
. Get the agent'sfort_character
and save it in a valueCharacterToHeal
.HealCharacter(AgentToHeal:agent, Navigatable:navigatable, Focusable:focus_interface)<suspends>:void= # Only heal the character if they are inside the HealVolume if: HealVolume.IsInVolume[AgentToHeal] CharacterToHeal := AgentToHeal.GetFortCharacter[]
-
With the character ready to heal, you need to make sure your medic stays close to the character being healed. Create a
navigation_target
fromAgentToHeal
usingMakeNavigationTarget
and save it in a variableNavigationTarget
. Then in abranch
statement, call theNavigateTo()
function using the NPC'snavigatable
interface to have your medic navigate to theAgentToHeal
. Also in thebranch
function, call theMaintainFocus()
function to make sure your medic focuses on theAgentToHeal
. Using abranch
statement in this context lets you run bothNavigateTo()
andMaintainFocus()
asynchronously at the same time, and lets you run any code after yourbranch
immediately. For more information on branch expressions, see the branch in Verse page.# Only heal the character if they are inside the HealVolume if: HealVolume.IsInVolume[AgentToHeal] CharacterToHeal := AgentToHeal.GetFortCharacter[] then: Print("Character is in volume, starting healing") NavigationTarget := MakeNavigationTarget(AgentToHeal) branch: Navigatable.NavigateTo(NavigationTarget) Focusable.MaintainFocus(AgentToHeal)
-
Enable the
VFXSpawner
to play VFX as your medic heals a character. Then in adefer
expression, disable theVFXSpawner
. Because the code for disabling theVFXSpawner
is in adefer
expression, it won't run until the current scope exits. In this situation, it means that the code will only run when the function ends, so it is guaranteed to be the last thing that happens in the function. For more information on defer expressions, see the defer page.branch: Navigatable.NavigateTo(NavigationTarget) Focusable.MaintainFocus(AgentToHeal) VFXSpawner.Enable() defer: VFXSpawner.Disable()
-
When healing the
CharacterToHeal
, healing should stop when one of two conditions happens. Either the character's health is healed past theHealingThreshold
, or the character exits theHealVolume
. To accomplish this, you'll use arace
expression. Set up arace
expression between aloop
and anAwait()
on theHealVolume.AgentExitsEvent.
branch: Navigatable.NavigateTo(NavigationTarget) Focusable.MaintainFocus(AgentToHeal) VFXSpawner.Enable() defer: VFXSpawner.Disable() race: loop: HealVolume.AgentExitsEvent.Await()
-
Inside the
loop
, get the current health of the character usingGetHealth()
and save it in a valueCurrentHealth
. Then in anif
statement, check if theCurrentHealth
plus theHealingAmount
is greater than theHealingThreshold
. If so, your medic should stop healing andbreak
out of the loop. However, if the character's current health is just a little less than the healing threshold, you want to heal them up to the healing threshold. Add a secondif
statement inside the first one that checks ifCurrentHealth
is less than theHealingThreshold
. If so, set the character's health to theHealingThreshold
.race: loop: CurrentHealth := CharacterToHeal.GetHealth() if(CurrentHealth + HealingAmount > HealingThreshold): if (CurrentHealth < HealingThreshold): CharacterToHeal.SetHealth(HealingThreshold) PrintNPCB("Character has reached HealingThreshold, stopping healing") break HealVolume.AgentExitsEvent.Await()
-
Otherwise if the
CurrentHealth
plus theHealingAmount
is not greater than theHealingThreshold
, set the character's health to theCurrent Health
plus theHealingAmount
.if(CurrentHealth + HealingAmount > HealingThreshold): if (CurrentHealth < HealingThreshold): CharacterToHeal.SetHealth(HealingThreshold) PrintNPCB("Character has reached HealingThreshold, stopping healing") break else: CharacterToHeal.SetHealth(CurrentHealth + HealingAmount)
-
At the end of the
loop
, sleep for aHealingDelay
amount of time. Without this sleep, characters will be healed every simulation update, so theHealingDelay
will prevent them from being healed instantly. Your completedHealCharacter()
code should look like the following.# Heal the character, then wait a HealingDelayAmount of time. # Ends when the character's health reaches the HealingThreshold # or the character leaves the HealVolume. HealCharacter(AgentToHeal:agent, Navigatable:navigatable, Focusable:focus_interface)<suspends>:void= # Only heal the character if they are inside the HealVolume if: HealVolume.IsInVolume[AgentToHeal] CharacterToHeal := AgentToHeal.GetFortCharacter[] then: Print("Character is in volume, starting healing") NavigationTarget := MakeNavigationTarget(AgentToHeal) branch: Navigatable.NavigateTo(NavigationTarget) Focusable.MaintainFocus(AgentToHeal) VFXSpawner.Enable() defer: VFXSpawner.Disable() race: loop: CurrentHealth := CharacterToHeal.GetHealth() if(CurrentHealth + HealingAmount > HealingThreshold): if (CurrentHealth < HealingThreshold): CharacterToHeal.SetHealth(HealingThreshold) PrintNPCB("Character has reached HealingThreshold, stopping healing") break else: CharacterToHeal.SetHealth(CurrentHealth + HealingAmount) Sleep(HealingDelay) HealVolume.AgentExitsEvent.Await()
-
Back in
OnBegin()
, in thethen
expression inside of yourloop
, callHealCharacter()
by passing theAgentToHeal
, theNavigable
interface, and theFocusable
interface.if: DequeueResult := AgentsToHeal.Dequeue[] set AgentsToHeal = DequeueResult(0) AgentToHeal := DequeueResult(1) then: Print("Dequeued the next agent to heal") HealCharacter(AgentToHeal, Navigatable, Focusable)
-
Your medic will not always have a character to heal near them, and the
Dequeue[]
function will fail if there are no agents in theAgentsToHeal
queue. To handle this, add anelse
statement to the end of theloop
. Inside thisif
statement, callSleep()
for aHealingDelay
amount of time, thenAwait()
theHealVolume.AgentEntersEvent
. This way your medic character will not endlessly callDequeue[]
on theAgentsToHeal
queue, and will instead wait for a new character to enter the `HealVolume before restarting the loop. Your completed loop should look like the following.loop: # Get the next agent in the queue to heal. If there is an agent to heal, heal them by calling AgentToHeal. # If there are no agents to heal, wait until an agent enters the HealVolume if: DequeueResult := AgentsToHeal.Dequeue[] set AgentsToHeal = DequeueResult(0) AgentToHeal := DequeueResult(1) then: Print("Dequeued the next agent to heal") HealCharacter(AgentToHeal, Navigatable, Focusable) else: Print("AgentsToHeal is empty!") Sleep(HealingDelay) HealVolume.AgentEntersEvent.Await()
Tracking when Characters are in the Heal Volume
To know when characters enter or exit the HealVolume
, you'll subscribe both the HealVolume
's AgentEntersEvent
and AgentExitsEvent
to new functions.
-
Add a new function named
OnAgentEnters()
to themedic_example
class definition. This function takes the agent who just entered theHealVolume
, and enqueues them in theAgentsToHeal
queue.OnAgentEnters(EnteredAgent:agent):void= Print("Agent entered the heal volume")
-
In
OnAgentEnters()
, check that the agent in the volume is not the medic character. If so, set theAgentsToHeal
queue to the result of callingEnqueue[]
with theEnteredAgent
. Your completedOnAgentEnters()
function should look like the following:OnAgentEnters(EnteredAgent:agent):void= Print("Agent entered the heal volume") if (EnteredAgent <> GetAgent[]): set AgentsToHeal = AgentsToHeal.Enqueue(EnteredAgent)
-
When an agent exits the
HealVolume
, you don't need to remove them from theAgentsToHeal
queue. This is because the loop inOnBegin()
already callsDequeue[]
in a loop. However, you may want to run code when an agent exits the volume in your examples, so you'll set up a function for this now. Add a new function namedOnAgentExits()
to themedic_example
class definition.OnAgentExits(ExitAgent:agent):void= Print("Agent exited the heal volume")
-
In
OnBegin()
, subscribe theHealVolume
'sAgentEntersEvent
andAgentExitsEvent
toOnAgentEnters
andOnAgentExits
respectively. Since it should start disabled, this is a good place to callDisable()
on the character spawner.OnBegin<override>()<suspends>:void= Print("Hello, AI!") VFXSpawner.Disable() HealVolume.AgentEntersEvent.Subscribe(OnAgentEnters) HealVolume.AgentExitsEvent.Subscribe(OnAgentExits)
Moving the Heal Volume with the Medic
When the medic character moves, the HealVolume
needs to move with them to match their current position. The same is true for the VFXSpawner
. To do this you'll use a new function DeviceFollowCharacter()
.
-
Add a new function named
DeviceFollowCharacter()
to themedic_example
class definition. This function needs to run asynchronously to continuously update the device positions, so add the<suspends>
modifier to it.DeviceFollowCharacter()<suspends>:void=
-
Inside the
DeviceFollowCharacter()
function, get thefort_character
of the medic by first getting the agent usingGetAgent[]
, then callingGetFortCharater[]
.DeviceFollowCharacter()<suspends>:void= if: # Get the agent (AI Character) this behavior is associated with. Agent := GetAgent[] # Get the fort_character interface of the agent to access Fortnite character-specific behaviors, events, functions, and interfaces. Character := Agent.GetFortCharacter[]
-
Now you need to continuously move the
HealVolume
andVFXSpawner
to theCharacter
's position. You'll do this by looping aMoveTo()
on both devices. Start aloop
and get theCharacter
's transform and save it in a variableCharacterTransform
.if: # Get the agent (AI Character) this behavior is associated with. Agent := GetAgent[] # Get the fort_character interface of the agent to access Fortnite character-specific behaviors, events, functions, and interfaces. Character := Agent.GetFortCharacter[] then: loop: CharacterTransform := Character.GetTransform()
-
Call
MoveTo()
on both theVFXSpawner
and theHealVolume
, moving them to theCharacterTransform.Translation
andCharacterTransform.Rotation
. Set the duration toUpdateRateSeconds
seconds. Finally, callSleep()
for anUpdateRateSeconds
amount of time to prevent the devices from updating their position every simulation update. Updating the device position every simulation update can cause jittery movement on your devices. Your completedDeviceFollowCharacter()
code should look like the following.DeviceFollowCharacter()<suspends>:void= if: # Get the agent (AI Character) this behavior is associated with. Agent := GetAgent[] # Get the fort_character interface of the agent to access Fortnite character-specific behaviors, events, functions, and interfaces. Character := Agent.GetFortCharacter[] then: loop: CharacterTransform := Character.GetTransform() VFXSpawner.MoveTo(CharacterTransform.Translation, CharacterTransform.Rotation, UpdateRateSeconds) HealVolume.MoveTo(CharacterTransform.Translation, CharacterTransform.Rotation, UpdateRateSeconds) Sleep(UpdateRateSeconds)
-
In
OnBegin()
, after theif
statement where you save your character interfaces but before the loop, spawn an instance ofDeviceFollowCharacter()
.
Adding your Character to the Level
-
Create a new NPC Character Definition named Medic. Click your new NPC character definition to open the NPC Character Definition screen.
-
In the NPC Character Definition screen, modify the following properties:
-
Under NPC Character Type, set Type to Guard. The guard interface lets you access guard-specific character functionality, such as events for when the guard is alerted or suspicious, and lets you hire guards to use as allies. Guards may also equip weapons, while Custom and Wildlife-type characters currently cannot. You can also change the name of your character under the Name tab.
-
Under NPC Character Behavior, set Behavior to Verse Behavior. Then set the NPC Behavior Script to
medic_example
. Your character will still have access to functionality from the guard interface, but will use your Verse script to decide what to do duringOnBegin
andOnEnd
. -
In the Modifiers tab, under Guard Spawn Modifier, click the Cosmetic tab to change your character's cosmetic appearance. You can choose from a preexisting cosmetic, or enable Character Cosmetic Retargeting to use a custom model. Note that only guards and Custom-type characters can use character cosmetic retargeting, while wildlife cannot. For more information on character modifiers and which ones apply to different character types, see the Character Definition page.
-
-
Save your NPC character definition. In the Content Browser, drag your NPC character definition into the level. This will automatically create a new character spawner and assign your NPC character definition to it.
-
Drag one mutator zone and one VFX spawner device into the level.
-
Select your character spawner. In the Outliner, under User Options:
-
Set AIBehavior Script override to your
medic_example
script. Overriding the `AIBehavior Script in the outliner allows you to reference devices in the level, and you'll need this functionality to assign your HealVolume and VFXSpawner. -
Set HealVolume to the mutator zone, and VFXSpawner to the VFX spawner you placed in the level.
-
-
Select your mutator zone. In the Outliner, under User Options, set Zone Visible During Game to True. This will help you visualize where the
HealVolume
is, and how it moves with the medic character. -
Select your VFX Spawner. In the Outliner, under User Options, set Visual Effect to an effect of your choice. This example uses the Bubbles effect to convey healing, but you might want to use something different, such as fireworks or sparks. Change the visual effect to suit the needs of your character.
-
Click Launch Session in the UEFN toolbar to playtest your level. When you playtest, your character should heal injured characters who enter the mutator zone. When healing a character, VFX should play and the medic should follow and focus on the character being healed.
Complete Script
The following is a complete script for an NPC Character that heals characters whose HP is under a certain threshold.
medic_example.verse
using { /Fortnite.com/AI }
using { /Fortnite.com/Characters }
using { /Fortnite.com/Devices }
using { /Verse.org/Colors }
using { /Verse.org/Random }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /UnrealEngine.com/Temporary/SpatialMath }
# A Verse-authored NPC Behavior that can be used within an NPC Definition or a Character Spawner device's Behavior Script Override.
medic_example<public> := class(npc_behavior):
# The HP threshold a character must be at before healing them.
@editable
HealingThreshold:float = 50.0
# How long to wait before healing characters
@editable
HealingDelay:float = 1.5
# How much to heal characters per healing instance
@editable
HealingAmount:float = 5.0
# The volume characters enter to receive healing.
@editable
HealVolume:mutator_zone_device = mutator_zone_device{}
# The VFX spawner to play VFX as characters are being healed.
@editable
VFXSpawner:vfx_spawner_device = vfx_spawner_device {}
# The agent to follow while they're being healed
var AgentToFollow:?agent = false
# The queue of agents to heal in the case of multiple agents entering the heal volume.
var AgentsToHeal<public>:queue(agent) = queue(agent){}
# Used to specify how quickly to update the position of the HealVolume and VFXSpawner
UpdateRateSeconds<private>:float = 0.1
OnBegin<override>()<suspends>:void=
VFXSpawner.Disable()
HealVolume.AgentEntersEvent.Subscribe(OnAgentEnters)
HealVolume.AgentExitsEvent.Subscribe(OnAgentExits)
if:
# Get the agent (AI Character) this behavior is associated with.
Agent := GetAgent[]
# Get the fort_character interface of the agent to access Fortnite character-specific behaviors, events, functions, and interfaces.
Character := Agent.GetFortCharacter[]
# Get the navigatable interface of the character to set specific targets for the character to travel to.
Navigatable := Character.GetNavigatable[]
# Get the focus_interface of the character to set specific targets to focus on after traveling to them.
Focusable := Character.GetFocusInterface[]
then:
# Set the HealVolume and VFXSpawner to continuously follow the NPC Character
spawn{DeviceFollowCharacter()}
loop:
# Get the next agent in the queue to heal. If there is an agent to heal, heal them by calling AgentToHeal.
# If there are no agents to heal, wait until an agent enters the HealVolume
if:
DequeueResult := AgentsToHeal.Dequeue[]
set AgentsToHeal = DequeueResult(0)
AgentToHeal := DequeueResult(1)
then:
PrintNPCB("Dequeued the next agent to heal")
HealCharacter(AgentToHeal, Navigatable, Focusable)
else:
PrintNPCB("AgentsToHeal is empty!")
Sleep(HealingDelay)
HealVolume.AgentEntersEvent.Await()
else:
# If code falls here something failed when gathering the agent and its interfaces
PrintNPCB( "Error in NPC Behavior Script on NPC Setup",
?Duration := 6.0,
?TextColor := NamedColors.Red )
# Heal the character, then wait a HealingDelayAmount of time.
# Ends when the character's health reaches the HealingThreshold
# or the character leaves the HealVolume.
HealCharacter(AgentToHeal:agent, Navigatable:navigatable, Focusable:focus_interface)<suspends>:void=
# Only heal the character if they are inside the HealVolume
if:
HealVolume.IsInVolume[AgentToHeal]
CharacterToHeal := AgentToHeal.GetFortCharacter[]
then:
PrintNPCB("Character is in volume, starting healing")
NavigationTarget := MakeNavigationTarget(AgentToHeal)
branch:
Navigatable.NavigateTo(NavigationTarget)
Focusable.MaintainFocus(AgentToHeal)
VFXSpawner.Enable()
defer:
VFXSpawner.Disable()
race:
loop:
CurrentHealth := CharacterToHeal.GetHealth()
if(CurrentHealth + HealingAmount > HealingThreshold):
if (CurrentHealth < HealingThreshold):
CharacterToHeal.SetHealth(HealingThreshold)
PrintNPCB("Character has reached HealingThreshold, stopping healing")
break
else:
CharacterToHeal.SetHealth(CurrentHealth + HealingAmount)
Sleep(HealingDelay)
HealVolume.AgentExitsEvent.Await()
# Sets the HealVolume and VFXSpawner to continuously follow the character by looping
# MoveTo onto the character's position.
DeviceFollowCharacter()<suspends>:void=
if:
# Get the agent (AI Character) this behavior is associated with.
Agent := GetAgent[]
# Get the fort_character interface of the agent to access Fortnite character-specific behaviors, events, functions, and interfaces.
Character := Agent.GetFortCharacter[]
then:
# Loop MoveTo on the HealVolume and VFXSpawner to match their position to the
# NPC Character
loop:
CharacterTransform := Character.GetTransform()
VFXSpawner.MoveTo(CharacterTransform.Translation, CharacterTransform.Rotation, UpdateRateSeconds)
HealVolume.MoveTo(CharacterTransform.Translation, CharacterTransform.Rotation, UpdateRateSeconds)
Sleep(UpdateRateSeconds)
# When an agent enters the HealVolume, add them to the
# AgentsToHeal queue if they are not the NPC Character.
OnAgentEnters(EnteredAgent:agent):void=
PrintNPCB("Agent entered the heal volume")
if (EnteredAgent <> GetAgent[]):
set AgentsToHeal = AgentsToHeal.Enqueue(EnteredAgent)
# When an agent exits the HealVolume, PrintNPCB to the log
OnAgentExits(ExitAgent:agent):void=
PrintNPCB("Agent exited the heal volume")
# Custom wrapper that provides a default duration and color.
PrintNPCB(Msg:string,?Duration:float = 3.0, ?TextColor:color = NamedColors.Green):void =
Print("[new_npc_behavior] {Msg}", ?Color := TextColor, ?Duration := Duration)
# This function runs when the NPC is despawned or eliminated from the world.
OnEnd<override>():void =
if(Agent := GetAgent[]):
Print(medic_example_message_module.OnEndMessage(Agent))
else:
PrintNPCB("OnEnd")
queue.verse
list(t:type) := class:
Data:t
Next:?list(t)
queue<public>(t:type) := class<internal>:
Elements<internal>:?list(t) = false
Size<public>:int = 0
Enqueue<public>(NewElement:t):queue(t) =
queue(t):
Elements := option:
list(t):
Data := NewElement
Next := Elements
Size := Size + 1
Dequeue<public>()<decides><transacts>:tuple(queue(t), t) =
List := Elements?
(queue(t){Elements := List.Next, Size := Size - 1}, List.Data)
Front<public>()<decides><transacts>:t = Elements?.Data
CreateQueue<public><constructor>(InData:t where t:type) := queue(t):
Elements := option:
list(t):
Data := InData
Next := false
Size := 1
On Your Own
By completing this guide, you've learned how to create a medic character that automatically heals characters under a certain threshold. Using what you've learned, try to create your own medic character with their own special behaviors.
-
Can you create a medic who swaps between damaging and healing volumes based on whether an enemy is in the volume?
-
How about a medic who uses a depletable resource to heal characters? How would the medic restore this resource? Could they restore it over time, or could they restore it by attacking enemies?