Mesh Animator Documentation

Overview

Welcome to the Mesh Animator documentation page. Mesh Animator is a toolset for converting and fully animating characters, animated assets, or any other kind of skinned mesh.

Below you'll find resources on how to use Mesh Animator and all it's features. To report any bugs or for questions not covered in the documentation, please e-mail jschieck@gmail.com.

Topics

Getting Started

Setting up an animated character or mesh is very straightforward and can be done in a single step. After importing the .unitypackage, open the Mesh Animator creation window by selecting Assets/Create/Mesh Animator...

The following window will appear.

Bake Options:

Generate Snapshots

Once the settings are correct, click Generate Snapshots. This will bake the animations out into MeshAnimation assets.

**NOTE** - If your skinned mesh's pivot is not facing Y up, it's rotation may appear off until playback begins. This is because all weight information is lost during baking.

Generated Assets

Once baking is complete, the following assets will be created.

Each animation will get a MeshAnimation asset and a MeshAnimator prefab will be created. Rebaking the same asset will update existing MeshAnimation files and create a new prefab, so it's good practice not to use the generated prefab as your main asset, but rather a child of a top level prefab.

MeshAnimation

The Mesh Animation is the asset that stores the animation info.

Mesh Animator

The Mesh Animator is the main script that animates and swaps frames during gameplay. It functions similarly to the legacy Animation component.

Using Mesh Animator

Mesh Animator functions much like the legacy Animation component. In it's simplest form, animations are played by calling the Play() method.

public MeshAnimator meshAnimator;
void Start()
{
    meshAnimator.Play("idle");
}

The Mesh Animator has a few callbacks that can be used.

public MeshAnimator meshAnimator;
void Start()
{
    // called when an animation completes
    meshAnimator.OnAnimationFinished += (anim) =>
    { 
        Debug.Log(anim + " finished."); 
    };
    // called when a new frame of animation is displayed
    meshAnimator.OnFrameUpdated += () => 
    { 
        Debug.Log("Animation frame changed.");
    };
    // called when a MeshAnimator enters or leaves the screen
    meshAnimator.OnVisibilityChanged += (isVisible) =>
    {
        Debug.Log(isVisible);
    };
}

Controlling Mesh Animator with Mecanim

With the convience of Mecanim's state machines, I've included an example MecanimMeshAnimatorController.cs script. This script looks at the current state of a Mecanim animator, and plays the appropriate animation on the Mesh Animator. State names MUST match the name of the MeshAnimation and blend trees are not supported.

Example State Machine

Since running an animator with it's own controller when it's not needed is excessive and hurts performance, I've also included an example state machine that achieves the same as the Mecanim controller but much more efficiently.

using UnityEngine;
using System.Collections;

public class AnimatorStateMachine : MonoBehaviour
{
    public MeshAnimator meshAnimator;
    public bool crossFade = false;
    void Start()
    {
        meshAnimator.Play();
        meshAnimator.OnAnimationFinished += OnAnimationFinished;
    }
    void OnAnimationFinished(string anim)
    {
        string newAnim = string.Empty;
        switch (anim)
        {
            case "idle":
                newAnim = "run_forward";
                break;
            case "run_forward":
                newAnim = "run_backward";
                break;
            case "run_backward":
                newAnim = "run_left";
                break;
            case "run_left":
                newAnim = "run_right";
                break;
            case "run_right":
                newAnim = "idle";
                break;
        }
        if (crossFade)
            meshAnimator.Crossfade(newAnim);
        else
            meshAnimator.Play(newAnim);
    }
}

Memory Implications

With Mesh Animator, depending on the number of vertices, and number of frames in your animation, it is going to take up more memory than a traditional SkinnedMeshRenderer and Animator. Where you save is on the CPU. Finding a good balance between the two is important. If a character has 5k vertices and 20 animations each a few seconds long, it's not practical to use Mesh Animator.

The best use of Mesh Animator is with low-poly models with short animations. Each frame of animation is generated and copied from the base, this is why so much memory is needed for larger meshes and longer animations.

A 4k triangle character (like in the example) takes up 500KB of memory. So to display a 1 second idle animation baked at 30fps, you would need approximately 15MB of memory. Memory usage is the same whether you are displaying 1 character, or 1000 characters. A character that has only 400 triangles takes 32KB of memory per mesh. So the same length animation would only take 960KB of memory. You can use this information to plan accordingly in your projects.

Crossfading animations requires a temporary mesh to be created for the interpolation and adds a little CPU overhead for lerping the vertex positions. This overhead grows larger the more vertices your mesh has. The temporary meshes are pooled automatically so mesh animators sharing the same base mesh can use it. Crossfading a mesh requires at least 1 extra copy of the base mesh.

Mesh Animator Code Reference

All Mesh Animator scripts reside in the FSG.MeshAnimator namespace.

Implement as follows:

   using FSG.MeshAnimator;

Variables

    // The mesh that will be animated
    public Mesh baseMesh;
	
    // The default animation that will be played if playAutomatically = true
    public MeshAnimation defaultAnimation;
	
    // Array of animations on the animator
    public MeshAnimation[] animations;
	
    // Playback speed of the animator
    public float speed;
	
    // Continue playing animation when the animator is off screen
    public bool updateWhenOffscreen;
	
    // Should animation begin playing when the animator is enabled
    public bool playAutomatically;
	
    // Reset the animator OnEnable and play the default animation
    public bool resetOnEnable;
	
    // The target object to recieve AnimationEvents
    public GameObject eventReciever;
	
    // Get the current animation playing
    public MeshAnimation currentAnimation { get; }
	
    // The FPS the animations were baked at
    public int FPS;
	
    // Callback for when an animation has finished playing
    public Action OnAnimationFinished;
	
    // Callback for when the mesh has changed frames
    public Action OnFrameUpdated;
	
    // Callback for when the object has entered or left the viewable screen
    public Action OnVisibilityChanged;

Public Methods

    // Crossfade an animation by index
    public void Crossfade(int index, float speed = 0.1f);
	meshAnimator.Crossfade(0, 0.25f);
	
    // Crossfade an animation by name
    public void Crossfade(string anim, float speed = 0.1f);
	meshAnimator.Crossfade("idle", 0.25f);
	
    // Play the default animation, or resume playing a paused animator
    public void Play();
	meshAnimator.Play();
	
    // Play an animation by name
    public void Play(string anim);
	meshAnimator.Play("idle");	
	
    // Play an animation by index
    public void Play(int index);
	meshAnimator.Play(0);	
	
    // Play a random animation
    public void PlayRandom(params string[] anim);
	meshAnimator.Play("idle", "idle_bored", "dance", "sit");	
	
    // Play an animation after the previous one has finished
    public void PlayQueued(string anim);
	meshAnimator.Play("wave");
	meshAnimator.PlayQueued("idle");
	
    // Pause an animator, disabling the component also has the same effect
    public void Pause();
	meshAnimator.Pause();
	
    // Restart the current animation from the beginning
    public void RestartAnim();
	meshAnimator.RestartAnim();
	
    // Get the MeshAnimation by name
    public MeshAnimation GetClip(string clipname);
	MeshAnimation clip = meshAnimator.GetClip("idle");