Bart Kevelham ยท Engineering Immersive Realities


Building Avatars in Code with Unity's AvatarBuilder

Why I made this

Many years ago now, I was trying to figure out how to have a character in my Unity applications which could change dimensions. For one the idea was to support an avatar customization option in educational VR experiences we were working on, but another use-case was the generation of synthetic diverse training datasets for the markerless motion capture research we were conducting at Artanim at the time.

Unity can build a humanoid Avatar for you while your game is running, but its documentation barely explains how. Even today the documentation, although recently improved, does not do a great job of explaining how to use the API. Those who tried, as I did, inevitably ran into issues with the system, particularly so when trying to modify an existing Avatar asset.

The sad thing is that once you figure out how things work and what behavior to account for, it really isn't all that difficult. The video below shows 36 identical avatars (i.e. using the same base model and skeleton) customized to look completely different.

To document my efforts and provide a helping hand to the community, I wrote a very small Unity project that does one thing: it builds an Avatar in code and gives it to an Animator. You can find it on GitHub: bkevelham/unity-avatar-generation. Since then it has consistently collected stars, forks and kind comments.

I wrote the below mostly in response to a question I received a while ago. And rather than let it die in my outbox, I thought I'd put it up here. This article explains in a bit more detail what the project does, how the code works, how to use it with your own characters, and what to do next if you want characters with different body sizes.

What is an Avatar, and why build one in code?

In Unity, an Avatar is an asset which tells the Mecanim animation system how your character's skeleton is put together. It answers two questions:

  1. Which object is which body part? For example: the object called mixamorig:LeftForeArm is the left lower arm.
  2. What does the character look like in its resting pose? This is the T-Pose you will see when configuring the Avatar using the editor, or the one it complains about when your character does not stand in a T-Pose.

With this information, Unity's Mecanim can play the same "humanoid" animation on many different characters. A walk animation made for one character can then work on another character with different bone names and different proportions. This is called retargeting.

Normally you never think about this. You import a model, set its Rig to Humanoid in the import settings, and Unity creates the Avatar for you. That works well for models you have when you build your game. It does not help when:

Luckily Unity exposes the Avatar creation functionality through AvatarBuilder.BuildHumanAvatar. You give it your character and a description of the skeleton, and it gives you back a new Avatar.

What is in the project

The project is deliberately tiny and doesn't try to do anything smart on purpose. It has one scene and three scripts.

FileWhat it does
Scenes/SampleSceneContains the standard Mixamo character in a T-Pose. Its Animator has an Animator Controller, but the Avatar field is empty on purpose.
Scripts/HumanoidAvatarBuilder.csA component on the character. When you press Play, it builds the Avatar and gives it to the Animator.
Scripts/AvatarUtils.csDoes the real work. It holds the list of bone names and creates the HumanDescription that Unity needs.
Scripts/Editor/HumanoidAvatarBuilderEditor.csShows a help text in the Inspector that explains the setup. It does not change any behaviour.

At the time I made it with Unity 2020.1.9f1. The API has not changed much since then, so it should work in older and newer versions too.

When you open the scene and press Play, the character starts animating. Nothing was set up by hand in the Avatar field: the Avatar you see at runtime was made by the code a moment earlier.

How the code works

Building an Avatar takes four steps. Each step is small.

  1. Describe the skeleton: every object in the character, with its position, rotation and scale in the T-Pose.
  2. Describe the human: which of those objects are the hips, the spine, the left upper arm, and so on.
  3. Put both in a HumanDescription, together with a few settings.
  4. Call AvatarBuilder.BuildHumanAvatar and give the result to the Animator.

Step 1: the skeleton (SkeletonBone)

A SkeletonBone stores one object's name and its local position, rotation and scale. Together, all the SkeletonBone entries describe the resting T-pose of the character. The project simply walks through every child of the character and copies its current values.

