How would this be theoretically done?
I believe the main trick is using vertex animation instead of skeletal animation, at least for distance actors. The bone count of a crowd by itself would kill performance. There have been a few livestreams on the Unreal Engine channel where they animate birds/fish/plants with vertex animation, and one where they turn character animations into blueprints, but I’m not having much luck finding them right now.
Bones can be LODded pretty easily. This is not the place you should try to optimize first. Vertex animations aren’t free either. The trick is to LOD everything else as well. Skeletal Mesh actors aren’t that expensive on their own if you disable eveything not needed. I have test scene that simulate 256 skeletal actors using simple AI system. This run without massive performance hit on Mobile. CPU time was around 10ms.
There is great presentation about high level ideas used in Assasin creed. GDC Vault - Massive Crowd on Assassin's Creed Unity: AI Recycling
I didnt checked Assassins Creed yet, but there are so many cheap tricks: like the birds in Zen Garden (Epic’s free project) that are particles and there is also impostor techniques that you can animate a series of them looking you have regular chars being animated when there is none and you have also recorded several angles so the end result is very convincing… once they are at certain LoD the mesh itself appears playing its animations.
We are kinda waiting for Ryan Brucks (Epic’s) to present details for his impostor solution being used for Fortnite. There he is doing with foliage during the landing phase at the game start.
The trick to keeping performance up and asset footprint low would be to instance as much as possible. You could start with 1 NPC and make it function using a logic tree and then to make different actors use full body morphs to reshape the actors at run time. For example a child could be reshaped to an old lady by just activating the morph.
The result is you could have 100’s of NPC’s instanced of a single channel framework and each shape added could be mixed with another. Don’t want to do the math but if you have 12 body shapes and 12 head shapes a little mixing here and there you could have 100’s of different shapes.
If somebody reads this thread and doesn’t want to use instanced meshes, a huge performance bottle neck is the shadow and the collisions for the skeletal meshes.
Here is a link were I develop a new plugin to simulate zombie horde/crowds within Unreal Engine.