How to gain more performance with 100s of AI actors?

I have a simple AI that just moves to where the player is, using a behavior tree and navmesh. I’m trying to spawn 300 of them and I get 20 fps (not cooked game, just running from the engine). Their mesh is one of the Paragon characters. Single player game.

What I’ve tried:
Disabled collision on the mesh, I only have it on a collision cylinder.
Turned off “Can ever affect navigation” on all components.
Set the mesh to the lowest LOD (LOD 4).
Edit: also turned off “Generate overlap events” on all components.
Turned off “Always check floor”.
Disabled physics interaction.
Set the collision cylinder’s collision to query only.

None of these made any noticeable impact. Nor did turning off the animations. If I remove the mesh, that doubles my fps (well, 40 is still not great, but it would be acceptable. Although ideally I’d like to spawn in a couple hundred more of them, and of course invisible enemies aren’t part of my game design document :slight_smile: .)
They are all visible on the screen at once, so implementing some sort of occlusion culling wouldn’t work.

Now, I have a pretty old GPU, but I only get 30 fps even when none of them are visible, so that shouldn’t be the main problem I think.

What else can I try?

The good thing is that the profiling tools of unreal are quite good so you can check exactly what is taking time in your frame. You can start simple by using stat unit, to see if your frame time is mostly affected by the render or the game thread. You can then also use stat game to see what is taking time in your game thread. If you want to take a deeper look, you can also do stat startfile to record data while playing, and then stat stopfile to stop. Then you can use the profiler to analyze the file, and see which function is taking the most time.

Are you using a character as the base for your AIs? Because from my experience, the Character Movement Component is quite heavy, and is not meant to be used for that many units in a scene. If you find that this is the case after analyzing your profiling data, then you should consider swapping your AIs to be pawns instead of characters, with a Floating Pawn Movement component. I was recently trying to do something similar, and switching from characters to pawns tripled my fps

3 Likes

If you’re only dropping 10fps from a base of 30, you’re actually doing pretty great there.

Are you using NavMesh? If so, are you using Walking physics or NavMeshWalking physics? NavMeshWalking is significantly more performant for things of that nature (a lot of the principles for it were developed in conjunction with Dungeon Defenders, one of the earlier Unreal games that supported hundreds of independent enemies in world at once)

If you’re running 30fps with the engine staring at a blank screen in editor, you are in desperate need of new hardware.

2 Likes

I should’ve been more clear.

  • I get 110+ fps when no AI is spawned.
  • I get about 40 when 300 of them are spawned, but with no mesh.
  • I get 30 when they do have mesh, but they aren’t visible on the screen (I’m looking at a wall)
  • I get 20 when all of them are on the screen. (Same when I disconnect the “move to” node in the behavior tree graph, so they just stand there.)

I am using navmesh, with walking physics, because with NavMeshWalking they get stuck on each other / stop for no reason. Also, I get no performance gain (20 fps with NavMeshWalking as well).

Now I turned off cast shadow on the AI, this gives me 7 fps (20 to 27).

I’ll get back in a minute with profiling tool results.

1 Like

I am using a character as the base. Not sure how well would the Paragon character work as a simple pawn, but I’ll try if it comes to that.

Although based on my profiling results, I think that’s not even the main problem, but I might be wrong.


World tick time and tick time?

It seems to be it. CharMovementTotal, EndScopeMovementUpdate, MoveComponent Time and possibly others all are part of Character Movement Component. And all of that is happens in Tick of character movement, which is why Tick Time and World Tick Time are also high

points of advice before moving into drastic changes:

Do all of your Animation Blueprint nodes have the lightning bolt icon on them? If not, you are doing things that prevent them from running most efficiently

Also, how many drawcalls and polys are your models bringing into play?

Figuring out what’s up with NavMeshWalking could make a big difference. If your BehaviorTrees / movement are doing weird things when you switch to NavMeshWalking, then that could eat up any gains though. You might try using NavMeshWalking in combination with using a CrowdPathFollowingComponent in the AI Controller, but additional diagnoses may be necessary.

Other things that get into drastic changes, there are some options, or you might have to craft your own options, for making anim blueprints and AIs tick less frequently. A truly drastic change is writing a management system that actually handles ticking of your AIs on a less frequent basis, including the Pawns, the AI Controllers, the Animations, the AnimBlueprints, etc.

When you drop that many AIs into a situation, there’s a lot more going on than just making the AIs work… but that doesn’t mean it can’t be done.

1 Like

All these are good points, but as you can see from the screenshot, in this case it’s clear that the majority of the frame time is on the character movement
OP even said that the BTs are simple Move To tasks, and even tried with the meshes hidden, which only ticks montages by default

From my tests:

It’s also very easy to test, when you stop moving and the AI doesn’t have to move, FPS goes back to over 120

I’m not sure why the character movement takes this much time, which apparently happens when updating overlaps, and this test was done with NavmeshWalking instead of Walking even. When testing with a floating pawn movement component, my AIs can still trigger overlaps just fine when walking, so I don’t really know what the difference is

