Search Unity

Animator state machine for top-down 2d games with non-rotational sprite animation

Discussion in '2D' started by stuckwiththisname, Nov 19, 2013.

  1. stuckwiththisname

    stuckwiththisname

    Joined:
    Nov 19, 2013
    Posts:
    9
    Hello. I'm testing out the new Unity 2d tools and I've run into a design issue when creating an animation state machine for the main player.

    The object I'm animating (the main player) is rendered using sprites and there are a different set of images for each action / direction pair (right is the same as left, but flipped, as in the official Unity sample 2d project). Similar to a classic Zelda or Final Fantasy game, when the player faces a different direction, a new image is shown; the sprite isn't rotated.

    If I were using a 3d model, the animation would be done via IK or bone transformations or something similar and the player's facing would be illustrated via the model's rotation. However, since I need to switch to a different set of frames (from the spritesheet) when the player is facing a different direction, in order to change the Sprite via an Animation, I think that I need to have a separate state for each action / direction pair. Is there a better solution?

    I've tried implementing this state machine in a few different ways. The trouble comes from the directions: left-attack isn't the same state as up-attack. I have 3 actions right now (idle, walk, attack) and 3 directions (up, left, down). Since more actions are planned, I'd like to avoid creating all possible permutations of transitions. I could create walk-up -> attack-up, and walk-left -> attack-left and so on, but would roughly triple the translations (compared to a 3d model). I'd like some way to configure how the player moves from idle -> attack, independent of the direction, if at all possible.

    Most recently, I've tried using sub-state machines. (I'd embed images here, Chrome is fubar'd on this machine.)

    Top level states: http://i.imgur.com/wAMu5Fh.png
    Direction handling in sub-state: http://i.imgur.com/E5d9QLv.png

    I have created a sub-state machine for attack, which has states for attack-left, attack-up and attack-right. There is also a state named "switch": this state is used to determine which of the three attack states should be entered (based on the direction parameter). I have a similar pattern for idle and walk. Anytime the player needs to enter the walk state, which is indicated by an isWalking parameter, a transition is created to walk.switch, which will determine which walk-state should be entered. I did this to avoid distributing the transition conditions that consider the player's heading.

    This was working well, but I think the switch state is being rendered for one frame. The transition states from the switch state are met when the switch state is entered. For example, the walk.switch state is entered from idle when isWalking is true. walk.switch then has a condition to move to walk.down when the direction parameter = down and isWalking is true. When the state machine enters walk.switch, it could immediately move to walk.down, but I think it's delaying on switch for a frame (which shows the default sprite for the player).

    I've tried a few other failed designs, and I could go on an on about them but I don't think it would necessarily help anyone. I'd be grateful to hear any thoughts or recommendations on how to proceed.
     
  2. Adman

    Adman

    Joined:
    Jun 1, 2013
    Posts:
    12
    I'm not sure I wholly understand, but let me tell you what I've done.

    I have a character sprite sheet with walk animations in each of the 8 cardinal directions (up, down, left, right, and diagonals). I created animation clips for each of these walk directions, and all 8 are included in my animation controller state machine.

    I put the default "Any State" state in the middle of my state machine. Then I put the "WalkUp" state above it, and the "WalkDown" state below it, and "WalkLeft" to the left and "WalkRight" to the right, and all the diagonals in the right spots. This isn't necessary, but made it look nice. The I did a transition from "Any State" to each of my walk states. So I've got spokes radiating out from Any State to each walk state.

    I then added 8 Triggers, called Up, Down, Left, Right, etc. I attach those triggers to my transitions appropriately (the Up trigger goes on the transition between Any State and Walk Up).

    In my code, I determine which direction the guy is walking, and set the proper trigger in my animation controller. I only want to do this when the direction changes, so there's some logic in there to account for that (otherwise I'd constantly be setting and resetting the same animation).

    Does that help?
     
  3. stuckwiththisname

    stuckwiththisname

    Joined:
    Nov 19, 2013
    Posts:
    9
    Hey Adman. Thanks for your response!

    I understand your design; I had a similar one at one point. What would you do if you added support for attacking? Would your walk-left have a transition to attack-left, walk-right would have one to attack-right and so on? Extending the spokes of your wheel outwards by another layer, so to say?

    I think I'm going to have to avoid the Any State for most actions (and doing so will unfortunately create the need for many more transitions). Eventually I will have a Swim action, and it wouldn't make sense for the player to be able to enter the Walk state from Swim (if he's in the middle of the pond). ...but then again, that could be limited with a CanLeaveWater parameter and condition.

    This tool set is flexible enough for me to make a real mess with it! =)
     
  4. keely

    keely

    Joined:
    Sep 9, 2010
    Posts:
    967
    $controller.PNG
    $idle_blendtree.PNG
    $walk_blendtree.PNG

    Sorry I didn't have time to read your full post, but it seems to me I understand where you are going.

    One way to go about this is to have 2D blendtree for each movement (example: idle, walk, run, ...)

    The screenshots show a setup where we have 8 directions and idle + walk:
     
    flyQuixote, nunesbarbosa and Salazar like this.
  5. Adman

    Adman

    Joined:
    Jun 1, 2013
    Posts:
    12
    Hmm. Time to start learning about blend trees, I guess. Thanks!
     
  6. tiggus

    tiggus

    Joined:
    Sep 2, 2010
    Posts:
    1,240
    Thanks, that is exact same question I had and always good to know the Unity recommended method :)

    EDIT: Although now that I'm digging into it I am stumped as to how you do something like move and attack at the same time. Lets say you are swinging an axe from top-down view while moving north. I would think you need to swap out the sprite texture of the axe(and probably arms) each keyframe for an attack animation, is there an easy way to accomplish that with animation curves?
     
    Last edited: Nov 20, 2013
  7. keely

    keely

    Joined:
    Sep 9, 2010
    Posts:
    967
    You can animate which sprite asset is showing at which time with animation system. Make a curve for the SpriteRenderer.sprite property. Just see any of the tutorials out there how its done.
     
  8. tiggus

    tiggus

    Joined:
    Sep 2, 2010
    Posts:
    1,240
    Got it working, thanks!
     
  9. Adman

    Adman

    Joined:
    Jun 1, 2013
    Posts:
    12
    Can you provide some details on what you did? Screenshots or sample project?
     
  10. Adman

    Adman

    Joined:
    Jun 1, 2013
    Posts:
    12
    Actually, I might totally be in the wrong thread here...

    I'm using spritesheets with pre-drawn frames of animation. I'm not sure if using Blend Trees to change from one animation state to another even makes sense, since they are completely discrete...
     
  11. keely

    keely

    Joined:
    Sep 9, 2010
    Posts:
    967
    Even though not their original purpose, the 2D blendtree to group 8 different directions of same animation is very convenient. This allows you to keep your state machine very simple (see screenshots). The "blend" is just a discrete change from one sprite anim to another, but that doesn't cause any harm.
     
    uncientificlab likes this.
  12. Adman

    Adman

    Joined:
    Jun 1, 2013
    Posts:
    12
    Cool. Actually, after reading a bit about blend trees last night, I WAS able to get something up and running with my idle/walk sprite animations. There were some bugs, but I think I see a clear path ahead of me to get it working. Thanks for taking the time to follow up on this post!
     
  13. stuckwiththisname

    stuckwiththisname

    Joined:
    Nov 19, 2013
    Posts:
    9
    Hey keely. I finally got a chance to follow up on this question. Blend Trees seem to be exactly what I was looking for. Thanks for your help!
     
  14. jjimenez

    jjimenez

    Joined:
    Nov 13, 2013
    Posts:
    1
    Salazar likes this.
  15. Litterbox

    Litterbox

    Joined:
    Jan 5, 2014
    Posts:
    7
    I followed along with this, and for the most part it seems to be working great. My only problem now is that it seems like my character sort of flickers between animations during a transition. For example, say I'm running down and I let go of the controls. My character will often times turn upwards, or to the left, etc. for a split second, and then settle back on his correct animation.

    Anybody have any ideas on this?
     
  16. WSllc

    WSllc

    Joined:
    Jul 3, 2012
    Posts:
    6

    It may be due to the Interpolation setting on your Rigidbody2D object. A couple days ago I noticed the Unity Lessons video on 2D Character Controllers shows the instructor setting the Interpolation setting of his main characters' Rigidbody2D object to "Interpolate". I didn't know why so I looked it up. The Unity Docs explain some flickering may occur on your character/object if you don't set this value, due graphics physics possibly getting out of sync.

    The training video point I'm referring to is at 8min 53sec.

    http://unity3d.com/learn/tutorials/modules/beginner/2d/2d-controllers

    Here's the doc reference.

    http://docs.unity3d.com/Documentation/ScriptReference/Rigidbody-interpolation.html
     
  17. WSllc

    WSllc

    Joined:
    Jul 3, 2012
    Posts:
    6
    Thanks jjimenez keely! This thread and your contributions have helped me very much. I was struggling to find good examples of using blend trees for 2D games and now I feel much more solid on using them.
     
  18. Litterbox

    Litterbox

    Joined:
    Jan 5, 2014
    Posts:
    7
    Thanks for the response. I appreciate it. However, I don't believe my issue is a result of a Rigidbody component as I hadn't added one at all yet while setting up the Blend Tree. I've since added one, considering that maybe the issue was a result of not having a Rigidbody, or of being stationary while animating. But, the issue persists.

    For example, if I run to the right my character will trigger his run_right animation, and loop. When I let go, he'll flicker into an upward facing idle animation for a split second, and then settle back on his correct idle-facing animation.

    What happens isn't a movement flicker, but more of a transitional flicker. It's as though the Animator has trouble for a moment deciding what it should be doing and toggles back and forth rapidly before settling on the correct option. During the transition, I can see that the bar for "Walking" is still filling, while "Idle" is depleting. So, there's a bit of an overlap, which I'm assuming is how this is supposed to work on account of this being traditionally used to blend two 3D animations together.

    So, with that in mind, I thought this might have to do with the transition timeline. I shortened the transition to 0% thinking this would reduce overlap of the animations and give me a hard cut between the two animations I'm looking for, though this only seems to make the glitch happen more quickly. It's less noticeable this way, but still very much there. Everything between 0% - 100% seems to exhibit this behavior to varying degrees.

    I considered the possibility that maybe my animations had a rogue upward facing frame in them, but that's not the case. They animations are setup just fine.

    I also considered the possibility that it may have been an issue with the dead-zone not being calibrated correctly on my Xbox 360 Controller, however the keyboard has this problem as well with the controller unplugged.

    I'm really at a loss at this point.
     
    Last edited: Jan 7, 2014
  19. WSllc

    WSllc

    Joined:
    Jul 3, 2012
    Posts:
    6
    This might be a really dumb reply, but are there any other apps (or drivers) running that could be interfering with the input signals being polled by Unity? Or apps causing Unity to struggle?
     
  20. Litterbox

    Litterbox

    Joined:
    Jan 5, 2014
    Posts:
    7
    I actually considered that as well. I even went so far as to try the project on an entirely different computer (my laptop) but the problem occurs there as well.
     
  21. Litterbox

    Litterbox

    Joined:
    Jan 5, 2014
    Posts:
    7
    Originally, I had designed my Blend Tree to use only N, S, E, W directions. Thinking that maybe this glitch was a result of that decision, I remade the Blend Tree, Character, sprite sheet, etc. Even the project was remade from scratch.

    I re-made everything, re-wrote the scripts, re-built the animations, animator transitions, etc. I followed jjimenez's YouTube screencast to the letter as far as I can tell, yet I still get this glitch.

    I'm going to include my bare-bones Blend Tree script here as well as some screenshots of my workspace to hopefully help with this:

    https://www.dropbox.com/s/9iz4ju15ewtxsc7/unity_blendtree_0003_Layer 1.jpg
    https://www.dropbox.com/s/8eqq7fou9ltsv9d/unity_blendtree_0002_Layer 2.jpg
    https://www.dropbox.com/s/8pdvd3f3g8juo6d/unity_blendtree_0001_Layer 3.jpg
    https://www.dropbox.com/s/ytz1ks5mwnaf6rj/unity_blendtree_0000_Layer 4.jpg



    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class PlayerScript : MonoBehaviour
    6. {
    7.     private Animator anim;
    8.  
    9.     // Use this for initialization
    10.     void Start ()
    11.     {
    12.         anim = GetComponent<Animator>();
    13.     }
    14.  
    15.     void FixedUpdate()
    16.     {
    17.         #region Walking Toggle in Animator via Input
    18.  
    19.         // Store input from GetAxis to determine if value
    20.         // is non-zero to toggle walking bool in Animator
    21.  
    22.         float lastInputX = Input.GetAxis ("Horizontal");
    23.         float lastInputY = Input.GetAxis ("Vertical");
    24.  
    25.         // If input: walking = true, else walking = false.
    26.  
    27.         if(lastInputX != 0 || lastInputY != 0)
    28.         {
    29.             anim.SetBool ("Walking", true);
    30.  
    31.             #region Remember Last Direction Faced
    32.  
    33.             // Remember last direction faced by checking
    34.             // lastInput(X  Y) for positive/negative values
    35.             // and assign LastMove(X  Y) a value on the
    36.             // blend tree to hold the appropriate idle direction
    37.             // after key press ends.
    38.  
    39.             if (lastInputX > 0)
    40.             {
    41.                 anim.SetFloat ("LastMoveX", 1f);
    42.             }
    43.             else if (lastInputX < 0)
    44.             {
    45.                 anim.SetFloat ("LastMoveX", -1f);
    46.             }
    47.             else
    48.             {
    49.                 anim.SetFloat("LastMoveX", 0f);
    50.             }
    51.  
    52.             if (lastInputY > 0)
    53.             {
    54.                 anim.SetFloat ("LastMoveY", 1f);
    55.             }
    56.             else if (lastInputY < 0)
    57.             {
    58.                 anim.SetFloat ("LastMoveY", -1f);
    59.             }
    60.             else
    61.             {
    62.                 anim.SetFloat ("LastMoveY", 0f);
    63.             }
    64.  
    65.             #endregion
    66.         }
    67.  
    68.         else
    69.         {
    70.             anim.SetBool ("Walking", false);
    71.         }
    72.  
    73.         #endregion
    74.     }
    75.     // Update is called once per frame
    76.     void Update ()
    77.     {
    78.         #region Send Input Float to Anim
    79.  
    80.         float inputX = Input.GetAxis ("Horizontal");
    81.         float inputY = Input.GetAxis ("Vertical");
    82.  
    83.         anim.SetFloat ("SpeedX", inputX);
    84.         anim.SetFloat ("SpeedY", inputY);
    85.  
    86.         #endregion
    87.     }
    88. }
    89.  
    90.  
    91.  
    I feel like I'm somehow overlooking something really obvious at this point, but I just can't seem to get this thing working. Also, if it matters, I'm using Unity 4.3.2f1.
     
  22. pfmpaulo

    pfmpaulo

    Joined:
    Jan 9, 2014
    Posts:
    7
    LitterBox, I'm having the very same problem, it's pretty bizarre, since the frame that is showing is always the same frame, so I removed the animation that had that frame, now it is flickering on another frame haha
     
  23. Litterbox

    Litterbox

    Joined:
    Jan 5, 2014
    Posts:
    7
    I'm glad to hear it's not just me, then. Can you tell me what version you're on? I may try using an older version, if possible, to see if maybe the latest version is causing problems.
     
  24. pfmpaulo

    pfmpaulo

    Joined:
    Jan 9, 2014
    Posts:
    7
    I'm using 4.3.2f1. I'm going to sleep now, but i pretend to remake the movement code tomorrow, if I get any progress, will comeback to report it. =)
     
  25. Litterbox

    Litterbox

    Joined:
    Jan 5, 2014
    Posts:
    7
    It doesn't seem to be an issue with version. I've now tried, 4.3.2, 4.3.1, 4.3.0, and 4.2.2. Though, the last one did not work at all on account of the new 2D tools I'm using. All of the others however still exhibit this behavior, though.
     
  26. pfmpaulo

    pfmpaulo

    Joined:
    Jan 9, 2014
    Posts:
    7
    I change some of the code today, at first I just changed some values and played a little bit with the speed of the animation, to see if had any impact on the problem, no luck... so I changed some of the lastInput code, sent it to the Update function, instead of the FixedUpdate... still no luck, then I moved all the code from Update, to FixedUpdate, nothing... so I made the movement to not be based on Time.deltaTime, because at this point... hell I don't know, I was just trying haha but no luck...
    So I can only think that this is a bug with the blend tree stuff... will try to do the animation without it... it's probably gonna be a mess, but it's the only thing that I can think of.
     
  27. pfmpaulo

    pfmpaulo

    Joined:
    Jan 9, 2014
    Posts:
    7
    Ok, so I got it to work without the Blend Tree, which is pretty lame, since Blend Trees seems to be a very good way to keep the Animator organized, and I think that now the animation is not as fluid as it was before.
    Here is how I did: to the Idle animation, i kept using the blend tree, that part is working, but the idle animation is actualy only one frame... so idk

    I'm gonna put some screenshots here, if you doesn't understand what I've done (because it is really confuse) or if you find a way to make it less ugly, please tell me hehe

    Those first two screenshots shows how I've done the transitions between the Idle state and every Walk state, keep in mind that I kept using the Blend Tree for the Idle animation
    https://www.dropbox.com/s/oabf8qzvqn1fe0g/Captura de tela 2014-01-10 14.01.11.png
    https://www.dropbox.com/s/jy30d8piesxgdel/Captura de tela 2014-01-10 14.01.17.png

    Those last two screenshots shows how I've done the transitions between the walking states itself
    https://www.dropbox.com/s/trchxk7ssp7t627/Captura de tela 2014-01-10 14.19.58.png
    https://www.dropbox.com/s/m3rtrjf8vosh18i/Captura de tela 2014-01-10 14.20.02.png

    btw, on the two later screenshots, above the conditions for the transition happens, there is some graph on the animation, I don't know exactly how that work, but try changing it if any bugs happens in the transition. =)


    I hope there is a way to make it look a little less messy and ugly haha but I believe it is working hehe
     
    Last edited: Jan 10, 2014
  28. Litterbox

    Litterbox

    Joined:
    Jan 5, 2014
    Posts:
    7
    Haha. It sounds like we more or less reached the same conclusion, around the same time, in regards to tossing out the Blend Tree all together.

    Here's a link to the script I made to handle this particular piece. It's only showing the Update portion and the public variables used within it. But, it should give you an idea on how I more or less re-did what the Blend Tree did.

    http://pastebin.com/mE324VbB

    I opted out of using the Animator to actually handle transitions in this case, opting instead to use it as a sort of list to simply store animations which I would later call on by hand via script. Not sure it's the most graceful way of handling it, but it's getting the job done nicely so far. Here's a screenshot of my animator:

    https://www.dropbox.com/s/p05n2k96bsrou4s/unity_blendtree_workaround.jpg

    Hope this helps anybody else out there who ended up frustrated or confused by this blend tree glitch.
     
  29. pfmpaulo

    pfmpaulo

    Joined:
    Jan 9, 2014
    Posts:
    7
    Yeah haha
    looks cool, I'm still gonna try to use Blend Trees for other purposes, it's a pretty cool feature =)

    Gonna use your script, I believe that makes the animation more fluid than the way I did. The way I did, the animation has a half-second delay to transition it.
     
  30. pfmpaulo

    pfmpaulo

    Joined:
    Jan 9, 2014
    Posts:
    7
    Ok, so I were working to do something that looked cleaner in both the animator and the code, and using your code as an inspiration (I actually didn't know that I could play an animation with the code haha) I come up with this:

    https://www.dropbox.com/s/i7rhsg60gett9kd/Captura de tela 2014-01-11 16.43.01.png
    https://www.dropbox.com/s/1wk5d03stxblz4m/Captura de tela 2014-01-11 16.43.41.png
    https://www.dropbox.com/s/6r2x29zstbehoib/Captura de tela 2014-01-11 16.44.00.png

    Ok, so I used the code to handle the transitions and play the animations, and to make the idle, I used 1D blend Tree, when there is no speed, it plays the idle animation, if there is speed, it plays the walk animation... pretty simple stuff, less code and the animator stays clean =)
     
  31. WSllc

    WSllc

    Joined:
    Jul 3, 2012
    Posts:
    6
    Litterbox pfmpaulo, I had to leave this thread for the past couple days to experiment with fixes in between personal stuff that needed tending to.

    I've attached a sample project I created last night. Apologies for the length of this post, :) and the lame Idle state animation (just grabbed a frame and added a growing line to see something move). I didn't implement animated idle states but that shouldn't impact anything.

    I did see flickering as you're describing.

    I've been trying many techniques to avoid it while still using blend trees. I've messed with Trigger parameters and the Any State state, Input Axis settings tweaks, yields and co-routines (in case the code was telling the animator to do something and not waiting for it to finish before sending more commands), all to no avail. :(

    But then something hit me as I was watching the flicker. The annoying frame was always a particular one. I experimented and think I've figured it out.

    In the project I've attached the only thing I see flickering is when moving the character in a diagonal direction via pressing 2 arrow keys simultaneously (ie. Down Left), and let the character walk for a bit, then let off both keys simultaneously... at that point... I do see a single frame of either the Down (or Left) animation without the other just before it enters the Idle state. Which one is irrelevant and almost random I think. But I don't think that's a bug. Here's why:

    I think it's the falloff rate of how the axis are returning to a zero center-joystick position in the animation controller (ultimately from the Input but it's easily visible in the animation controller). I think it's too slow and also that each axis is technically (ever so slightly) falling off at different rates. So one axis will reach zero and the other will slightly still be falling off towards zero (like at 0.1 or 0.105 or whatever) and be just large enough a value to show that axis' frame before finally getting cutoff at the next time cycle.

    Those axis values are tied to the Input.GetAxis values. Input.GetAxis has built-in ramping up/down in values to smooth incremental values and simulate an analog joystick moving through it's motion. If you look in the Project Settings > Input panel you'll see some settings you can tweak which illustrates that simulation.

    So in addition to having the arrow keys for left/right/up/down and simultaneously pressing two for a diagonal movement. I also coded "q" "e" "z" "c" as dedicated diagonal keys. These are not using a simulated "axis" so there isn't any ramping / smoothing, they're straight boolean on/off key states. If you move the character down-left diagonally via the "z" key, let him walk a bit, then let up, the last frame flicker isn't there. I believe because the blend tree isn't ramping down between two values over time, it just goes to zero position all at once... bang.

    I decided to verify this and modify the Input.GetAxis lines to the Input.GetAxisRaw lines. The Raw versions will only return integers of the axis ramp values... -1 0 1. Thereby effectively making it like I used GetKey, but this way the keys used could still be the "axis" arrow keys. When I run the project using the Raw version lines I do not get the flicker effect.

    No clue on how to control the falloff rate, but I did mess with the Input settings which seemed to correspond to falloff but there weren't any changes that I could see in the controller.

    Also I never tried your flicker effect outside the editor's run mode. Maybe if it's a built runtime executing without the Editor in the background it'll behave differently?

    Anyway, I hope this helps. Let me know.


    View attachment $Vlad Blend Tree.zip
     
  32. zombiegorilla

    zombiegorilla

    Moderator

    Joined:
    May 8, 2012
    Posts:
    9,052
    Just to clarify, where do you see the flicker? In the Game window in the editor? If so, does it not show up in the scene view?

    I ask because I have a problem, where after a while, I get flickering/artifacts in the Game window (usually they are elements from nested animations). They don't appear in the scene window, and they don't show up in builds/devices. Often if I just restart Unity they will go away for awhile. Wondering if it may be the same weirdness that I am seeing. So far, I just ignore it. I seems to be an editor display bug, and doesn't affect my builds. I haven't got to filing a bug report yet, but will do soon.
     
  33. pfmpaulo

    pfmpaulo

    Joined:
    Jan 9, 2014
    Posts:
    7
    Yaaaay
    More ways to solve the problem, well the flickering is visible in every window, in the scene, game, in the preview, it's not just something that happens in the editor. I'll take a look at this test project that you posted WindingSprings, and try to play with AxisRaw stuff. Thanks =)
     
  34. Patorama

    Patorama

    Joined:
    Jan 19, 2014
    Posts:
    3
    I don't know if anyone is still following this thread, but I wanted to post what fixed the flicker for me.

    The problem (I think) is that the animator was still in the walking state when the speed slowed down to X:0, Y:0. This point is equidistant from all of the 8 points in the Blend Tree. The animator didn't know which state to use and threw in a random one, causing that one or two frame flicker before transitioning to idle.

    What I did was change the script so that the game made sure that either InputX or InputY was not equal to zero before passing along the x and y values. This way, the walking blend tree is never set to 0,0. The script looked like this:

    Code (csharp):
    1.     void SetAnimation()
    2.     {
    3.         //Get the X and Y input values
    4.         LastInputX = Input.GetAxis ("Horizontal");
    5.         LastInputY = Input.GetAxis ("Vertical");
    6.        
    7.         //If either X or Y is not equal to zero, pass the speed values and set the walking bool to true
    8.         if(LastInputX != 0 || LastInputY != 0){
    9.             anim.SetFloat("SpeedX", LastInputX);
    10.             anim.SetFloat("SpeedY", LastInputY);
    11.             anim.SetBool("walking", true);
    12.             //Determine the last direction walked for the idle transition
    13.             if (LastInputX > 0){
    14.                 anim.SetFloat("LastMoveX", 1f);
    15.             } else if (LastInputX < 0){
    16.                 anim.SetFloat("LastMoveX", -1f);
    17.             } else {
    18.                 anim.SetFloat("LastMoveX", 0f);
    19.             }
    20.  
    21.             if (LastInputY > 0){
    22.                 anim.SetFloat("LastMoveY", 1f);
    23.             } else if (LastInputY < 0){
    24.                 anim.SetFloat("LastMoveY", -1f);
    25.             } else {
    26.                 anim.SetFloat("LastMoveY", 0f);
    27.             }
    28.         //Both X and Y are zero, set the walking bool to false. Use the idle animations
    29.         }else{
    30.             anim.SetBool("walking", false);
    31.         }
    32.     }
     
  35. WSllc

    WSllc

    Joined:
    Jul 3, 2012
    Posts:
    6
    Patorama, I see your point. Could you have reached the same effect if you had given the Blend Tree something to show for 0,0? Like placed an idle animation specifically set for PosX and PosY blend parameters equal to 0,0? That wouldn't do anything for showing a specific idle facing the last known facing direction, but other than that shouldn't it also fix the flicker under your observations?
     
  36. Patorama

    Patorama

    Joined:
    Jan 19, 2014
    Posts:
    3
    I initially tried that, but I ran into some problems having a sprite at (0,0).

    1. If I placed a single sprite there, for example idleDown, it wouldn't always match with the idle animation it would transition to. I suppose I could have tried setting that sprite in code based on the direction the player had just been moving, but it seemed like that sort of defeated the purpose of the blend tree to begin with.

    2. I also tried setting an idle animation in each direction just outside of (0,0). So I had IdleUp at (0, 0.01) and WalkUp at (0,1). The same for each direction. Oddly enough, this created an even bigger flicker. I think the different idle animations were too close and still fighting each other as the speed reduced down to zero. It also started to really clutter the Blend Tree.

    Ultimately the version I posted worked the best. No additional code, just a change in the order of operations. If you watch the Animator while moving around, the walk animation transitions to the idle animation when the player's speed hits (0,0), but before those two variables are passed to the animator, meaning it never hits that awkward single frame that causes the flicker.
     
  37. WSllc

    WSllc

    Joined:
    Jul 3, 2012
    Posts:
    6
    Ah I see. I was next going to suggest your #2 item. I like how you've implemented it. Makes sense. Good job! I'm going to start using them the way you've outlined. Thanks for sharing! :)
     
  38. BadFoolPrototype

    BadFoolPrototype

    Joined:
    Sep 7, 2013
    Posts:
    14
    Hey guys, just wanted to thank you for this post!
    I've used the solution proposed here with the fix from Patorama (not sending the lastInputX & Y when they are both 0.0f) and it works like a charm.
    Mecanim is much more maintainable and readable.
    Thanks!
     
  39. Souri

    Souri

    Joined:
    May 30, 2013
    Posts:
    12
    Hi folks, apologies for digging up this thread, but I found it to be an incredible useful way to implement animation for a top-down 2D player (e.g. Gauntlet, Zelda etc). If you're planning to do it, this is *definitely* the way to go!

    HOWEVER, as the posts above have encountered, there are some lingering issues (flickering etc) that haven't been resolved. I've made some small changes and it's working perfectly for me now, so I thought I'd pass it forward in the case that others may find this thread and want use this implementation without issues. btw, my code is based on what was written in the youtube video here by jjimenez:


    The problem with the flickering arises with this line (there are two instances of them, one in Update, and the other in FixedUpdate - you will need to change them both):

    Code (CSharp):
    1. if (InputX != 0 || InputY != 0) {
    In this line, we say that if we're moving, we're gonna set off the character animations in the blend tree. Sounds good, but when we take our finger off the button, InputX and or InputY will still ramp down to 0 and so it will still stay on the walking animation until then. If you were pressing both buttons, one of those might hit 0 first depending one which you had let go of earlier and trigger an animation because of that, and then other will get to 0 right after and start another animation sequence, which is what is causing that flicker. We're going to change that to:

    Code (CSharp):
    1. if (Input.GetButton("Horizontal") || Input.GetButton("Vertical")) {
    So, as soon as the player takes their finger off the button, there's no frame or two showing the walking anim until InputX and/or InputY decreases to zero. It should instantly go to the idle state and it's corresponding anim. This also happens to resolve the problem of the character not always facing diagonally once you let go of the buttons after walking diagonally - it sometimes faced, for example left, after pressing left + up because you depressed the left key a *split* millisecond before the up key.

    Also, in the FixedUpdate(), setting the walking anim off and on isn't really needed here since we're doing that in Update() already, so we can get rid of that.. So, the changes I've made are like this:

    Code (CSharp):
    1.     //reference to player's animator component
    2.     Animator anim;
    3.  
    4.     void Start () {
    5.  
    6.         anim = gameObject.GetComponent<Animator> ();
    7.  
    8.     void Update () {
    9.  
    10.         float InputX = Input.GetAxis ("Horizontal");
    11.         float InputY = Input.GetAxis ("Vertical");
    12.  
    13. //        if (InputX != 0 || InputY != 0) {
    14.         if (Input.GetButton("Horizontal") || Input.GetButton("Vertical")) {
    15.  
    16.             anim.SetBool ("walking", true);
    17.  
    18.             if (InputX > 0 ) {
    19.                 anim.SetFloat("speedX", 1f);
    20.             }
    21.             else if (InputX < 0) {
    22.                 anim.SetFloat("speedX", -1f);
    23.             }
    24.             else {
    25.                 anim.SetFloat("speedX", 0);
    26.             }
    27.  
    28.             if (InputY > 0 ) {
    29.                 anim.SetFloat("speedY", 1f);
    30.             }
    31.             else if (InputY < 0) {
    32.                 anim.SetFloat("speedY", -1f);
    33.             }
    34.             else {
    35.                 anim.SetFloat("speedY", 0);
    36.             }
    37.  
    38.             //not moving, set the anim
    39.         } else {
    40.             anim.SetBool ("walking", false);
    41.         }
    42.  
    43.     void FixedUpdate() {
    44.  
    45.         float lastInputX = Input.GetAxis ("Horizontal");
    46.         float lastInputY = Input.GetAxis ("Vertical");
    47.  
    48.         if(Input.GetButton("Horizontal") || Input.GetButton("Vertical")){
    49.  
    50.             if (lastInputX > 0) {
    51.                 anim.SetFloat("lastmoveX", 1f);
    52.             }
    53.             else if (lastInputX < 0) {
    54.                 anim.SetFloat("lastmoveX", -1f);
    55.             }
    56.             else {
    57.                 anim.SetFloat("lastmoveX", 0);
    58.             }
    59.  
    60.             if (lastInputY > 0) {
    61.                 anim.SetFloat("lastmoveY", 1f);
    62.             }
    63.             else if (lastInputY < 0) {
    64.                 anim.SetFloat("lastmoveY", -1f);
    65.             }
    66.             else {
    67.                 anim.SetFloat("lastmoveY", 0);
    68.             }
    69.         }
    70.     }
    Update: There are some other major issues I've found that I've had to fix to make this better. There's still ramping down when you let go of a button - you'll notice it when you walk diagonally and then release one of the buttons. There's a small pause before the new correct animation goes in effect.

    So, when you let go of one of the buttons, the code "else if (lastInputX < 0) {" won't tell the animator to change until it ramps down to zero so there's a .5 or so second delay. I think since most people will use this for 2D games and expect an arcade instant-response feel to things, so it's best to just get rid of all ramping with:

    Code (CSharp):
    1.  
    2. float InputX = Input.GetAxisRaw ("Horizontal");
    3. float InputY = Input.GetAxisRaw ("Vertical");
    4.  
    I'll just post my updated and refactored code in the next post.
     
    Last edited: Nov 29, 2014
    jswonn likes this.
  40. Souri

    Souri

    Joined:
    May 30, 2013
    Posts:
    12
    Hi folks, as mentioned in the previous post, here's my updated and refactored code. Works great!

    Code (CSharp):
    1.     Animator anim;
    2.  
    3.     void Start () {
    4.         anim = gameObject.GetComponent<Animator> ();
    5.     }
    6.  
    7.     void Update () {
    8.         float InputX = Input.GetAxisRaw ("Horizontal");
    9.         float InputY = Input.GetAxisRaw ("Vertical");
    10.  
    11.         if (Input.GetButton ("Horizontal") || Input.GetButton ("Vertical")) {
    12.             anim.SetBool ("walking", true);
    13.             setPlayerDirectionAnim(InputX, InputY);
    14.         } else {
    15.             anim.SetBool ("walking", false);
    16.         }
    17.     }
    18.  
    19.    void setPlayerDirectionAnim(float horizInput, float vertInput) {
    20.          anim.SetFloat ("inputX", horizInput);
    21.          anim.SetFloat ("inputY", vertInput);
    22.          anim.SetFloat ("lastmoveX", horizInput);
    23.          anim.SetFloat ("lastmoveY", vertInput);
    24. }
    That's it! The animation responses are instant and no flickering!

    Movement will also be instant - no acceleration or deceleration (just like arcade-style). If you want that back though then you can create a new set of values and base the movement on that... e.g.

    Code (CSharp):
    1. float moveX = Input.GetAxis ("Horizontal");
    2. float moveY = Input.GetAxis ("Vertical");
     
    Last edited: Nov 29, 2014
    hms0589, jswonn and Erenyx like this.
  41. schkorpio

    schkorpio

    Joined:
    Apr 15, 2015
    Posts:
    14
    Thanks a lot everyone, this has been a massive help to me. :D

    I went further and used a 16 direction sprite, and inside the blend tree I use 0.5 float values for the in 16th diagonal frames, and a non-raw axis, so that as you go from say Up to Up-Right, it puts in a few frames of Up-Up-Right, and the transitions are quite smooth!
     
  42. Badonkadoo

    Badonkadoo

    Joined:
    Jan 8, 2016
    Posts:
    1
    To fix the flicker I moved all my code from the FixedUpdate() to the Update()
     
  43. Raf95

    Raf95

    Joined:
    Nov 8, 2017
    Posts:
    23
    Anyone has an idea how could this be implemented to 3D controller