Sunday, December 6, 2015

HT: Adding lean left/right to the mecanim animation

It took a while to figure this out. To do this, I needed to create new animations, a steer left, and steer right animation, and play them when the player is turning left or right.

Technically, they weren't animations. They were just single frame poses.
Preparing the Mecanim state machine for this is easy: the move animation state just needs to be converted to a blend tree, then I'd add the steer left and right animations to it:

I made a new parameter called "Steer". It's a float. The "moving straight forward" animation is set to use 0, the "lean left" is in -1, and the "lean right" is in +1.

The preview shows how it will end up looking like:

Now, how to properly play those in runtime? What I want to do is detect when the player is rotating the thumbsticks, and use that to play the lean left and lean right animations correspondingly.

This same thumbstick is the one used for movement. So it's when I'm rotating the thumbstick I should transition from lean left to right and vice-versa.

Turns out it's not so easy.

I'm using CharacterController which doesn't calcualte angular velocity. I could've just used that to detect when the character is rotating (equivalent to when the player is rotating the thumbstick).

My first thought was to use dot product on the character's current forward direction, and the direction where he needs to go to. You see, I make the character smoothly rotate towards where the thumbsticks are pointing. When you want to do a smooth rotation, you'd need to store where they currently are facing, and where they need to face to, and slowly interpolate to where they need to face to. So I thought of using those existing variables already.

The red dot here is where the thumbstick is pointing to, and the yellow dot is where the character is currently facing.

The dot product tells me how far apart the two direction values are. But it doesn't tell me if the direction where the character should go to is to their left or to their right. That's important since I need to know if I should play the lean left or lean right animation.

This is the video that taught me dot products:

So I needed an additional way to determine as to what direction the thumbsticks are rotating to. I thought of deriving the angles (in degrees) that the direction values are creating. Then subtract the two to tell if we're rotating to the right or to the left.

It almost worked well.

I noticed "bumps" when I rotate my character to the left.

I realized it was because angle values I calculate always get clamped to 360 degrees. When the target angle is over the 360 degree mark but the current angle isn't over that yet, the resulting value is too big, causing the "bump".

I recorded the angle values to a graph, so I could see what exact values I'm getting.

I figured the only way I could get around this was to get the proper angle value when it crosses over the 360 degrees mark. With a little bit of trial and error, I found the code that best suited the situation. It looks like this:

if (_angleDelta > 270) _angleDelta = -(360 -  _angleDelta);
if (_angleDelta < -270) _angleDelta = 360 + _angleDelta;

The only thing left was that I still noticed spikes in the graph of my "Steer" getting function. Depending on how bad your framerate is, it made the whole animation look like it was stuttering. I guess there was noise in the input data.

This graph looked a lot worse before.

So I just added a sort of average filter on the last 4 values recorded in the graph and use that instead:

It all seems rather straightforward now that I've put all this in a blog post, but before this, I forgot what "dot product" is and what values it returns, I didn't know what code to use to clamp angle values, and I didn't know what an "average filter" was. There was lots of looking through the Unity manual, and I wasn't even sure if this "Steer" factor idea was the right way to go about it.

It's all exploratory when solving a problem like this and trying out clever solutions.

I was a little against posting a blog post about this because it did take up the entire day, making all those screenshots and videos, and it's making me feel that people would think I'm obsessing over the little insignificant details. At least the time it took to make and fix the whole steer feature took only about a day of work.

1 comment:

  1. I am extremely impressed along with your writing abilities and also with the format in your blog. Anyway stay up to the excellent high quality writing, it's rare to find a nice weblog like this one these days.


Made me post. 0/10.