private static SkeletonBone[] CreateSkeleton(GameObject avatarRoot)
{
    List<SkeletonBone> skeleton = new List<SkeletonBone>();

    Transform[] avatarTransforms = avatarRoot.GetComponentsInChildren<Transform>();
    foreach (Transform avatarTransform in avatarTransforms)
    {
        SkeletonBone bone = new SkeletonBone()
        {
            name = avatarTransform.name,
            position = avatarTransform.localPosition,
            rotation = avatarTransform.localRotation,
            scale = avatarTransform.localScale
        };
        skeleton.Add(bone);
    }
    return skeleton.ToArray();
}

Note that this only works because the character in the scene is already standing in a T-Pose. If your character has another pose, you need to get the T-Pose values from somewhere else and put those in the SkeletonBone entries instead.

Step 2: the human (HumanBone)

A HumanBone connects one of your object names to one of Unity's body part names. Unity has a fixed list of body part names, such as Hips, Spine, LeftUpperArm and Left Index Proximal.

In the project, this connection is a simple dictionary in AvatarUtils.cs. On the left is the Mixamo name, on the right is Unity's name:

public static Dictionary<string, string> HumanSkeletonNames = new Dictionary<string, string>()
{
    {"mixamorig:Hips", "Hips" },
    {"mixamorig:Spine", "Spine" },
    {"mixamorig:Spine1", "Chest"},
    {"mixamorig:Spine2", "UpperChest" },
    {"mixamorig:LeftArm", "LeftUpperArm" },
    {"mixamorig:LeftForeArm", "LeftLowerArm" },
    // ... and so on for every bone
};

The code then walks through the character again. For every object whose name is in the dictionary, it makes a HumanBone:

if (HumanSkeletonNames.TryGetValue(avatarTransform.name, out string humanName))
{
    HumanBone bone = new HumanBone
    {
        boneName = avatarTransform.name,   // your name
        humanName = humanName,             // Unity's name
        limit = new HumanLimit()
    };
    bone.limit.useDefaultValues = true;
    human.Add(bone);
}

Objects that are not in the dictionary (such as custom non-humanoid bones) are still in the skeleton from step 1. They just do not get a body part name.

Step 3: the HumanDescription

The HumanDescription holds both arrays and some settings for how much arms can stretch with respect to an IK target or how roll rotations are divided between arm and leg joints. The values below are the same defaults Unity uses when it imports a humanoid model:

HumanDescription description = new HumanDescription()
{
    armStretch = 0.05f,
    legStretch = 0.05f,
    feetSpacing = 0f,
    hasTranslationDoF = false,
    upperArmTwist = 0.5f,
    lowerArmTwist = 0.5f,
    upperLegTwist = 0.5f,
    lowerLegTwist = 0.5f,
    skeleton = CreateSkeleton(avatarRoot),
    human = CreateHuman(avatarRoot),
};

Step 4: build the Avatar

The last step lives in HumanoidAvatarBuilder.cs. It runs on Awake, so the Avatar exists before the Animator starts playing:

public void Awake()
{
    _Animator = GetComponent<Animator>();
    HumanDescription description = AvatarUtils.CreateHumanDescription(gameObject);
    Avatar avatar = AvatarBuilder.BuildHumanAvatar(gameObject, description);
    avatar.name = gameObject.name;
    _Animator.avatar = avatar;
}

That is the whole thing. From here the Animator Controller plays its humanoid animations on the character, exactly as if the Avatar had come from the model import.

How to use it

Try the sample

  1. Clone or download the repository from GitHub.
  2. Open the folder in Unity Hub. Any recent Unity version should be fine.
  3. Open Assets/Scenes/SampleScene.
  4. Select the character. In the Inspector, see that the Animator's Avatar field is empty.
  5. Press Play. The character animates, and the Avatar field now shows the Avatar made by the code.

