fighting up to two dozen enemies at once the game runs perfeclty smooth...

30 enemies and the game starts to go from the 300-480 fps down to 30-180.... 60 enemies and all attacking at once...the game goes down to 4-12 fps

now it is time to think about performance.

it is a 3D game, godot 3.5.1 with action combat mechanics.

the lag spikes mostly happens when all enemies are attacking, so it has something to do with the damage logic... which is basically a back and fort between the player script to check for defense and the enemy script to check for damage...and 1 box shaped hitbox that is activated on certain frames when enemies play their attacking animations but I don't know if I can make it anymuch simpler than that so it must be something lese.

when they enemies are just minding their own business and are not in fighting mode... the game runs decently well.

so what are the first things I can do to raise that performance when a bunch of enemies all go ammassing against you? I mean they will probably kill you since they can stun lock you, 60 vs 1 and even bees can kill a dude... but at least you will die with a good performance.

  • \$\begingroup\$ Have you run the profiler to check what is actually causing the game to slow down/ where most of the time is spent? \$\endgroup\$
    – Zibelas
    Jun 30, 2023 at 21:56
  • \$\begingroup\$ @Zibelas roughtly 50% of the _physicsprocess calls are related to the enemies attacking, then 30% related to enemies chasing the player, after all... nothing much else is happening in the test scene, so 80% of what is happening in the scene has to do with enemies trying to kill the player \$\endgroup\$
    – Cei
    Jun 30, 2023 at 22:08
  • 1
    \$\begingroup\$ I'm not familiar with Godot so it is a long shot but in Unity that can easily happen when a lot of trigger colliders stack on top of each other. In such case the solution is for enemy attack colliders to ignore each other and only register player collisions. There must be some collisions matrix that controls that. \$\endgroup\$
    – Nikaas
    Jul 6, 2023 at 7:31

1 Answer 1


Before I start, a note on terminology: I'll be calling "agent" to any non-player entity that does things in the world. They include enemies, but the term "agent" is not limited to enemies, it applies also - for example - to companion characters.

And about performance in general: before we look into doing things faster (i.e. porting to C# or C++), we should look into doing things less often, which is what this answer focuses on... But you should also consider doing less. If ultimately you need to cap the number of enemies in the world, then design around that.

Chase performance

I presume the agents figure out how to much to go to the player, or whatever target they have, each frame. You want to change that, so that only one agent figures out the direction per frame, and the rest just keep going the same direction they were going (which is likely to be approximately correct, unless the target - e.g. the player - is a lot faster than the enemies).

Attack performance

On one hand, you don't want to process multiple attacks the same frame. Another way to put it is that you will give the player invulnerable frames (even if it is only the same frame of the attack). So only the first attack to connect in a frame gets processed and others are discarded.

On the other, you might need to limit how many enemies can attack at the same time... Before it gets to collision checks... That is: the enemies take turns to attack.

It does not have to be one by one, for example, you could have at most 5 enemies attempting to attack at the same time. And the others do not have to be idle... They can even play attack animations, but they will not harm the player (not because the player is invulnerable and code returns early, but because their collisions are disabled and so the code for them never runs).

Smoke and mirrors I say.

AI director

This all suggest that instead of having a back and forth between player and enemy scripts, before doing anything, you want to centralize the logic. There must be some central code that decides who can update their path, and who gets to attack.

The place where you centralize the logic has a name: It is the AI director.

Traditionally the AI director... Directs. It tells the agents what to do. And most of its operation is asynchronous. A second approach is also possible: have each agent report their intention, and the director tells them if they are allowed or not to proceed. I believe that for you it is easier to refactor your code into that second approach. And you might then refactor into the traditional approach if you need to.

Note: Also traditionally the AI also handles spawning enemies, and dynamic difficulty... It does not have to. There are no hard rules. It is your game. But, if you have code to spawn enemies, that might be a good place to implement - or at least connect - your AI director. Otherwise, I suggest to start with a new autoload.

Having an AI director will ease experimenting (e.g. what happens if the enemies don't attack at all? what happens if only a few of them chase? etc.). And that is what I would suggest you do: implement an AI director (it does not have to be a complex one, just one that does as much as you need), and experiment with it, to try to find a balance between performance and game-play.


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .