NPCs Causing FPS Drops

I’ve recently started developing an NPC-fighting-based game. I’ve disabled collisions for player->player, player->npc, and npc->npc however when NPCs bunch up together I still receive massive amounts of lag. I’m talking going from 60 FPS to 8FPS. I read somewhere that disabling some humanoid states might help so I disabled all of the ones that I don’t use. That didn’t seem to help. I also tried changing all MeshParts to Box Colliders and I’m still getting massive FPS drops. I don’t think it’s anything to do with my code, it’s definitely something physics or rendering sided because like I said the game runs 60FPS fine until they all are inside of or very close to each other. Does anyone have any suggestions on what could be causing this?

pls no “create a custom humanoid, roblox’s are bad” replies

2 Likes

“create a custom humanoid, roblox’s are bad” Just kidding.

Hey, I’m currently building an NPC Service that can handle up to 300 NPCs. Generally I’d like to reach 1000 NPCs. But Right now the max is Quite literally 300NPCs. You get the best performance with 60 NPCs but can increase it up to 200NPCs without dropping below 40FPS in some cases.

As such I’ve done extensive research on Humanoids.

Roblox can’t really handle the NPCs. Simply because of the Physics. Using a Collision Group will help the Physics System in general. But the fact is there’s still some overhead in actually filtering them. Although all improvements are better than none.

The real down fall here is contact collisions. Roblox still count contact between the NPCs themselves and the world. And when they bunch up its gets really bad. Upwards to 50k contact points. Based on how many parts are inside of the character. There’s nothing you can really do about it sadly.

But some ways you can mitigate this is to simply try to reduce the number of “simulated” characters. This means you could possibly anchor a group of NPCs. and have 1 NPC control the movement of the rest. Which is an approach I’m currently in the process of taking.

Also You definitely don’t want NPCs running on a main thread.

Cheers.

1 Like

I’m definitely not anywhere near 300 NPCs at one time, I’m talking maybe 15-20 at a time. I keep NPCs de-spawned until the player(s) get to them. Is it usual for this to happen with 15-20 NPCs all standing in the same location?

Also, what do you mean a main thread? I have all NPCs running from one script. It fetches what target they should be chasing after every 1.25s and moves them towards that target every 1/9s.

I’m not really knowledgeable when it comes to technical terms. But lets say you launch Roblox. And Roblox is running your scripts on one Thread.

Thread being a line of processes that are handled by a single CPU core. (Not sure about this)

Anywho, If you have a dual core CPU. Then lets say there’s 2 threads. (Pretty sure there’s more than this)
If one core is always handling your scripts then the other cores aren’t doing anything.

By running your scripts in a new thread you let another CPU Core handle it so that it doesn’t slow down the main thread. And both scripts are able to run at the same time.

If that makes sense, Someone more technical than me should be able to elaborate more… and chew me out for being so illiterate haha.

But, Its okay if you still want the player to collide with the NPC. Just prevent the NPCs from colliding with themselves. For about 20 NPCs. You should be able to get away with this easily. It could be the way you are spawning them, or the way they are bunched up. Which as I said generates a lot of contact points which effects the physics.

Alright, thanks. I’ll see if I can do some raycasting to keep them kind of in a row instead of all bunching together.

just do a math.random one one axis.

Vector3.new(0, 0, math.random(50))

You can then check if an NPC is already in this location using a Region3 or something less computationally expensive. And if it is, just do another random, and repeat until you find a spot that’s not taken. Though this could cause an infinite loop if there’s less spaces than there are NPCs. So you’d need to be careful with that.

Edit: Actually your Ray might be better if you can use the same ray for all of your NPCs without having to raycast more than once.

I remember a question like this was raised some time ago. That never received a solution.

Try looking through this and see if you can salvage anything. I think that the underlying issue lies with the physics backend and such is out of your control to handle, though I wouldn’t don’t know.

@T0ny I decided to Raycast in front of the NPC and if it hits an NPC in front of it I just move the NPC to otherNPC.HumanoidRootPart.Position-otherNPC.HumanoidRootPart.CFrame.lookVector instead of its current target – this works well until you start circling them, then they get all bunched together again sometimes. It helps though.

@colbert2677 Yes, I looked through that post and debated replying to it before I made this one however that post was super old, not structured well, and I didn’t want to bump it after so long without a reply.

1 Like

You may have covered it while testing, but does the new lighting change performance for you at all? Something I also believe I overlooked in the post linked was the use of the microprofiler. This should highlight the big problems causing these drops.

Some suggestions;

  • rather than using a ray to detect close NPC’s, iterate through your NPCs and compare their current position to your moving NPCs intended position. This should be far less expensive. This is even better if you create an easily accessible list of NPCs as you create them, because now you’re only using that instead of constantly calling :GetChildren()
  • Give your NPCs slight target variations. If you’re moving them all to the player, their target possible should be the players position + (random horizontal vector within attack range). This will get enemies to circle, in an incredibly basic fashion.
  • a recent tweet by clonetrooper highlighted different humanoid collision styles, playing with this could have significant impact. (On my phone on work break, will try and edit a link in later)
  • R6 v R15 has significant differences. I barely had issues with R6, is it necessary to have X amount of NPCs being R15?

I’m not exactly sure how to interpret the microprofiler or notice which numbers are unusual, but when the lag spikes happen this is what it looks like:

Anything that sticks out to you?

In the microprofiler, if there are any specific bars spanning more than one section, this indicates a frame being lost to some process. So, if physics is spanning three sections, you know to disable collision and anchor the current object.

There’s a tutorial on the microprofiler on the developer wiki. Simply search for microprofiler.

1 Like