How to optimize modular meshes?

I’m making a zombie wave shooter designed for Quest 2 devices and I’m trying to get a 10 zombie horde to run smoothly.

The zombies meshes are 2k vertices but Unity says it’s 8k vertices in play mode for some reason (no lights or shadows enabled and no skybox or camera), but from what I’ve heard it’s because there’s 4 different materials (which are all the same material just applied to different objects).

The problem lies within the draw calls that these zombies create. Because the zombie is modular, it has a lot of different objects causing a lot of draw calls per frame. I’m aiming for 70 draw calls maximum but a horde of 10 zombies plain gave me 800 draw calls.

I created an LOD system to help with this and also turned a lot of stuff in the zombie static but I still get 15 draw calls for a zombie when it’s far away, 26 when up close. With these optimizations I managed to get a horde of 10 zombies with 96 draw calls and 88k verts in the scene, but 96 draw calls is way too much and I’d want it to be more like 50-ish.

I also tried combining the different parts of it in blender which reduced it to 10 draw calls per zombie but also broke the rig and I’m hoping there’s an alternative.

There’s only one material for the zombie and it’s using the universal lit shader. Messing with the material didn’t change anything performance-wise. Last resort for me is to drop the modular zombie system I have and use something more performant, which might be necessary.

TLDR; How to get rid of draw calls on a mesh with lots of different objects?

For static meshes you’d normally “combine” them. There are plenty of mesh combiners on the Store. I would recommend looking for one that supports mesh combine of animated characters - although I’m not confident that there is such a tool due to the animation rig. It’s possible that you’ll have to do this manually and figure out why the rig broke when you did that.

Unfortunately this issue is common with “modular characters”. So much so that publishers provide a single prefab with all variations of skin, body parts, armor, equipment, details in the same prefab. This then creates excessively large instances unless you add scripts to remove any unused parts from the instance once you’re satisfied, and it still doesn’t solve the draw call issue.

Depending on render pipeline (URP?) you may have some options for dynamic batching, specifically with Unity 6 providing the GPU resident drawer. But here also I believe that this only works with static geometry.

1 Like

@CodeSmile There is a way to combine skinned meshes, though it can be a bit tricky. It’s totally worth it though if one absolutely must stick to using the built-in skinned mesh renderer and animation system.

The one I use can be found here GitHub - joshcamas/skinned-mesh-combiner: A Skinned Mesh Combiner that doesn't require actual skinned meshes as inputs. The author suggests using the newer version they posted however that one does not currently support multiple materials. If you only have one material per character though, the newer version is a lot faster and generates way less garbage.

I’m not sure how well the Quest works with the newer render pipelines but if you can make the switch it might be worth it. The SRP batcher can make multiple materials and draw calls a complete non-issue (at least on PC, I’m not sure about mobile and VR). It might be worth trying it out.

Another thing to consider might be to do away with skinned meshes and animators altogether. Again, I don’t know how well the Quest 2 will support this but for performance critical games with many many animated characters the built-in Unity animation system and skinned mesh renderer is effectively a thing of the past. When scalability counts, animation textures are absolutely the way to go. That being said it probably isn’t worth that much effort just for a dozen or so characters. Usually, you don’t need to look into that kind of thing unless you are trying to get thousands of characters animated on-screen.

2 Likes