I made a new blueprint, derived from pawn instead of character, added the mesh, capsule collision, and a FloatingPawnMovement. (And made some minor changes so that the Paragon character at least doesn’t fail to compile, etc.). Now I’m spawning these instead, and I get about 34 fps when they are all on the screen (with no shadows).

And it’s the same after they stop moving, still 34. (Now they aren’t playing their animation, because the Paragon asset needs character movement component, but that’s a separate issue.)

What’s even more interesting to me is that if I turn on cast shadows in this case, it makes me go from 34 to 13 fps.

Untitled-3

63 fps with the mesh removed. (So that’s 40 to 63 with no mesh, using pawn instead of character.)

You can fix the anim blueprints to work with a pawn movement component, that shouldn’t be an issue

Can you do a stat startfile session and analyze the file in the profiler? Once you load it click on the Game Thread from the list in the bottom part of the window, and then click on the little fire icon next to the search bar in the middle of the window, that should expand to the slowest functions. Post a screenshot of the result

Also, do try to test in standalone instead of in the editor, there is a lot of overhead when running through the editor, which can of course affect performance. If you do run in standalone, you can make the window fullscreen by pressing the ` key and typing fullscreen

1 Like

With characters:

With pawns:

Running in standalone gives me unexpected(?) results:

  • Pawn, shadow: 15 fps
  • Pawn, no shadow: 40 fps
  • Character, shadow: 17 fps
  • Character, no shadow: 40 fps

This way there’s basically no difference between pawn and character. BUT only when they are all visible on the screen, when they are out of view I get:

  • Pawn, no shadow, out of view: 110+ fps
  • Character, no shadow, out of view: 66 fps

This, and the big difference between shadows on and shadows off makes me think it has to do with rendering / GPU?
So I made a “stat unit” in standalone as well. Is such a difference between PIE and Standalone normal?
Untitled-1

Thank you both by the way for helping me so far, I appreciate it a lot.

1 Like

It doesn’t make sense that the fps is the same, since in your first screenshot the gamethread is running twice as slow as the second one

But, on your pawn, there are a couple of settings that I would advise checking, especially when dealing with many instances at once:

  • Disable Morph Targets (True for performance)
  • Allow Anim Curve Evaluation (True for performance)
  • Per Bone Motion Blur (False for performance)
  • Skip Kinematic Update When Interpolating (True for performance)
  • Visibility Based Anim Tick Option (Tick Montages Only for performance)
  • Component Use Fixed Skel Bounds (Tue for performance)
  • Enable Update Rate Optimizations (True for performance)
5 Likes

I have no clue what’s going on anymore. I did a test, restarted the engine, run the exact same test again and I got completely different results. Here, I changed nothing between these two, other than restarting the engine (both are PIE, with characters):

By the way, on the 2nd one it shows an average of 24 fps with a minimum of 19, while in reality it never went below 26. How is this possible?

Also, when I was writing my previous post, I also had to restart the engine, because the enemies were invisible when I tried to run the game as a standalone. But only in standalone. The project was saved. I changed nothing. Clicking on “new editor window (pie)” gave me one result (visible enemies) and just clicking on “standalone game” gave me another (invisible ones). After a restart they were both the same.

I’m just writing this to show the amount of weirdness I’m experiencing.
So now I’m gonna do everything again, run the tests, and restart the engine every. single. time. in between them. Just to be sure.

Okay, now I noticed that making a profiling session file affects the performance, maybe that was the reason for the 24 vs 26+ difference. Anyway.
I still get the same result (no difference in average fps between char and pawn when played in standalone).

I started recording for the profiler when they were all on screen, so the resulst don’t show the huge difference when they’re all out of view (66 to 110+). Unfortunately in my case that’s almost irrelevant.

I made the changes on the pawn that you listed here, no difference.

I made a build and I get a minimum of 47 fps with all 300 AI on screen. (So PIE: 27, standalone: 40, build: 47+). Then I made these changes you listed on the skeletal mesh component of the character based AI too, and I’m getting a couple more fps out of it. Good tips, thanks again.

It seems like I can stick to the character based solution, since in most cases all of these characters will be on screen anyway. At least for now. Hopefully adding other stuff won’t be too taxing on performance later.

Honestly I’m a bit surprised that you’re not getting a big performance boost by switching away from character movement, for me it was an absolutely massive difference, and I was testing with only 150 enemies on screen

Just out of curiousity, I have not seen it mentioned here, are your animation blueprints running on threadsafe threads?

Worth pointing out in this thread, since someone just brought me back to it, is that 5.x includes a new Mass Entity system, that should allow for major improvements in some of these areas. Unfortunately, there’s not much in the way of documentation or samples of how to use it yet.

2 Likes

Not even kidding when I say that this might be one of the most useful comments I have seen on these forums. Might add that if you really want to still use Characters instead of Pawns (like me), make sure to tick “Update only if rendered” on their movement component - that is, if that wont break AI functionality in your game :slight_smile: