Motion Matching research

In this post, I will disect some of the work I’ve been doing the last 2 weeks for a personal project. To put things on perspective I am developing a classic survival horror (PS1 Resident evil / Silent Hill style), with tank controls. The thing is the tank controls seems a bit outdated nowadays and people seems to hate them (I don’t). So as this is a personal project and my plyground, I decided to make some tests with different control schemas. While I was deciding what to do, Epic Games released the Game Animation Sample wich blow my mind with the animation quality and I decided that I wanted that animation quality for my game.
As the character on my game does not need fancy movements I only needed to implement the locomotion movement stuff so I think it would be esay to do (Big mistake). Some of my goals to implement this system in my game where the following:

  • Don’t use Unreal Engine provided animations, implement the system with other animations so I can fully understand how to integrate them.
  • Don’t copy stuff from the unreal engine animation sample, I can obviously check what they do, but I want to implement my own system without copying stuff so I ensure myself I understand what I am doing.
  • Aim to make the game’s animations exceptional. My goal is to develop a system that allows the animations to feel polished, expressive, and high-quality.

With this goals in mind, I began researching where to source the animations I needed. I looked through fab and other sotores, but nothing convinced me. Eventually I discovered this page: motorica.ai

Using this platform, I downloaded all the animations needed, configured with my charcter speed, rotation rate and acceleration so they match my character movement settings (Said this seems easy but this stage took me some time). The page requests you to configure and download the animations one by one (Unless you contact them, and reach some economic agreement).

This is the list of animations I created using this page:

  • Idle
  • Walk Normal starts x 4 (Front, backwards, left, right).
  • Walk Strafe starts x 4 (Front, backwards, left, right).
  • Run starts x 4 (Front, backwards, left, right).
  • Walk Stop x 4 (Front, backwards, left, right).
  • Run Stop forward.
  • Walk to run animation.
  • Walk pivots x 8 (45º Left and right, 90º Left and right, 135º Left and right, 180 Left and right).
  • Run pivots x 8 (45º Left and right, 90º Left and right, 135º Left and right, 180 Left and right).
  • Pivots in place x 8 (45º Left and right, 90º Left and right, 135º Left and right, 180 Left and right).

With all the animations downloaded, I began working on the engine implementation.

Before diving in the engine implementation I needed to do some research on how the «Motion matching system» works. I started looking at the Game Animation Sample but there were too many classes, databases and stuff. Instead, I decided to look for a more approachable tutorial that explained the core concepts so I could better understand the sample project afterward.

The tutorial I found was this «Your First 60 Minutes With Motion Matching». I started following this tutorial, using my own animations and building up all needed classes. When I completed the tutorial, the movement was fine but I needed to address some stuff. At this point I was prepared to look at the Game Animation Sample.

With the info I got at that point, I’m going to make a step to step simple list of things I need to do:

  • Create the pose search schema that will store the pose search parameters to select the animations. This schema determines what the search queries against the animation database.
  • Create the animation databases with the downloaded animations from motorica.
  • Create the pose search normalization set that should store all the databases that can be used on the character (As said on the tutorial).
  • Create a chooser that will select the current database depending on the current animation params.
  • Create an «Animation Blueprint» that has all the parameters needed (Is Walking, Is Idle, Is pivoting, IsStarting…).

I created the pose search schema, I ssigned the skeleton, creted a mirror table so the animations can be mirrored but left the pose search params with the default values.

Then, I created the animation databases (The PSD prefix means Pose Search Database):

  • PSD_Idles (Will store the idle animations)
  • PSD_RunStops (The run to idle break animations)
  • PSD_WalkStops (The walk to idle break animations)
  • PSD_SprintLoops (The sprint loop anims)
  • PSD_WalkLoops (The walk loop anims in all directions)
  • PSD_SprintStart (The idle to sprint animations)
  • PSD_WalkStarts (The idle to walk animations in all directions)
  • PSD_SprintPivots (Sprint turn animations in all directions)
  • PSD_WalkPivots (Walk turn animations in all directions)
  • PSD_TurnInPlace (Idle turn in place animations in all directions)

Before creating the chooser, I needed to set up the Animation Blueprint and determine which variables were required to correctly select the appropriate animation database at any given moment.

I created some enums so I can define different states for different moments:

/*
* Enum states to know if the character is idle or moving
*/
UENUM(BlueprintType)
enum class EREMovementState : uint8
{
	Idle		UMETA(DisplayName = "Idle"),
	Moving		UMETA(DisplayName = "Moving"),
};

