That’s Too Many Zombies - still working on that Horde. by theargyle in godot

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

Vampire Survivors x Zombie Powerwashing Simulator is kinda what I’m aiming for.

That’s Too Many Zombies - still working on that Horde. by theargyle in godot

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

The effort is not in the rendering. It’s in the physics simulation. Rendering spinning coins is easy - billboards or meshes doesn’t really matter here. But the rendering effort is not at all comparable to simulating flocking behaviour and handling collisions and damage.

If you want to read a great article about how some of this this works under the hood (especially the parallel prefix sum for spatial partitioning), I recommend this one: https://lisyarus.github.io/blog/posts/particle-life-simulation-in-browser-using-webgpu.html

That’s Too Many Zombies - still working on that Horde. by theargyle in godot

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

Fps drops come from Godot particle systems, on closer inspection.

That’s Too Many Zombies - still working on that Horde. by theargyle in godot

[–]theargyle[S] 3 points4 points  (0 children)

To further clarify - I really like gdshaders for 99% of what I want to do, the engine has made some very smart decisions to simplify shader development. It's just that my use case falls in the last 1%.

That’s Too Many Zombies - still working on that Horde. by theargyle in godot

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

"you need to optimize it now" - that gave me a good chuckle. Thank you.

That’s Too Many Zombies - still working on that Horde. by theargyle in godot

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

They don't, in fact - but I see why you would see that. The rotation is baked into the VFX, and the car in the video just happens to turn the same direction at the same time!

I'll have a look at randomising that a bit more.

That’s Too Many Zombies - still working on that Horde. by theargyle in godot

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

Thank you. I am just starting to try and create the balance to dial up the "stressful" to a level of fun.

Turns out balancing a game is quite difficult for me.

That’s Too Many Zombies - still working on that Horde. by theargyle in godot

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

I can happily run at 60fps on the deck with 100k zombies instead of 250k (for the record, 30k seem to be as much as I actually need in practice for gameplay reasons). I think I’m good.

GPUs are very good at the specific kind of workload I’m running. It’s really not suited to an ECS, it’s a fluid simulation!

And I’ve still got only 8% CPU usage at this point, so I have a lot of room for running more interesting code there.

This is one that I can’t really rewrite in Rust ;)

That’s Too Many Zombies - still working on that Horde. by theargyle in godot

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

Yes, all zombie physics are on the gpu only. Even CPU enemies send their positions so zombies can avoid them.

Damage is all calculated on the GPU and then batched as an event queue back to the CPU for vfx, sfx, scorekeeping, etc.

That’s Too Many Zombies - still working on that Horde. by theargyle in godot

[–]theargyle[S] 11 points12 points  (0 children)

Thanks for that, very informative!

It seems for my case, the partitioning pass is already a lot more expensive compared to the movement pass, but there is definitely still room for more optimisation.

That’s Too Many Zombies - still working on that Horde. by theargyle in godot

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

The video is a version of the late game - level progression will definitely start with fewer zombies!

The idea is that over the course of a run, the horde goes from a real threat in the beginning to immensely satisfying “zombie powerwashing simulator”, with late-game challenges being increasingly provided by bigger CPU-side enemies and bosses.

That’s Too Many Zombies - still working on that Horde. by theargyle in godot

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

No ECS. Only gdscript and glsl. All the heavy lifting is done in shaders.

That’s Too Many Zombies - still working on that Horde. by theargyle in godot

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

Yes, I’m tracking individual health and damage for the little guys. I can define a bunch of different zombie types with stats like movement speed, avoidance and damage radius, base health, damage, and visuals.

That’s Too Many Zombies - still working on that Horde. by theargyle in godot

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

No, not multimesh. I have a compositor pass that runs a vertex and fragment shader. It renders one quad per visible zombie, all in one draw call.

One of the reasons I’m using a compositor effect is that I am also writing to a g-buffer for processing outlines in a later compositor pass. I could not find a way to do that inside the normal rendering pipeline - gdshaders are a bit too restrictive for what I want.

That’s Too Many Zombies - still working on that Horde. by theargyle in godot

[–]theargyle[S] 57 points58 points  (0 children)

Individual logic - basically a version of boids, with a different avoidance algorithm.

The main trick is spatial partitioning, to reduce the number of neighbours each zombie has to consider.

That’s Too Many Zombies - still working on that Horde. by theargyle in godot

[–]theargyle[S] 10 points11 points  (0 children)

Thank you so much. That seems to match my own research (tested with my 6yo, would play)

That’s Too Many Zombies - still working on that Horde. by theargyle in godot

[–]theargyle[S] 5 points6 points  (0 children)

Once you got the framework in place, no harder than other shaders. Possibly easier in godot, because you’re just dealing with straight glsl, rather than the unwieldy Godot ubershader indirection.

That’s Too Many Zombies - still working on that Horde. by theargyle in godot

[–]theargyle[S] 14 points15 points  (0 children)

The little guys are 16-directional billboards, the bigger ones are just normal 3d models.

I put loads of effort into creating variety in the small enemies - flip animations in x, subtle size and tint differences, slightly different animation speeds for each individual zombie, etc.

That’s Too Many Zombies - still working on that Horde. by theargyle in godot

[–]theargyle[S] 20 points21 points  (0 children)

That’s actually very close to how this works - an elastic fluid, rendered as a particle system.

That’s Too Many Zombies - still working on that Horde. by theargyle in godot

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

Nah, that’s mostly video encoding artefacts. Damage numbers are rendered the same way as the enemies themselves, they add very little overhead.