Hello BizChugg! Welcome to the UEFN forums
Sounds like you are building a cool way of controlling the NPC spawners!
As you mentioned, breaking this into 4 separate class instances is better, so lets do that (for your sanity!)
Solution
What is the problem? You want to enable a NPC spawner when there is atleast one player inside the zone. I’m assuming you want this to work for multiple players, since you didn’t specify.
volume_controlled_npc_spawner := class(creative_device):
@editable
Volume : volume_device = volume_device{}
@editable
NPCSpawner : npc_spawner_device = npc_spawner_device{}
var InZone : int = 0
OnBegin<override>():void=
Volume.AgentEntersEvent.Subscribe(OnEnter)
Volume.AgentExitsEvent.Subscribe(OnExit)
OnEnter(Agent:agent):void=
# Enable the spawner when there was no players, but the event was fired
if (InZone = 0):
NPCSpawner.Enable()
set InZone += 1
OnExit(Agent:agent):void=
set InZone -= 1
# Disable the spawner when there are no players left in the zone
if (InZone = 0):
NPCSpawner.Disable()
This will only enable the spawner device when there is atleast 1 player inside the given volume.
I don’t know what the intention of the switch device you were using, but you can certainly implement that into the snippet I gave you here.
NOTE: This code is untested, so I’m not 100% sure if it works.
Personal tips based on your code snipped
1. OnBegin overriding
I noticed that you overrode your OnBegin function like
OnBegin<override>()<suspends>:void=
However, you had no suspending code in the function body.
It’s worth noting that when overriding OnBegin
from a creative_device
the <suspends>
specifier is optional!
Think about it. A suspending function CAN/has the possibility to run in one frame, where a non-suspending function can NOT run in multiple frames.
Therefore, you only have to write
OnBegin<override>():void=
2. Event Usage
When you have an event, such as Volume.AgentEntersEvent, you can run code in two main ways when the event happens.
First way of handling events
You can “wait” for that event to happen. This means that somewhere in your code, you have to literally freeze the code until that event happens. Commonly, this is used in loops, races or other concurrency contexts.
You can accomplish this by using the .Await() method as such:
loop:
Volume.AgentEntersEvent.Await() # This will freeze the code, and wait for the event to occur.
SomeCodeRunsHereEveryTimeAgentEntersVolume()
A slight more advanced usage:
This is particularly useful for when you want to determine which event was triggered when listening to a collection of events. For example, like this:
OnBegin<override>():void=
for (VolumeIDX->VolumeDevice : VolumeDevices):
spawn {ListenForVolumeEnter(VolumeDevice, VolumeIDX)}
ListenForVolumeEnter(Volume:volume_device, VolumeIndex:int)<suspends>:void=
loop:
Volume.AgentEntersEvent.Await()
if (HasPlayerUnlockedLevel(Player, VolumeIndex)):
RunLevelCode(VolumeIndex)
Second way of handling events
You can Subscribe to events. I see that you have done this successfully, so I’m not going to explain this as detailed.
If you want to take a deeper dive, take a loot at this talk by Conan Reis at Unreal Fest 2023 Verse Concurrency—Time Flow: Everything, Everywhere in UEFN, All at Once | Unreal Fest 2023
especially at 29 minutes
Good luck with your project and feel free to come back to the forums when you need assistance!