/*
* When moving, enum to know if the character is walking or running
*/
UENUM(BlueprintType)
enum class EREMovementMode : uint8
{
	Walking		UMETA(DisplayName = "Walking"),
	Running		UMETA(DisplayName = "Running"),
};

/*
* This enum demines how the character rotates
*/
UENUM(BlueprintType)
enum class ERERotationMode : uint8
{
	OrientToMovement	UMETA(DisplayName = "OrientToMovement"),
	Strafe				UMETA(DisplayName = "Strafe"),
};

/*
* The gait represents, from an input perspective, how 
* the character should move
*/
UENUM(BlueprintType)
enum class EREGaitMode : uint8
{
	Walking		UMETA(DisplayName = "Walking"),
	Running		UMETA(DisplayName = "Running"),
};

I also needed variables with the speed, if the character is starting to move, and if the character should pivot. This information can be defined as variables, and can also be Anim Blueprint methods. To use methods as blueprint varaibles on the animation chooser, you must define them as follows:

//Returns true if the character is moving (Based on trajectory future velocity and acceleration)
UFUNCTION(BlueprintCallable, BlueprintPure, meta = (BlueprintThreadSafe), Category = "AnimMethods|Trajectory")
bool IsMoving();

//Returns true if the character is starting to move (Based on trajectory future velocity and acceleration)
UFUNCTION(BlueprintCallable, BlueprintPure, meta = (BlueprintThreadSafe), Category = "AnimMethods|Trajectory")
bool IsStartingToMove();

//Returns true if the character is pivoting (Based on trajectory turn angle, movement and Rotation mode)
UFUNCTION(BlueprintCallable, BlueprintPure, meta = (BlueprintThreadSafe), Category = "AnimMethods|Trajectory")
bool IsPivoting();

//Returns true if the character should turn in place (Based on root yaw and actor yaw difference)
UFUNCTION(BlueprintCallable, BlueprintPure, meta = (BlueprintThreadSafe), Category = "AnimMethods|Trajectory")
bool ShouldTurnInPlace();

//Returns the trajectory turn angle (Based on trajectory current and future velocity)
UFUNCTION(BlueprintCallable, BlueprintPure, meta = (BlueprintThreadSafe), Category = "AnimMethods|Trajectory")
float Get_TrajectoryTurnAngle();

We need to make them «blueprint pure» and also «Blueprint thread safe».

To update the values that are stored on variables, I created 3 methods that are called on the Tick function, they are also BlueprintNativeEvents, so they can be overriden in blueprint.

//Update trajectory params (Previous , current and future velocities)
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "AnimMethods|Update")
void UpdateTrajectoryVariables();

//Here we update all the state related variables (Movement state, movement mode, rotation mode, etc)
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "AnimMethods|Update")
void UpdateStateVariables();

//Here we update all the other animation related variables (Speed, acceleration, etc)
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "AnimMethods|Update")
void UpdateAnimVariables();

I decided to make a base «Animation blueprint» with all the basic and shared stuff across all characters and enemies, and then inherit from this base class to implement character specific stuff, using c++ or blueprint.

With all the necessary variables and methods defined in the Animation Blueprint, I can proceed to create the chooser and select the appropriate animation database based on that information. I created the chooser by right clicking on the content browser and selecting «Chooser table».

A popup will appear, here I set the Chooser Type to: Generic Chooser

In the Result type we can set «Object Of Type».

In the «Result Class» I selected «Pose seach database» as this will be the asset that will return the chooser.

We also need to create a parameter, this is the class that stores the parameters that we will need to choose between different databases. In this case the parameter is the anim blueprint we created before.

The structure of the chooser will have 2 levels, we can have nested choosers on the row slots. The way this is going to work is by selecting a nested chooser, based on the «Movement State» and «Current Gait»:

Now inside this nested states, we need to create the logic. Let’s start with the Idles:

Here we store the Idles, Stops, and Turn In place databases. Here, the speed allow us to select the stop database based, on character speed, the idle if the speed is <1.0, and finally the turns in place.

The order of the rows inside the chooser is important, as the system will always return the first row that meets all the requirements. For example, if both the Idle and TurnInPlace rows match the current conditions, the chooser will return the first valid entry, in this case, PSD_Idle.

Now, let’s see the next nested chooser:

This is the walk nested chooser. Its configuration is fairly simple, as it relies solely on boolean variables to determine which database should be used.

The Run nested chooser is very similar:

At this point, we have everything required to integrate the animations into the Animation Blueprint. It’s important to remember that the animations should match, as closely as possible, the character’s acceleration, maximum speed, and rotation rate. These parameters are defined in the Character Movement Component inside the Character Blueprint.

