Search Unity

Reverse Mecanim animation midway through?

Discussion in 'Animation' started by rukadev, Oct 10, 2013.

  1. rukadev

    rukadev

    Joined:
    Oct 10, 2013
    Posts:
    3
    Hi all,

    I'm new to the Mecanim way of doing things and am struggling with a particular animation I want to achieve.

    I have a door which opens when some event happens (say, a character moving into a trigger zone). The opening of the door lasts for 60 frames.

    If the character leaves the trigger zone before the doors have finished opening, I want the doors to immediately begin closing from where they currently are within the open door animation. So if the doors have half-opened, they should not continue to open but should begin closing from the half-open position.

    I can't figure out how to achieve this or determine if this is possible - I've done a lot of searching on this but haven't found anything.

    Currently I have a transition from an idle state to a door opening state, and from door opening to door closing (and then back to idle). Door closing has a speed of -1 so playing in reverse works fine. If the door is part-way opened when the transition from opening to closing happens, the animation jumps to the end before it starts closing. The animation is one I've exported from blender 2.68a (FBX) if that makes a difference.

    I would really appreciate any ideas on how to make this work. Thanks in advance for reading. :D
     
  2. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,694
    You can do it the Mecanim way, using transitions. Just transition to the door closing state as soon as it needs to happen. (Don't set Exit Time as a condition.)
     
  3. rukadev

    rukadev

    Joined:
    Oct 10, 2013
    Posts:
    3
    Hi Tony,

    Thanks for your assistance.

    I already have a transition setup to move to the door closing state (from the door opening state) when required. As soon as the transition takes place, the animation jumps to the end and starts playing backwards.

    I have the same animation motion set for both states - the animation opens the door. The door closing state has a speed of -1 so it plays in reverse. Do I need a separate door closing animation for this? I have a feeling this would still force the animation to jump to its initial position rather than resuming halfway through.

    Other ideas I've had are to find a way to store the animation position at the point in time the transition takes place and set this position in the next state so that the animation picks up where it left off. I'm not having much luck with this either.

    If you have any other suggestions I'd be grateful to hear them. :)
     
  4. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,694
    Good point. As soon as it transitions to the door closing state, the door closing animation will start from the fully-open position, even if was only half open.

    A blend tree would do the job. Blend on a float parameter where 0 means the door is fully closed and 1 means the door is fully open. The animation clip for 0 would be the door in the closed position (stationary). The clip for 1 would be the door in the open position.

    To open the door, smoothly increase the parameter from 0 to 1. If you need to close the door, start decreasing it instead.

    Mike Talbot at WhyDoIDoIt.com wrote an article and some code to handle smooth floats: http://whydoidoit.com/2012/04/01/smoothed-vector3-quaternions-and-floats-in-unity/
     
  5. sotirosn

    sotirosn

    Joined:
    Jan 5, 2013
    Posts:
    24
    This would be kind of cheating as it only works for animations with two frames. Any animations in between the end and the start would be skipped. Works for a door but what about something like climbing up and then climbing down? The down needs to start at a point synced with the up, right?
     
  6. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,694
    For climbing, you could blend on something like:

    -1 = climb down (climb up in reverse)
    0 = neutral
    +1 = climb up

    The blend values [-1,+1] are arbitrary; they just happen to match up nicely with Input.GetAxis(). To start climbing down, you just smooth down from +1 to -1.
     
  7. rukadev

    rukadev

    Joined:
    Oct 10, 2013
    Posts:
    3
    Thanks once again for the assistance, Tony.

    I've tried everything I could think of with blend trees and with alternate ways of setting up the transitions, using ForceStateNormalisedTime, etc. I have it *almost* working - currently the animation skips to the end for a single frame before resuming where it was and reversing. This seems to be because ForceStateNormalizedTime only takes effect on the *next* frame, and I can only set it after the transition to the 'door closing' state has begun - which means I can't prevent one frame of 'door closing' being rendered at its initial (fully open) position.

    It seems in order to use the blend trees in the manner described I'd have to (as you suggested) have one key frame at the start and one key frame at the end as separate animation clips - that is, they wouldn't be animations at all, just key frames. It seems a bit much to use a system such as this to interpolate between two key frames. My animation has four, so I would actually have to compose the animation manually within Mecanim. I'd rather do this in Blender where I have more flexibility.

    I'm going to concede defeat on this. It doesn't seem as though this system supports (yet? :D) the 'reverse an animation from the current frame' scenario. I learned a lot by experimenting, though. :)

    Thanks once again. Cheers!
     
  8. junzy

    junzy

    Joined:
    Oct 26, 2013
    Posts:
    2
    Are there any new updates on this issue yet ?
     
  9. dougdodd

    dougdodd

    Joined:
    Apr 18, 2014
    Posts:
    32
    I needed a way to control a complex folding animation, and I wanted to be able to fold or unfold to any point and stop (and have it stay stopped).
    The keys for me are 2 things: Transition offset to 1e-05 & animator.speed = 0 to freeze an animation
    I was able to achieve this effect with a 2 static states (like opened and closed). My rig is a mechanical basketball "hydragoal" consisting of a set of folding/unfolding animating parts (mechanical joints) that need to animate together for the rig to look correct.
    After wrestling around with mecanim for (more than a) few hours, I finally got something that achieves my goal. I just set a bool goingUp = true to transition from GoalUp to GoalDown, and then if I set the same bool goingUp = false to trigger transition from GoalDown to GoalUp. Now in my Controller object, if I'm pressing upArrow I set goingUp = true and when I release either arrow, I set the animator.speed = 0. I also set the transitions to be interruptable by the Next state; which means I can go up or down smoothly and stop at will. If I reverse, it starts where it left off and reverses smoothly.

    upload_2016-3-1_15-23-3.png
    Turned out to be as simple as I wanted it to be. Gotta love Unity.

    Code (CSharp):
    1.     void Update()
    2.     {
    3.         if (Input.GetKeyDown(KeyCode.UpArrow))
    4.         {
    5.             animator.speed = .5f;
    6.             animator.SetBool("goingUp", true);
    7.         }
    8.  
    9.         if (Input.GetKeyUp(KeyCode.UpArrow) || Input.GetKeyUp(KeyCode.DownArrow))
    10.         {
    11.             animator.speed = 0;
    12.         }
    13.  
    14.         if (Input.GetKeyDown(KeyCode.DownArrow))
    15.         {
    16.             animator.speed = .5f;
    17.             animator.SetBool("goingUp", false);
    18.         }
    19.  
    20.     }
    21.  
    I don't understand the following, but it works for me for states that have animation clips that represent a single frame (intermediate state) of animation. I set the Transition offset to 1e-05 and this allows me to smoothly transition from "frame n0" to "frame n1". If I set it to 0, it doesn't work like I expect it to. I am still trying to understand how mecanim works in different situations. I find it unwieldy and inconsistent atm, but when it works WOW.
    upload_2016-3-1_15-9-4.png

    Up (or rest state)
    upload_2016-3-1_15-25-31.png

    Arbitrary point between up & down
    upload_2016-3-1_15-27-37.png

    Down
    upload_2016-3-1_15-28-23.png upload_2016-3-1_15-25-31.png upload_2016-3-1_15-27-37.png upload_2016-3-1_15-28-23.png
     
  10. dougdodd

    dougdodd

    Joined:
    Apr 18, 2014
    Posts:
    32
    I found a far superior way to do what I wanted to do and I felt compelled to post. It involves only 1 clip and is VERY easy to set up. Had I had this knowledge earlier, this would have saved me days of mucking around with other frustrating approaches.

    Single state in mecanim: I have a single clip for the goal to fold down (including 3 parts that need to rotate precisely together).
    upload_2016-3-1_19-22-35.png
    In the Animator, I create a float value called "speedMultiplier".
    upload_2016-3-1_19-36-37.png
    In the Inspector for this clip, I enable the Speed multiplier and set the parameter name to "speedMultiplier".
    upload_2016-3-1_19-39-6.png

    The code is simple:
    Code (CSharp):
    1.  
    2.     public Animator animator;
    3.  
    4.     void Awake()
    5.     {
    6.         animator.speed = 0;
    7.     }
    8.  
    9.     private float speed = .5f;
    10.     private bool animating = false;
    11.  
    12.     void Update()
    13.     {
    14.         if (Input.GetKeyDown(KeyCode.DownArrow))
    15.         {
    16.             if (animating) return;
    17.             animating = true;
    18.  
    19.             animator.SetFloat("speedMultiplier", 1f);
    20.             animator.speed = speed;
    21.         }
    22.  
    23.         if (Input.GetKeyUp(KeyCode.UpArrow) || Input.GetKeyUp(KeyCode.DownArrow))
    24.         {
    25.             animator.speed = 0;
    26.             animating = false;
    27.         }
    28.  
    29.         if (Input.GetKeyDown(KeyCode.UpArrow))
    30.         {
    31.             if (animating) return;
    32.             animating = true;
    33.  
    34.             animator.SetFloat("speedMultiplier", -1f);
    35.             animator.speed = speed;
    36.         }
    37.     }
    38.  
    39.  
    The key here is to set speedMultiplier =-1 when I wish to go backwards.

    The beauty of this approach is I only need 1 clip per complex movement. I can then play or "index" into that clip forward or backward, preserving the "frame pointer" pointing to the correct frame. This means that in-game the players can adjust the position of the goal to wherever they want, whenever they want. This would also work for manually controlling a crane that has specific movements that involve multiple parts that can naturally move forward or in reverse.
    I have a mechanical robot character which is not set up for bi-ped character animation that I can now control interactively just like I might "control" a robot in real life with a complex controller. I will set up a demo with his arm to demonstrate this movement.
     
    Dondosh2 likes this.