Ways to achieve well performing crowds of people (or any moving skeletal character) in Unreal Engine

What tools and procedures can be used for displaying moving crowds in the Unreal Engine?

Abstract

When it comes to rendering crowds of moving skeletal meshes in the real time, there're two serious performance bottlenecks:

  1. Draw calls

    Large number of moving actors (Pawns / Characters) in the scene generating multiple number of draw calls to CPU

    If you think about instancing Skeletal Meshes - something like using Hiearchical Instanced Skeletal Mesh, it actually is not the right direction to go. Firstly, such component does not exist in the unreal engine, and secondly, it does not care about the animations bottleneck.

  2. Animations

    Skeletal meshes provide awesome flexibility for complex rigged animations, but they come with a hude performance cost due to all the bone weighting, post processing, animation updates, etc.

    There are optimization techniques available like LODs and animation sharing, but it can still be difficult or impossible to reach the desired performance level with higher number of characters in visible area.

Unreal Engine offers a bunch of tools that help to design an architecture of controlling and rendering crowds with animations in a scale of hundreds of thousands of members.

To achieve a good performance, there's a need to deal with both the mentioned bottlenecks in mind. The purpose of this page is to summarize options related to rendering large crowds of moving Pawn and Character type of actors with "skeletal" (usually vertex) animations included. There is a description of each way or system, performance overview as well as summarization of advantages and disadvantages of the solution.

Testing environment

All tests takes place is a very simple environment (World), see attached images in each sections. Tests consist of moving each person individually within the the space based on predefined trajectory in DataTable. There's a DataTable with a trajectory for each Person. The person itself is a BP derived type of C++ Character class.

All other properties are variable - always specified in the description of a specific procedure.

Custom solution using standard Unreal tools and Skeletal Animations

By using a standard technique of 1 world actor = 1 person, there's generated multiple draw calls from each such person (transform, mesh, animation...). One of the top priorities is to reduce this number of draw calls. There are a bunch of options to achieve that:

To reduce draw calls, there's possible to write a component that would work in the similar way like Hierarhical Instanced Static mesh (HISM), but for skeletal meshes.

To optimize animations, there's possible to apply animation sharing.

These two mentioned point are must do. Beside them, there is a bunch of other optimization techniques to use (animations enabling, tick farme adjustments, billboards technique and so).

Usually, if there is not necessary to use clear skeleton animations, it's better to look at the solutions described below. They are mostly based on Vertex animations and ready-to-use instancing technique.

Hierarhical Instanced Static Mesh + Vertex Animations

This solution lies on converting Skeletal mashes to Static meshes with AnimToTexture plugin and use Hiearchical Instanced Static mesh (HISM) component. Hiearchical because it supports LODs settings.

Mass Framework

Mass is Unreal's in-house ECS (Entity Component System archetype-based) framework. The tool is technically based on Sequencer for gameplay code. It's created by the AI team at Epic Games to facilitate massive crowd simulations. It was featured in the Matrix Awakens demo Epic released in 2021, while since that time it grown to include many other features.