Optimising for large groups of enemies

Hey guys, so I’m working on a game where there’s going to be many enemies on screen at once, perhaps up to 30 at once. Each of these enemies may possibly be using pathfinding etc. Currently I have all of the enemies being spawned when the level starts, in total there could technically be between 0-320 enemies in the level at once. Obviously this seems to be getting extremely computationally heavy and the frame rate can slow down quite substantially even on my fairly good spec PC. So I’m wondering, what are some routes I can go about optimising this situation? Do objects not on screen get rendered or can I prevent that myself if they’re not? Should I create NPC spawners that spawn the enemies when the player gets so close, and then de-spawns and saves the stats of the enemies when the player is too far away? Are there flocking methods like a boids group or something that would work well with pathfinding? I realise this question is somewhat broad, but I’m just finding it hard to know where to start.

Should I create NPC spawners that spawn the enemies when the player gets so close, and then de-spawns and saves the stats of the enemies when the player is too far away?

Absolutely. While they won't be rendered if they're not in the camera frustrum... they'll still run all their scripts (eg: pathfinding, calculating distances between itself and the player, whatever else your AI is doing). That making them only spawn when within range should help your performance significantly.

Also, make sure you're using Rigidbodies, not CharacterControllers. In tests with 32 animated models with simple AI my performance tests have meant a difference of 300+ FPS and 40 FPS.

As for Boids/Flocking, there are lots of solutions out there, difficult to know how they'd integrate with your pathfinding without knowing what you're using, though. Off the top of my head, there's one on the unify wiki, and of course UnitySteer, for starters.

Check things like line of sight only every nth frame and stagger which get updated. That evens out the load. For example use the fixed frame count modulo 32 and the entity id modulo 32.

Or keep a work queue fifo and do N ai cycles each frame.