Mecanim Animation Events Workaround and Example

May 9th, 2013

Last night I spent a couple of hours trying to find a native solution for animation events in Mecanim in Unity, but all I found was a $35 Asset Store plugin. While I don’t doubt the usefulness of it and the interest of adding visual interface for events, it was much more than I needed (and was willing to pay for it). I couldn’t find any way to directly access animation frames either, so I spent the next few hours making a workaround for it.

If you know a better solution, don’t hesitate to use it and drop me a line. I’m sure it exists, but the doc is so incomplete that I simple couldn’t find it.

Now, what I wanted to do was to play a jump animation, then, during the last animation frame (before the transition to idle), teleport the character root to the new position of the biped. Here’s a video of what the final animation looks like in the Scene and Game views:

As you can see, the game object stays the same until the last frame of the animation, then it gets teleported to the position of the biped on top of the rock thingy. To achieve this, you will need to do a couple of things:

1. Import animations and set them up to work with your biped. For my example, you’ll need a jump animation that moves the root of the biped and an idle animation. You can look this up in billions of Mecanim tutorials on the Internet.

2. Put an empty game object at the position of the pivot point of your character (which should be on the ground between the character’s feet by the way), then parent it to the biped. I’m calling my object BipPivot. We will later use this object as a reference for teleportation.

3. Uncheck Apply Root Motion and check Animate Physics on the Animator component of your character. The former is done because we’re making a custom system to move the root and we don’t want the Animator to mess with it. Moreover, this option often gives strange errors (depending on your import settings). The latter is absolutely necessary, because it animates the avatar in FixedUpdate: we’re going to use this later to get the precise number of FixedUpdates of our animation.

4. Set up your Animator so that you have your Idle animation connected to your Jump animation and then Jump animation connected to your Idle animation.

5. Here’s where things get interesting. You have to select the transition from Jump to Idle and make the transition gap equal to 0% as shown in the image below. This is done because we want the Idle to kick in immediately after teleportation.

6. Now we’re going to measure the length of the Jump animation in FixedUpdates. To do this, you need to call your Jump animation by setting a parameter somewhere. Example: “animator.SetBool(“DoJump”, true);” Then run the following code:
private var frameCounter : int = 0;
//Base is the name of the animation layer, Jump is the name of the animation
//it is unclear from the documentation, but you can't compare the name in string
//it absolutely has to be hash
private var jumpState : int = Animator.StringToHash("Base.Jump");

function FixedUpdate()
{
if (animator.GetCurrentAnimatorStateInfo(0).nameHash == jumpState)
	{
		Debug.Log("I am a counter, the number you're looking for is on the right →");
	}
}

Run the game, run your Jump animation and observe the Console. The number on the right is the number of FixedUpdate “frames” of your Jump animation. In my case, the number is 158. Yours will probably be different. Remember it.

Once again, this is not a general Mecanim tutorial. If you don’t know how to start an animation, consult one of the Mecanim tutorials on the Unity website or on YouTube.

7. Now replace the code that you used to detect the number of frames by the code snippet below. It teleports the character to the position of our BipPivot game object.

private var frameCounter : int = 0;
//Base is the name of the animation layer, Jump is the name of the animation
private var jumpState : int = Animator.StringToHash("Base.Jump");
function FixedUpdate()
{
	//check the name of the current animation state
	if (animator.GetCurrentAnimatorStateInfo(0).nameHash == jumpState)
	{
		//increment frame count
		frameCounter++;

		//check if we have reached the last animation frame
		if (frameCounter >= 158) //because 158 is apparently the last jump animation frame
		{
			Debug.Log("Teleport now");

			//reset the frame counter
			frameCounter = 0;

			//teleport the parent game object to the position of BipPivot
			this.transform.position = this.transform.FindChild("Bip001").transform.FindChild("BipPivot").transform.position;
		}
	}
}

This method can be applied to any kind of animations and events. You can measure the length of the animation in FixedUpdates and then do something on a certain frame. I’m teleporting the character in the end of the animation. You can do something else.

I hope my code snippets will be useful to somebody and I also hope that there’s a better way to do this, because this is a hack, not a solution to the problem.

Coding, Unity3D | Comments | Trackback

  • Mido Basim

    Thanks! the frameCounter idea worked for me. I’m trying to use Mecanim for side scroller game. I used this idea to get jumping animation playing nicely along with running/walking animations in Mecanim.

    • http://sergeymohov.com/ Sergey Mohov

      Really glad to hear that. Good luck with your project!

  • Robert Kapas

    Thank you, this is exactly what i wanted!

    • http://sergeymohov.com/ Sergey Mohov

      That’s great! Good luck to you.

Categories

Find:

Links