Search Unity

Stop animation half way through and reverse

Discussion in 'Animation' started by gumboots, Aug 7, 2017.

  1. gumboots

    gumboots

    Joined:
    May 24, 2011
    Posts:
    298
    Hi there,

    Is there a way to play an animation in reverse midway without setting the Animator's speed?

    I've seen people suggest setting the speed of the Animator, but the Animator is controlling all the animations on that object, monitoring when that should be playing normally or not becomes too complicated.

    The speed of an animation should be able to be set on the animation itself either by code or not, which it can be, by the multiplier (which also seems to then ignore exit time - https://forum.unity3d.com/threads/speed-multiplier-ignores-exit-time.487006/), however this will restart the animation each time it needs to play backwards.

    I want to play an animation provided a condition is met, however if during the animation it is no longer met, I want to reverse that animation from where it currently is.

    All help appreciated!
     
  2. NoiseFloorDev

    NoiseFloorDev

    Joined:
    May 13, 2017
    Posts:
    104
    Have you tried setting it in code? I've found that changing properties in the animator window will reset the animation, but that doesn't happen when setting it with animator.SetFloat.
     
  3. gumboots

    gumboots

    Joined:
    May 24, 2011
    Posts:
    298
    Hey thanks for the response. Yeah I'm currently only setting it in code:

    Code (CSharp):
    1. bool isMouseDown = Input.GetMouseButton(1);
    2. anim.SetInteger(AIM_SPEED, isMouseDown ? 1 : -1);
    The problem is it jumps to the beginning of the animation to then rewind itself.
     
  4. NoiseFloorDev

    NoiseFloorDev

    Joined:
    May 13, 2017
    Posts:
    104
    Try a float instead of an int? It works for me without resetting the animation if I do that.
     
  5. gumboots

    gumboots

    Joined:
    May 24, 2011
    Posts:
    298
    As in it plays backwards for you? Could you share your code and a screenshot of your animator?
     
  6. NoiseFloorDev

    NoiseFloorDev

    Joined:
    May 13, 2017
    Posts:
    104
    Nothing special:

    Code (CSharp):
    1. float speed = animator.GetFloat("Animation speed");
    2. speed = EditorGUILayout.Slider("Animation speed", speed, -1, 1);
    3. animator.SetFloat("Animation speed", speed);
    4.  
     
  7. gumboots

    gumboots

    Joined:
    May 24, 2011
    Posts:
    298
    Ah ok but you've only got the one animation. If you want to transition to another state depending on whether speed is greater than or lower than 0, does that work for you?
     
  8. NoiseFloorDev

    NoiseFloorDev

    Joined:
    May 13, 2017
    Posts:
    104
    Can you make a sample scene to show what you're doing (so I don't have to guess :) ?
     
  9. gumboots

    gumboots

    Joined:
    May 24, 2011
    Posts:
    298
    Haha of course! Would've been smarter from the start, hehe:

    https://www.dropbox.com/s/xmq4qkiydasqq8g/Animation Issue Example.zip?dl=1

    So in the example, if you hold right click the stick should animate down, then once the animation is complete animate side to side. Once you let go of right click, the stick should animate up and then return to idle.

    The issue is two fold:
    1. The stick animation has to complete, you can't let go half way through and it animate from where it was.
    2. Secondly, the middle state ("Animate Out") uses a multiplier on it to determine whether it should the animation should play forwards or backwards, and it seems like the animation never finishes when it goes backwards, so on returning to idle, it gets stuck in the middle state.
    Thanks so much for your help!
     
  10. NoiseFloorDev

    NoiseFloorDev

    Joined:
    May 13, 2017
    Posts:
    104
    The animation doesn't restart for me (if I click and release during the "Out" transition, it goes up and down without restarting), but the transition never triggers if the speed is negative (in 2017.1.0f3) If I turn off Has Exit Time, the transition triggers, of course at the wrong time, without waiting.

    They're probably doing something like "if(new time >= exit time && old time < exit time)", which assumes time is moving forwards. What they should be doing (if my guess is correct) is more like "if(new time >= exit time != old time < exit time)", which detects crossing the exit time in both directions. But we can't fix that (might want to file a bug if you think that's what's happening), so let's look for a workaround...

    You could make separate reversed copies of each clip. Then you could switch from the forwards animation to the corresponding backwards animation when you need to switch direction, and you'd never have to use a negative speed. If you'll have a lot of animations this could be a pain, but you could probably automate creating the reversed animations with an editor script...
     
  11. gumboots

    gumboots

    Joined:
    May 24, 2011
    Posts:
    298
    Yes I ended up finding the same thing. But what I want is to be able to click and release instantly, and it return to idle from the current animating out frame, rather than having to go all of the way up, and all of the way down. I can't seem to find a way to achieve this, it has to go all the way up, and then realise it should be down and return to down.

    I have done the separate reversed clips, which does make it work with up and down, however I seem to be then be even further away from being able to lower mid-raise, as they're two entirely different clips?
     
  12. NoiseFloorDev

    NoiseFloorDev

    Joined:
    May 13, 2017
    Posts:
    104
    You'd need to switch between the clips manually, by looking at the current time of the animation and matching it up with the corresponding time in the other animation. For example, in normalized (0-1) time, if you're at 0.25 in the "down" animation, that's equivalent to 0.75 in the reversed "up" animation, so you'd call Play("UpAnimation", time) to play that animation.

    You can query the current time of the animation with Animator.GetCurrentAnimatorStateInfo().normalizedTime, to figure out what time you need to switch to, and you can get the currently playing clip with Animator.GetCurrentAnimatorClipInfo, so you can figure out which clip to switch to (eg. if you're on "Up", match it with "Down").

    Very awkward, for sure--maybe somebody has a better idea--but it's all I can think of. Be sure to file a bug about transitions not working when playing backwards, since getting that fixed would make this a lot easier for you...
     
  13. NoiseFloorDev

    NoiseFloorDev

    Joined:
    May 13, 2017
    Posts:
    104
    Hmm. While looking at animations for UI (very not sold on this...), I noticed something else strange. If I set the speed (not speed multiplier) of a state to -1, the clip plays backwards, but the blue bar in the animator view moves forwards, which means normalized time is still moving in a positive direction. In this case, transitions work, but to have a transition play when the reversed clip finishes, I have to set its exit time to 1, not 0.

    In contrast, setting the speed multiplier to -1 causes the blue bar in the animator view to move backwards, and exit time transitions don't work at all.

    Doesn't help the original problem, just some more weirdness that seems worth mentioning. You'd expect a speed multiplier to have the same effect as, well, multiplying the speed, but it actually behaves very differently.
     
  14. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    Hi guys, I'm currently working on this bug but haven't found a way to solve the issue for exit time with speed multiplier.

    Effectively setting the state speed to -1 reverse the whole clip. So you still see the blue bar going forwards.

    But I'm still not sure how to deal with speed multiplier and Exit time.

    Here some detail
    An exit time condition is true only if the previous state time is before exit time and current state time is after exit time
    Code (CSharp):
    1.  previousTime < ExitTime && currentTime >= ExitTime
    this work well if the time is going forwards.
    But with speed multiplier the time can switch from forwards to backwards, in this case previous time is always bigger than current time, so the condition for exit time will never be true

    @gumboots is expecting that a transition with an exit time of 1 should be triggerred either if the time is going forwards or backwards. But if you start to play a clip with speed multiplier of 1 and then half way in the middle of the clip you change the speed multiplier to -1 you will never get to the end of the clip.

    One thing that could maybe work but still need a patch in the code is a transition with an exit time of 1 and speedMultiplier > 0 for forwards playing and another transition with an exit time of 0 and speedMultiplier < 0 for backwards playing.
    To make it work I would need to reverse the previous and current time when we detect a negative speedmultiplier.

    What do you think? or maybe you have another idea?
     
  15. NoiseFloorDev

    NoiseFloorDev

    Joined:
    May 13, 2017
    Posts:
    104
    That's what I suggested was happening above ("if(new time >= exit time && old time < exit time)"). A crossed check would handle both directions (being careful not to be off-by-one when going backwards, which my earlier suggestion was):

    (previousTime < ExitTime && currentTime >= ExitTime) ||
    (previousTime > ExitTime && currentTime <= ExitTime)

    This doesn't care which direction time is moving, or if the play direction is changing dynamically.
     
  16. gumboots

    gumboots

    Joined:
    May 24, 2011
    Posts:
    298
    @Mecanim-Dev The only thing is ideally you could keep an exit time of 1 to allow for interrupting an animation. So a normalised exit time of 1 means 'the end', it doesn't care whether that's the start or the end, and that's dictated by the final calculated animation speed. Like you said, if the speed was changed part way through you'd need to recalculate the current time position. But that should solve the issue, right?
     
  17. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    It won't work for all case. Like I said if you start to play the clip, then half way you change the speed multiplier from positive to negative, the time will start to go backward so what would be the end of the clip in this case?
    Also we need to handle case with transition that have an offset, there is nothing that could prevent someone to transition at 50% of the clip, in this case what would be the end of the clip? it not clear.

    In your particular case that would work because the motion is symetric, the motion stop where it started, but it not always the case

    So we won't change the meaning of exit time, 0 mean begining of the clip, 1 mean end of the clip
    The change that we propose only affect how the condition for exit time is handled.
    So in your case you will still needs two transition
    AnimateOut -> Animate Sideways: exit time=1 && AimSpeed > 0
    AnimateOut -> Test Animation: exit time=0 && AimSpeed < 0
     
    Regent2203 likes this.
  18. Regent2203

    Regent2203

    Joined:
    Oct 13, 2017
    Posts:
    2
    @Mecanim-Dev
    Hello, have this thing been implemented already in new versions? I need it now)
     
  19. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    yes it should work now in 2018.1