Dev Blog 7: Animation System
Since the last update I've been busy working on animations and a system to help control how they are managed. Drawing the art, as usual, was slow going. I created the remaining attack animations and then I found myself making some hurt/damaged animations. I play those when the character is hurt by an enemy, which also led me to realize that my current (rudementary) approach for triggering animations was not going to work. I'd need to make sure that I don't allow the player to trigger another animation when attacking or while the player is showing they're hurt. There could potentially be many ways to trigger an animation and as such, I would need a good system to block multiple animations from triggering as well as something that helps maintain state that is closely tied to an animation.
Take for example, when the player is hurt. We want to do a few things like play a hurt animation, bounce the character away from the enemy, make them invincible for a period of time, and also disable the player from moving the character as they are bouncing away. So I designed a system that helps keep these sorts of things under its control.
While the first thing I had to change was that I'd no longer use Unity's built-in animation state machine (mechanim), as it wouldn't provide me an easy way to update variables unless I did some extra work inside each animation with Animation Events. I also felt that I'd probably forget to add them to some animations and it'd lead to future bugs. So all I'm doing in mechanim is defining which animations a character can perform and a few animation parameters, such as the direction the character is facing.
I was inspired by this excellent article 2D Animation Methods in Unity by Joe Strout. Where I did something very similar to his 'Approach 2'.
Inside my new logical animation construct I'm defining what actions to perform when entering the state and what actions to do when exiting the state. This also is done in a relatively generic fashion so that I can tack on new animations as I make them. The next issue I discovered was that Unity's animator does not have any way of notifying the program that an animation has completed. I had hoped to play an animation, wait for it to finish, then adjust other triggers accordingly. Well, that isn't possible at this point in time so it led me to monitor the current animation every frame and then raise my own signal when it changed.
The last issue I encountered was that if you tell the animator to play two animations in a row, on the same frame, it will occasionally not play the one that was last requested. I'd run into this situation because I wanted to insure that an idle animation was triggered after the player was done attacking. If the player timed it just right, you could begin attacking exactly at the same time that you just finished attacking. So the logic would essentially ask to play an idle animation and then play an attack animation during the Update() phase of the frame.
this.animator.Play("Idle"); ... this.animator.Play("Attack");
Well, the attack logic would say, "I'm attacking, so the player can't move until the animation completes". But since Unity would not actually go and play the Attack animation, it would never complete. While scouring the internet I found that others ran into this problem also. The solution was to write the logic so it would only ask to play one animation per frame and also to trigger the Play function call in the mono behavior 'LateUpdate()' function, as it does not suffer the same issues as Update() does when in that phase of the frame. Stupid, but it seems to work. I suppose I could have just left it in the Update() function, but I didn't want to risk accidentally reintroducing this in the future when time will have made me forget about this intricacy of Unity.