I never thought it was possible, but yes it is. I’ve tried it and used it in Academia. This is with the help of somebody else’s script of course. It’s not without drawback.
Optimizing multiple moving sprites is I think the hardest one to pull off. Even if they’re already sharing the same material, they still don’t batch consistently. When they do batch, there’s so many of them that the process of dynamic batching will actually take more CPU cycles and bogs down the frame rate. Most of the time, the batched sprites are scattered all around.
While browsing on the internet for ideas on how to optimize, I stumbled upon the great old Unify Community wiki. I haven’t visited the site for a while and I saw that there are a lot of new shared scripts. So I went through each one and found this SpriteManager by Brady Wright (thank you so much!). It’s actually an old script. It claims that it can draw multiple moving objects in one draw call. A bold statement, I thought. I went ahead and tried a simple test. The usage is a bit rough, but I was able to roll with it. To my amazement, the script works. I spent the next two days integrating the gem to our game. Now, all the moving agents in the game are rendered in a single draw call.
I took a good look at the code to understand how it works. It works by maintaining a single mesh for all sprites that were requested from the manager. The manager transforms each vertex in LateUpdate() using the Transform of the owner of each sprite. It then reassigns these transformed vertices to the mesh thus updating the positions of the quads. The engine just renders this one single big mesh. It’s so simple and clever.
An obvious drawback is it doesn’t have culling. Objects that are outside the view of the camera would still be rendered. The transformation of vertices is still computed on those sprites that should have been culled. This isn’t a real issue to us because most players play the game with the camera zoomed out most of the time. We expect players would play this way because one would naturally want to see how their school is doing. Culling will not contribute much.
Eventually of course, we’d probably hit a certain number of sprites where this system starts to bog down. We could always implement culling or try to optimize the code by ourselves when we hit that number. Until then, we’re just letting it be. It’s already good.