The first of many posts! Hopefully this will be detailed and useful to all that read.
One obstacle that had me going for awhile was how to determine when an attack animation was fully completed, without using the isPlaying member. Though this would tell me if an attack animation was done, it would not work well if you have more than 1 attack going in a row and wanted to do something special with each attack. Furthermore, it would always return true since another animation (usually walk or idle) would follow the set of attack animations. So this solution wouldn't work, and no other values were all that useful in knowing when a player was attacking, and when he was idling/walking.
On top of the goal of knowing when a player is attacking and when they are done, I wanted to be able to string attacks together in a combo. These go hand-in-hand, since we want combos, and we'll need to know when a combo is done so the state of the character can change.
So the problem is 2-fold: know when attacks are done, no matter how many occur in the set (though we are currently capped at 3 in a combo), and stringing the attacks together visually.
Attacking State Change Problem
I tossed around a random set of solutions, most of which I can't think of right now (if I remember any I'll post them in a comment). The solution I did settle on, and has been working well, is timestamps. Each AnimationState object has a member called length, a floating point value that is how long the animation is in seconds. So if the field returns 1.73, then the animation lasts for 1.73 seconds. When a combo occurs, the length of each attack animation is added to a single variable called attackTime. This is the total amount of time that the player will be in the attack state. So if combo1 last 0.5 seconds, combo2 last 0.25 seconds, and combo3 lasts for 0.75 seconds, the total amount of time the player will be attacking is 1.5 seconds.
When the player first performs an attack, a timestamp is taken and stored in a variable called timeOfAttack. This variable, in conjunction with the value returned by Time.time, is used to determine how much time, in seconds, has passed since the first attack. This is checked each update call. When the time that has passed is greater than or equal to attackTime, then the attack has completed, and various state related values are reset, including attackTime, the attacking state, and the comboNum (more on this variable below).
This has worked very well so far, with no noticeable issues. The states change when I expect them to, and there hasn't been any major performance hits for this approach that I've seen.
Stringing Attacks Together
Every time the player hits the attack button, the function Attack is called. This function updates the value comboNum each time it is called. This can be seen as the number of Attack function calls that has occurred so far. The function checks to see if the value is less than or equal to 3. If so, the value is passed into a switch statement. This switch statement will then add the length of that combo's animation to the attackTime value, and queue up it's animation for playback.
That's pretty much all there is to that one. Any additional logic for specific animations can go into the switch statement, but so far isn't needed. The code for the Attack function can be seen here.
Final Thoughts
I think this gives a pretty solid core for animations, attacks, and the state changes for moving, attacking, and idling. In the simple demo (embedded above) there's basic changes from idle to walking to attacking. A full combo has a smooth transition back to idle, but if you only do 1 or 2 attacks it pops back to idle. Currently thinking of ways to fix that.
One of the final things I'll probably do next for this is separate player input and movement into one class, and the animations all in another class. This way I can simply drag the animation class into any character type and use it seamlessly.
No comments:
Post a Comment