Skip to content

2 ‐ Setting up your first character

Guilherme Sousa edited this page Apr 20, 2025 · 1 revision

This page will go over everything you need to setup a character that uses motion matching. Note that all of this may be subject to change in upcomming releases.

Character Capsule

The character capsule is the main node that controls motion matching. It is resposible for compiling all the data needed to run a motion matching query and select an animation. The first step is to create a new scene of type MMCharacter. image This will be your character scene!

Character Movement

You should also add a node to hold the logic that will control your character. Were are some examples for AI vs Player characters. Tip: by setting target_velocity, the MMCharacter will interpolate its velocity to reach the target, based off its halflife value. For better results, your target velocity should be similar to the velocities of your animations: slower velocities will generate trajectories that will more easily match against slower animations!

Player Movement

extends Node

@export var max_speed = 3.0
@export var jump_speed = 3.0
@export var character: MMCharacter
@export var camera_pivot: Node3D

func _physics_process(delta: float) -> void:
	var stick_input = Input.get_vector("left", "right", "down", "up")
	var stick_input_world = Vector3(-stick_input.x, 0, stick_input.y)
	var desired_velocity = (stick_input_world * max_speed).rotated(Vector3.UP, camera_pivot.rotation.y)
	character.target_velocity = desired_velocity

func _input(event: InputEvent) -> void:
	if event.is_action_pressed("jump") and character.is_on_floor():
		character.velocity += Vector3.UP * jump_speed

AI Movement

extends Node3D

@export var character : MMCharacter
@export var nav_agent : NavigationAgent3D
@export var speed = 3.0

func _unhandled_input(event: InputEvent) -> void:
	if event.is_action_pressed("ui_accept"):
		var random_pos: = Vector3.ZERO
		random_pos.x = randf_range(-5.0, 5.0)
		random_pos.z = randf_range(-5.0, 5.0)
		nav_agent.target_position = random_pos # This position controls player movement
		print(random_pos)

func _physics_process(delta: float) -> void:
	var next_destination = nav_agent.get_next_path_position()
	var delta_pos_next = next_destination - global_position
	 
	var distance_remaining = nav_agent.get_final_position().distance_to(global_position)
	character.target_velocity = delta_pos_next
	if distance_remaining > speed:
		character.target_velocity = delta_pos_next.normalized() * speed
	nav_agent.velocity = character.velocity

At this point, you should have a character capsule, able to move around your level. Now onto animating your character!

Animating your character

Importing Animations

For motion matching to work it requires both a skeleton, and animation containing root motion for it.

Often times, glb, gltf and other pack multiple animations in a single file. Since this extension requires animations to be exported a individual files, this extension also includes an import plugin that allows you to define a directory to export animations to, as individual files. You can set this directory here.

image

Once you have imported your skeleton and its animation data, you can now add it as a child node of your character.

Setting up the Motion Matching Animation Library

The next step is to setup your motion matching library! This is the resource that holds all your libraries, as well as the motion matching features that will be used to select animation. You should first create a MMAnimationLibrary:

image

You can now use the animation player's editor to add both your MMAnimationLibrary to your animation player, and all your animations to your MMAnimationLibrary.

image image

Setting up Motion Matching Features

These features define how animations from you MMAnimationLibrary are selected. Let's start with a MMTrajectory, that is used to select animation based on your character's future trajectory.

  • Past/Future delta time: time used to sample the trajectory
  • Past/Future frames: number of trajectory points used for selection

For motion matching to work well, your character's generated trajectories should be similar to those computed from your features. This might require some tuning, but here are some tips:

  • The trajectory's delta time and point count should be identical to the one used on your feature (this is planned to be automated away soon).

You should also set your sampling rate to something sensible to your dataset size. Lower sampling crates will create more animation points to select from, at the cost of selection performance.

Baking Motion Matching Features

Baking is the process of computing all the features for your animations, so that they can be use at runtime for selection. To bake your animation library, select your character node. The Motion Matching editor should show up, as well as your motion matching library in its drop-down menu.

image

You can press the bake button to compute your features. You can make sure your data was generated correctly by using this editor to visualize the features computed from your animations! Here's a visualization of the trajectory feature.

image

Setting up the Animation tree

You should now add an AnimationPlayer and AnimationTree to your character scene. You can set the AnimationTree to use the AnimationPlayer, and set the AnimationTree on the character node. Your character tree should now look something like this. I've also included a capsule collider, and a RootMotionView for debug purposes.

image

For the purposes of this demo, I will use only once state to define my character's motion. Note that you could very well use two or more states (like "Idle" and "Walk"), as using two gives me more control over what animations can be selected at any given time.

The state we added to the AnimationTree must be a blend tree, and will serve to hold our motion matching node. Here's what that looks like.

image image

On the motion matching node, you should now be able to select the animation library you created previously!

image

At this point, your character is all set to be animated using motion matching!