Use it with your own character

  1. Put your character in a T-Pose. The code reads the current pose as the resting pose. If the character is not in a T-Pose when the code runs, the animations will look wrong.
  2. Add an Animator with an Animator Controller that uses humanoid animations. Leave the Avatar field empty.
  3. Add the HumanoidAvatarBuilder component to the same GameObject as the Animator.
  4. Update the name dictionary in AvatarUtils.cs. If you're not using a model with the Mixamo rig, just replace the Mixamo names on the left with the names of the bones in your own model. Keep Unity's names on the right.
  5. Press Play and check the Console for warnings or errors.

Unity needs at least the required bones to make a humanoid Avatar: hips, spine, head, upper and lower arms, hands, upper and lower legs, and feet. Fingers, toes, neck, shoulders, chest and upper chest are optional. If one of the required bones is missing from your dictionary, the Avatar will not be valid.

A quick way to check the result is to look at two properties after building:

if (!avatar.isValid || !avatar.isHuman)
{
    Debug.LogError("The Avatar could not be built. Check your bone names.");
}

The dictionary in the project is hardcoded on purpose. It makes it very easy to see which object is which body part. In a real project you may want to build this list in code instead, for example by reading it from a file, or by matching common naming patterns.

Common problems

"Invalid AABB" errors and NaN positions

This is the problem I see reported most often with Unity versions before 2022.1, and which has reportedly been fixed since then. When an animation plays on the new Avatar, the Console fills with errors like these:

Invalid AABB aabb
Invalid AABB rkBox
IsFinite(outDistanceForSort)
IsFinite(outDistanceAlongView)

You may also see NaN ("not a number") in the positions of some objects in your character.

The problem seemed to be caused by incorrect string references in private fields, which is hard to correct for. So if you cannot upgrade to a more recent version, just build your Avatar from scratch as outlined.

Other things to check

What's next: characters with different body sizes

Most people who ask me about this API want the same thing: one character model, but with longer legs, shorter arms, or a taller body. The project does not show this, but it is a small step from what it already does.

The idea

Remember that the SkeletonBone array stores the T-Pose of your character. So if you change the T-Pose before you build the Avatar, Unity will treat that changed pose as the normal shape of the character. All humanoid animations will then be played on the new proportions.

The order is important:

  1. Put the character in its original T-Pose.
  2. Change the bone lengths.
  3. If necessary, fix the height of the hips so the feet touch the ground! The "ground" here just means "y==0".
  4. Build the HumanDescription and the Avatar, as in the project.
  5. Give the Avatar to the Animator, and call Rebind() if the Animator was already running.

Changing a bone's length

A bone's length is the distance from one joint to the next. In Unity, that distance is simply the localPosition of the next joint (the child). So to make the left thigh 10% longer, you make the knee's local position 10% larger:

// Make the bone that ends at 'childJoint' longer or shorter.
// factor 1.1 = 10% longer, factor 0.9 = 10% shorter.
static void ScaleBoneLength(Transform childJoint, float factor)
{
    childJoint.localPosition *= factor;
}

Because the mesh is skinned to the bones, the mesh stretches with them. You do not need to change the mesh yourself. Keep changes within reason however. Depending on the exact model, there is only so much stretching or compression you can get away with.

Use localPosition for this, not localScale. Scaling a joint also scales everything below it, including the thickness of the mesh, and it makes the Avatar harder to get right.

Keep the feet on the ground

This is the step people forget. If you make the legs longer but leave the hips where they were, the feet will now be below the floor. If you make them shorter, the feet will float. And in case you have foot-IK enabled, Mecanim will make up for legs that are too long by bending the knees.

So after you change the leg bones, move the hips up or down by the same amount the feet moved.

After this, build the Avatar exactly as the project does. A nice side effect: Unity uses the hip height to scale humanoid motion, so a character with longer legs will also take longer steps.

Wrapping up

The full project is available on GitHub under the MIT license, so feel free to use it in your own work: github.com/bkevelham/unity-avatar-generation. If you build something with it, or find a problem, I would love to hear about it.