Hi,
I’m working on a small open world RPG (“The Elder Scrolls” like) and I have some problem with NPC setup.
Like in most (all?) open world RPG, I want my NPCs to be visible and active while in player radius, then be invisible and “paused” while out of radius but I really don"t know what’s the best way to achieve this. Plus I’m using world composition. Any help/ideas are welcome.
You need to split your World in multiple chunks. Each Chunk is a new Level, with NPCs, Enemies… etc
Now you need Level Streaming to generate a full world out of your chunks. Only Load visible Levels inside Player Radius. Unloaded Levels and theor NPCs won‘t consume resources.
They are already unloaded while distant tiles are unloaded and replaced by low LOD landscape mesh.
And that’s not what I’m looking for. In almost every Open world RPG, NPCs fade in/out at a certain distance around the player.
When they walk out of this distance, they fade out. When you follow them and you’re close enough, they fade in and keep walking from the location they faded out.
What culling means for an actor? Does it “freeze” it (Behavior tree, tick, etc) or only makes its skeletal mesh invisible?
And I know where is the draw distance on a mesh but can’t find such setting in a character class.
If guess I have to use a collision sphere on the player then execute some functions on the overlapped NPC actors but what are the things that I can do to save most performance?
Make the NPC skeletal mesh invisible
Stop Logic on brain component (using behavior tree)
Remove collision on caspule
Set actor tick enabled to disabled
???
I did some stress test on an empty level on a plane with a dynamic skylight and ~100 AI but even with this 4 steps, FPS are still far bellow the FPS without AI at all
Heres a little breakdown of a system I’ve built thats allowed me to have thousands of enemies in the level but only spawn the ones close to me.
A global manager that gets the location of every AI in my level and puts their location into an array of transforms. This only needs to happen once, on event begin play or construction script.
Break your level up into grids. Each grid will have 2 volumes. A larger trigger volume, and a smaller volume that contains all your NPC’s. When you enter the larger trigger volume, pull all the AI transforms that are in the smaller volume, into an array. Then every 5 seconds, your player character can check to see which of the transforms in that area are within a “spawning distance” and spawn those characters as you approach them. If the player leaves the trigger volume, stop checking the list of transforms for the specific volume that he left. Using this method, the player should only ever be checking a maximum of 4 separate volumes, if he is at the intersection point of 4 grid squares. Checking the list of transforms every 5 seconds means we can avoid using Event Tick as much as possible.
At this point, you can pass the logic onto the spawned NPC. Every 5 seconds, have the NPC loop a timer that checks to see how far away the player is, and if hes greater than “x”, destroy themselves, but pass their stored transform upon destruction back to the NPC manager. This way when the player character goes back, the NPC will spawn back right where they de-spawned. (This is only necessary if the AI moves at all, if its just a vendor that will never move, don’t worry about modifying the transform). If the NPC dies for whatever reason, you can remove the transform directly from the global manager, so the player never checks for it again until the next play through.
This system was allowing me to have 50 AI spawned at a time, and about 2-3,000 AI locations stored in memory that would spawn dynamically as I approached them, and de-spawn if I run far enough away, and even remember the location they de-spawned at if I decide to come back through the area. Using this approach I was able to maintain 120FPS constant in an otherwise empty level. This was also on a BP project mind you, so maybe a C++ project could allow for even better performance.
Guys, thanks for trying to help but please read my messages.
As it’s an open world RPG using streaming (world composition), I don’t want to destroy/spawn NPCs each time the NPC is out of the player’s radius.
This would makes the NPCs reset its behavior tree/teleport to it’s starting point, etc.
As distant landscape tiles are only lowpoly meshes without actor, only the actors on closest tiles are loaded. What I want is to set them in a “low CPU/memory usage” mode beyond a certain radius (something like 4000 UE units around the player) but not destroyed, and being fully active while in this radius.
I stated in my post that when I despawn the NPC, I transfer the transform back to the system, so the location is remembered when the player comes back through the area. Theres nothing to stop you from saving a structure that would contain all pertinent NPC data, and then reinitialise those variables from the global manager when they re spawn. In taking on a task such as the one you’ve given yourself here, you might have to rely on a custom solution, instead of hoping for a checkbox that solves your problem. I was just sharing something that might give you back some framerate, while allowing you to have persistent NPCs in the world. Besides all the things you’ve already tried, I don’t actually know any further way to tell them to go into a lower resource usage mode.
There are many ways of doing this. Vassili offers one good approach. Another approach is to have a Root Service on your Behavior Tree that modifies your NPC’s behavior (Idle, work, walk etc). Inside this service you can first check the distance between the Player and the NPC and if the distance is less than “MaxActiveDistance”, then continue (True), but if this is False (NPC is farther than MaxActiveDistance" then do nothing (False).
This effectively stops your BT from executing past your root service, effectively “Freezing” your NPC in place. You can take it further and also make the NPC invisible to save rendering cost. This is good because it keeps all your NPCs in the world frozen, but still keeps their BTs ticking and checking distance. A more performant solution is to destroy the actor altogether when far and respawn when close. This approach is more performant but harder because you will have to save each NPC’s state and restore it when Player gets close.
Ultimately you should try a few different approaches and come up with a custom solution that works for your game.