While reviewing the Game Animation Sample, I noticed that they set the character’s rotation rate to -1, which forces instant rotation. They then apply the actual rotation rate inside the Animation Blueprint by rotating the root bone using an OffsetRootBone node. I decided to follow this approach as well, since it provides much better alignment between the animation’s rotation and the character’s rotation.

Now let’s have a quick look at the anim blueprint:

So the first node is the motion matching, this nodes makes the animation selection inside the Chooser we configured earlier, after that we have the «Offset root bone» that is responsable to add offsets (Rotation and translation) on the root bone to try to match as closely as possible the animation translation and rotation to avoid foot sliding. Then We have a foot IK node, a pose hierarchy node that collects bone transforms for motion matching (Velocities, speed, etc…). After that we have the Slot and Sync nodes that will be used in the future to sync montages between the main character and the enemies, and finally the control rig node that is the one provided by epic games that manages foot placement and IK stuff.

The motion matching node needs to be configured, as we can see in the image, the node has binded 2 methods on the «On Update» and «On motion matching state updated».

The «On Update» method just updates and stores the actual database based on the chooser:

The «On Motion matching state update» the bound method just stores the current database tag (This information is primarily used for pivot animations, as I was experiencing issues with blending between start and pivot animations. Tracking the current database tag ensures smooth transitions between these states):

Finally if we double click the Motion Matching node, we have a subraph that can have more logic inside:

Here the main nodes are the «orientation warping» and «stride warping».

The Orientation Warping node is used to blend animations to better match the character’s velocity. For example, if we have four walking animations (forward, backward, left, and right), the character may move diagonally. This node, when correctly configured, blends the four animations to programmatically fill the “animation gaps.”

The used configuration can be seen in the image at the left. We need to set the spine bones, and the IK foot bones to ensure proper deformation and alignment during blending.

The «Stride warping» node is used to avoid foot sliding during critical moments, such as start and stop animations or when accelerating and decelerating while switching between walk and run states.

Here we also need to configure some variables related to the pelvis bone and the leg bones.

Now if we configured everything correct, and everything is working as expected, we should test our animations to see if they work:

I am visualizing the character rotation (green arrow), the root rotation (red arrow), and the predicted trajectory positions at different times (green, red, and white spheres).

Achieving this result required extensive research, studying numerous tutorials and videos over the past two weeks. While I am confident that some aspects could be further optimized for even better results, the current setup produces smooth and responsive movement animations, as demonstrated in the video.

After this, my next struggle was on how to implement gun handling with different guns. I was thinking on having all the animations (Idles/walk/run loops, start and stops) duplicated for every weapon, changing the arms pose, but this seemed too complicated and memory unoptimized for me.

The approach I finally ended up using, was to implement an state machine for the weapon animations (I will only need 4 for every weapon right now), shoot, idle, aiming and reload.

To implement this i created a child of the base anim blueprint as this will be a character exclusive feature.

This is the setup I followed to integrate the gun anims on my animgraph. Basically we store the motion matching locomotion anims on a cached pose, we also store the Weapon state anim on another cached pose. With these 2 poses I created a dynamic additive that I store on another cached pose called AdditiveOverlay.

The weapon state machine is the one responsible to select the weapon anim depending on gun params:

Now with the cached additive, and the locomotion cached pose from our motion matching node, I need to blend them.

For the blending, I used two Layered Blend Per Bone nodes. The first node utilizes a blend profile I created called UpperBody, which assigns weights from 0.1 to 1.0 starting at the first spine bone up to the hand bones.

This makes the blending softer than using a blending by a single bone, so the gun animations still conserves partial movement from the locomotion animations, making them less static.

The second layered blend per bone uses a regular Branch Filter approach to keep clavicles static, this makes the hands to be correctly placed on the gun, but not affecting the spine and neck bones movement.

The result of the 2 blends is connected to the Offset Root Bone (Where the motion matching node was previously connected).

The animations in the weapon state machine are now parametrized and update automatically whenever the character switches weapons. Additionally, we interpolate the alpha input of the Layered Blend Per Bone node between 0 and 1 (or 1 to 0) when the character equips or unequips a weapon, ensuring smooth transitions between the weapon and locomotion animations.

This is the result of the weapon animations implementation:

This is the current animation implementation I am using in my game. While I am certain there is room for further improvement, this setup is functional and meets my needs for now.

I hope you enjoyed reading this post. If you have any suggestions or questions, please feel free to leave them in the comments.

Deja un comentario

Descubre más desde Ismael Castellanos Ruiz

Suscríbete ahora para seguir leyendo y obtener acceso al archivo completo.

Seguir leyendo