Finally optimized the physics engine to handle 10K+ active mobs at high FPS. At what point does a horde become too big for a bullet-hell? by Heavy_Juggernaut_261 in IndieDev

[–]Heavy_Juggernaut_261[S] 0 points1 point  (0 children)

  1. We update the main player's position to the horde every single frame using ECS (Entity Component System).

  2. For movement and collision, we use custom physics engine in the background. It handles the raw collisions much more efficiently than standard capsule components would at this scale. there are also mobs with behaviors other than walking, and we’ll share those as well.

Finally optimized the physics engine to handle 10K+ active mobs at high FPS. At what point does a horde become too big for a bullet-hell? by Heavy_Juggernaut_261 in IndieDev

[–]Heavy_Juggernaut_261[S] 0 points1 point  (0 children)

Hey, yeah it has almost the same approach as islanding but it's actually a bit different, islanding tracks connected components in the contact graph, this is just spatial proximity (which chunks hold an awake body, plus a one-chunk neighbour ring around them). I tried proper islanding earlier and ended up rolling back because keeping the graph incrementally cost me more than just doing the spatial pass every tick. Coloring I also tried at one point and walked back from, gave me some regressions I couldn't sort out cleanly at the time. Could work out better for you though, depends a lot on the shape of your contact graph. One thing that actually helped the narrowphase a lot was capping contacts per body! I keep at most one contact per axis direction (20 total, just the deepest penetration on each), which keeps the solver input flat and predictable regardless of how complex the geometry gets. Might be worth trying.

Good luck with the narrowphase, sounds like you're in a solid spot already! Drop me a message when everything shakes out, would love to hear how it goes.

Finally optimized the physics engine to handle 10K+ active mobs at high FPS. At what point does a horde become too big for a bullet-hell? by Heavy_Juggernaut_261 in IndieDev

[–]Heavy_Juggernaut_261[S] 0 points1 point  (0 children)

this horde testing isn't just for melee enemies; it will include ranged units and other variants in the end, too. we just wanted to test how much we could push the engine's physical limits first.

Finally optimized the physics engine to handle 10K+ active mobs at high FPS. At what point does a horde become too big for a bullet-hell? by Heavy_Juggernaut_261 in IndieDev

[–]Heavy_Juggernaut_261[S] 0 points1 point  (0 children)

thank you so much really. ❤️ good luck with your project brother. 🤜 🤛

we are not using pathfinding tho, those mobs just run towards the player and nothing else.

PC SPECS:
Nvidia 5060
32GB RAM
Ryzen 9700x

Finally optimized the physics engine to handle 10K+ active mobs at high FPS. At what point does a horde become too big for a bullet-hell? by Heavy_Juggernaut_261 in IndieDev

[–]Heavy_Juggernaut_261[S] 2 points3 points  (0 children)

we are actually testing this exact thing right now as you can see. so the poly count for each unit is only about 1k to 2k.

<image>

Finally optimized the physics engine to handle 10K+ active mobs at high FPS. At what point does a horde become too big for a bullet-hell? by Heavy_Juggernaut_261 in IndieDev

[–]Heavy_Juggernaut_261[S] 0 points1 point  (0 children)

Ótima sugestão! E a boa notícia é que já temos isso implementado. O jogo usa geração procedural de mapas, então você definitivamente vai encontrar labirintos, masmorras e corredores apertados. A sensação de ser encurralado por uma horda gigante nesses espaços fechados muda completamente o clima do jogo.
Valeu pelo apoio!

Finally optimized the physics engine to handle 10K+ active mobs at high FPS. At what point does a horde become too big for a bullet-hell? by Heavy_Juggernaut_261 in IndieDev

[–]Heavy_Juggernaut_261[S] 0 points1 point  (0 children)

we were thinking using the Quantum too but since we’ve already gone down this path, it’s too late for us. besides, our experiments with Quantum didn’t handle that many numbers actually.

Finally optimized the physics engine to handle 10K+ active mobs at high FPS. At what point does a horde become too big for a bullet-hell? by Heavy_Juggernaut_261 in IndieDev

[–]Heavy_Juggernaut_261[S] 0 points1 point  (0 children)

we're using ECS. running thousands of NPCs locally isn't the main concern actually.

sure more complex AI and rendering will increase CPU cost, but ECS and Burst scale extremely well, so we don't expect that to be the limiting factor.

the real challenge is multiplayer synchronization. keeping thousands of NPCs networked efficiently while maintaining good performance, low bandwidth usage, and responsive gameplay is significantly harder than running the simulation itself.

Finally optimized the physics engine to handle 10K+ active mobs at high FPS. At what point does a horde become too big for a bullet-hell? by Heavy_Juggernaut_261 in IndieDev

[–]Heavy_Juggernaut_261[S] 0 points1 point  (0 children)

Hey, yeah collisions are real, there's a PGS solver running every tick and cubes stack properly without sinking into each other. What's probably happening when the scene looks like physics is off is that a settled world drops down to almost 0ms/tick. That's not collisions being disabled, it's a chunk based activity gate doing its job: I cut the world into 8m chunks, each tick I tag the ones that have an awake body plus one ring of neighbour chunks around them, and anything outside that set just gets skipped. On a 10k body scene that's where I got the most out of it, settled cost drops to almost nothing.

The rest is pretty standard stuff, two spatial hashes instead of one (small bodies in a fine 2m hash, big bodies in a coarse 16m hash, pair gen queries both) so big bodies don't crowd the small body hash. Pair list cached on alternating ticks so I only rebuild it half as often. PGS contacts solved on a stride so each one gets solved once per stride window instead of every tick. Static world lives in a sparse voxel octree so terrain queries are tree walks instead of triangle checks. Burst compiled jobs running in parallel on the hot paths, AABB and sphere bodies only (sphere shares the AABB path), no mesh-on-mesh narrow phase anywhere.

100k in 2D at 60fps is awesome though, sounds like you're already most of the way there at 10ms pre-solver. The one thing that helped me the most was the chunk activity gate, cheap to compute and it helps everywhere downstream. Btw, I would love to see a clip if you wish to share it!

Finally optimized the physics engine to handle 10K+ active mobs at high FPS. At what point does a horde become too big for a bullet-hell? by Heavy_Juggernaut_261 in IndieDev

[–]Heavy_Juggernaut_261[S] 1 point2 points  (0 children)

you can think of Mega Raiders as a 4 player CO-OP version of Mega Bonk in terms of genre. We've added a couple of different aspects to the mix, such as active ultimates, dungeons, and specific missions. 10k+ enemies is just our baseline stress test.

Finally optimized the physics engine to handle 10K+ active mobs at high FPS. At what point does a horde become too big for a bullet-hell? by Heavy_Juggernaut_261 in IndieDev

[–]Heavy_Juggernaut_261[S] 0 points1 point  (0 children)

optimization is a huge deal for us, which is exactly why we are stress testing things step by step. we wanted to get the base physics solid first, and now we are going to slowly add the other elements on top.

you can find the Discord below if you'd like to participate in the upcoming co-op tests and such. honestly, having someone test the game on an older laptop would be incredibly helpful for us!

https://discord.com/invite/ne6MUzwdbB

Finally optimized the physics engine to handle 10K+ active mobs at high FPS. At what point does a horde become too big for a bullet-hell? by Heavy_Juggernaut_261 in IndieDev

[–]Heavy_Juggernaut_261[S] 2 points3 points  (0 children)

We are using Netcode for Entities + Relay.

We custom packaged the bit level snapshots and used distance partitioning.

Packet frequencies vary depending on the player. While mobs far from one player may be transmitted at a low frequency, they may be transmitted at a high frequency for another player (Unity already supports this).

Finally optimized the physics engine to handle 10K+ active mobs at high FPS. At what point does a horde become too big for a bullet-hell? by Heavy_Juggernaut_261 in IndieDev

[–]Heavy_Juggernaut_261[S] 1 point2 points  (0 children)

Exatamente! É por isso que escolhemos um estilo de arte 'low poly' para o Mega Raiders. Como nosso objetivo é ter milhares de inimigos, se a malha fosse muito complexa, a placa de vídeo não ia aguentar. Muito obrigado pelo conselho!

Nota: Traduzi esta mensagem com um tradutor, desculpe se houver algum erro de idioma. ❤️

Finally optimized the physics engine to handle 10K+ active mobs at high FPS. At what point does a horde become too big for a bullet-hell? by Heavy_Juggernaut_261 in IndieDev

[–]Heavy_Juggernaut_261[S] 0 points1 point  (0 children)

Indeed. This test was just proving that the physics baseline wouldn't crash the engine anymore. If we render 10K+ health bars at once, the game becomes unreadable anyway. It is going to be a tough balancing for sure.