Search Unity

Animancer - Less Animator Controller, More Animator Control

Discussion in 'Assets and Asset Store' started by Kybernetik, Oct 8, 2018.

  1. 00christian00

    00christian00

    Joined:
    Jul 22, 2012
    Posts:
    1,035
    I deleted the message cause I had found it.
    I wasn't seeing the Playable variable cause it's not exposed with FloatControllerState.Serializable.
     
  2. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    I'm not sure what you mean. FloatControllerState inherits from the base ControllerState so it has the exact same property. If you take a look at the source code you can see that FloatControllerState.Parameter is just a wrapper around its Playable.GetFloat and SetFloat, but the Playable can still be accessed publicly.
     
  3. 00christian00

    00christian00

    Joined:
    Jul 22, 2012
    Posts:
    1,035
    Sorry I meant FloatControllerState.Serializable.
    I found the code anyway, FloatControllerState.Serializable.State.Playable, right?
    It seem to work that way.
     
  4. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Yeah, that's correct.
     
    00christian00 likes this.
  5. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Yes it is.

    The Directional Sprites/Character Controller example explains how you can use sets of 4 or 8 animations individually, which is particularly useful for sprite animations.

    Or if you want to blend between them to allow movement in any direction you can either use a ControllerState to play an Animator Controller containing a Blend Tree or use an AnimationMixer which works similarly but is more code based. The differences between them are described here.
     
  6. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Animancer v3.1 is out now.

    I've recently been getting quite a few questions from people who are in the middle of a project built around Animator Controllers where they would like the ability to play separate AnimationClips alongside them as well as people who are unsure of how to go about deconstructing their Animator Controllers to use Animancer instead.

    So that's the main focus of the v3.1 update:
    • Added a HybridAnimancerComponent with an example to demonstrate how to use a default Animator Controller for some things and separate AnimationClips for others.
      Added another example which renames the Animator Controller based player character from Unity's 3D Game Kit to use Animancer instead.
    • Added a Root Motion example to demonstrate how serializables can be used to indicate whether each animation should use root motion or not.
    • Added AnimancerUtilities.EditModePlay to more easily play animations in Edit Mode (it was already possible, this just makes it a bit easier).
    • Added a Toggle Looping Context Menu Function to AnimationClip assets (since you can't multi-edit them).
    See the Change Log for the full list of changes.

    I've also added a quick Survey to the Read Me to hopefully get some feedback about which features people actually use and what I can improve in the future. As always, any other feedback and questions are appreciated.
     
  7. 00christian00

    00christian00

    Joined:
    Jul 22, 2012
    Posts:
    1,035
    I think I found a bug.
    If I use the same animatorcontroller in two different scripts, the state variable of the controllerstate does not get set because it is set only on creation, and animancer find the key in the dictionary because the key is the controller itself.
    I cannot set the state variable manually, because it is protected.
    Should I not use the State variable at all and only use the one returned from the Transition method?
     
  8. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Yeah, if you are using a single Serializable in multiple objects then you will need to use the returned state which is specific to the object you just called Transition on because the Serializable.State property will only hold the most recently created state. So if it's a ControllerState.Serializable, you'll probably need to cast the returned state to ControllerState in order to do what you want with it:

    var state = (ControllerState)animancer.Transition(serializable);
    state.Playable...

    It's not really a bug, just a general limitation that I haven't found a better solution for.
     
  9. Ony

    Ony

    Joined:
    Apr 26, 2009
    Posts:
    1,977
    I've got a question about the new HybridAnimancerComponent.

    The examples provided for each component are much appreciated and cover things in depth, but with this particular component, can you provide a more concise explanation that doesn't involve a mini-golf game, state machine brains, creature scripts, etc? If the component absolutely needs these various other scripts to work then that's fine, but I suspect there's a relatively simpler way to set this up.

    I've already got my locomotion controller moving the character around, so I don't need the keyboard brain and all that.

    Basically I want to have control to do one of the following:
    • stop using the regular locomotion Animator controller and instead play an Animancer clip.
    • stop playing the Animancer clip and go back to the regular locomotion Animator controller.
    Is all the extra overhead necessary for this hybrid component to work? Maybe I'm missing something. It's been a long week. :)
     
    Last edited: Aug 18, 2019
  10. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    No, it definitely doesn't require any of that other stuff. It's more that I wasn't able to think of anything meaningful to actually demonstrate it with simpler concepts. Maybe I'll just add a new Basics example for it.

    To play a regular clip outside the Animator Controller, you just call Play(clip) (or CrossFade, etc.) like in any of the other examples.

    To go back to the Animator Controller you can either call Play("State Name") or if you want to go back to it without actually changing it internally you can call PlayController() (which I'm thinking of renaming to TransitionToController next version).

    If you take a look at the HybridAnimancerComponent script itself it will probably help you understand how it works.
     
    Ony likes this.
  11. Ony

    Ony

    Joined:
    Apr 26, 2009
    Posts:
    1,977
    Awesome, thank you. :)
     
  12. spacefrog

    spacefrog

    Joined:
    Jun 14, 2009
    Posts:
    734
    I got some strange playback speed discrepancy when i play my sprite anim clips in Edit (non-playback) mode using AnimancerUtilities.EditModePlay( _animancer, clip, false );
    I've set up sprite animations using sampelrate of 72 and a sprite keyframe on each 6th frame. The result is a 12 fps animation. The reason for this specific setup is to be able to seamlessly swap ( or mix, or complement ) the 12fps anims with 24fps animclips later on ( 72 is the lowest number that is dividable by both 12fps and 24fps)
    While in Playmode animancer plays everything perfectly fine using the correct animaionclip settings, but the In-Editmode-playback seems to ignore samplerate completly
    Any explanation why that might be the case ?
    I can simply disable editmode playback, but i thought it would be a nice tool to have ...
     
  13. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    I just tried it in the Directional Sprites/Character Controller example and it worked fine. Changing the sample rate on the MedicalExaminer-IdleDown animation changes its speed exactly as expected.

    Which version of Unity are you using? There were some issues with Edit Mode playback before 2018.3 so that might be related to what you're seeing.

    Uh ... 24 and 48 are both divisible by 12 and 24 as well.

    And if I'm understanding you correctly, couldn't you just put a keyframe every frame and use 12 and 24 fps sample rates to get the exact same result?
     
  14. spacefrog

    spacefrog

    Joined:
    Jun 14, 2009
    Posts:
    734
    Haha - of course ... seems i forgot the real reason why i chose this strange sampling rate ... :confused:
    regarding the "every frame a keyframe" thing

    It's a pipeline thing: we fill up sprite animationclips with higher "time" resolution ( = more sprites per second ) on the go and as we need . Some animations will require this in respect of smooth playback. some other might get a few additional inbetweens here and there and possibly not through the whole clip range. Frame exact events are maintained this way too
    And this all while keeping animation references in the ongoing project intact ( we are talking about 30k individual sprite frames ) . This is all is done using custom importers, which convert xml based description into the sprite animcationclips:
    If the clip already exists, and is referenced in the project, inbetween frames are simply filled up, without breaking the rest of the animation and the references into the project...

    But i will re-evaluate some of the decisions made early on and try to lock down the strange framerate issue when not in playback mode
     
  15. spacefrog

    spacefrog

    Joined:
    Jun 14, 2009
    Posts:
    734
    BTW: i use Unity 2018.4.5f1
    I updated my animclip creation code to use a samplerate of 24 ( don't really know why i thought i had to go with 72, but luckily this involved just changing one const declaration ... )
    Anyways - problem still persists - animclips in edit mode ( non playback ) play back at much higher pace , including of course anim event triggering, which i mainly use for sound triggerring ATM... so the issue can immediatly be noticed just by those sounds ...

    And regarding your own examples working, i think they play at the wrong framerate in edit mode too:
    To verify, look at your med-examiner scene / anims. The med-examiner clips in your Character Controller example scene are set to sample rate 1 ( one sprite per second ). if you select the sprite in the scene, go to the regular (Unity) animation window , and press "Play the animation clip" there you can see them clearly play at 1 fps.
    Now select your mediacal examiner parent object watch how the edit mode playback kicks in: this one clearly plays much faster than the one from the original Unity animation preview ..
    Hope this helps ...
     
  16. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Found the problem.

    In 2018.1, animations in Edit Mode didn't play on their own so I set up a system to constantly evaluate them, but evidently somewhere between then and 2018.3 they made Unity play them so now you're getting both the regular playback and my updates at the same time, giving double speed.

    A simple fix is to comment out line 1688 in AnimancerPlayable (the call to playable.Evaluate((float)deltaTime);). Or you can just comment out the entire contents of that method, but then the Inspector won't update smoothly (in Edit Mode).
     
  17. spacefrog

    spacefrog

    Joined:
    Jun 14, 2009
    Posts:
    734
    Awesome !
    thanks a lot for the fix, will test ASAP, right now i need some well earned nap to freshen up my batteries ...

    Update: anim playback in editmode works like charm now :)
     
    Last edited: Aug 20, 2019
  18. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    @Ony

    Just in case you're still having any trouble, I've added a Basics/Hybrid System example to explain how to use a HybridAnimancerComponent as simply as possible. Let me know if there's anything confusing about it.
     
    Ony likes this.
  19. Ony

    Ony

    Joined:
    Apr 26, 2009
    Posts:
    1,977
    Oh, fantastic, thank you. :) I got it figured out the other day no problem, but this new example should help to clarify things even more.

    I'm really loving Animancer! It's exactly what I need for swiftly and easily handling of all of the animations our games use. I'll leave a review after we get this latest build out.
     
    hopeful likes this.
  20. PlayingKarrde

    PlayingKarrde

    Joined:
    Aug 19, 2013
    Posts:
    38
    Hi there, I just bought Animancer and I'm really liking it so far. I have a question (bug?) though. I am trying to integrate it with Puppetmaster and it works fine if I have Animancer set to Normal for the Update Mode, but Puppetmaster requires the animator to be set to Update Physics. However, when I do this the animation is stuck in it's reference pose so clearly something is going wrong.

    I thought maybe it's something to do with the update order but I can't figure out a good solution. Do you have any ideas?
     
    hopeful likes this.
  21. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    I'm currently on mobile and won't be back at my computer for a few days so I can't test it at the moment, but from memory the only issue I've had with update modes is that in older versions (before 2019 I think) changing the mode after start up had no effect.

    For something to be stuck in the reference pose, my first guess would be that you're using a generic rig and trying to play a humanoid animation on it. Have you looked at the object's inspector? There are a few warnings it can show at certain times.
     
  22. PlayingKarrde

    PlayingKarrde

    Joined:
    Aug 19, 2013
    Posts:
    38
    I am using a generic rig yes but my animations play fine in any other mode. They aren't authored for a humanoid rig nor do they have their settings to it. The t-pose effect only occurs when I have Update Physics set in the Update mode AND have Puppetmaster enabled. With update set to normal it will play fine, or if I disable Puppetmaster, it plays fine. So I'm not sure where the issues lie exactly.
     
  23. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    That's really strange. I don't think the issue is Animancer itself because none of my code directly deals with applying the animated values, that's all handled under the hood by the Playables API so there isn't really anything you could strip out to narrow down the problem. I don't even know if there's a function I could intentionally use to return to the default pose.

    I'm not familiar with Puppetmaster though. Are you able to modify the source code to figure out what the problem might be?
     
  24. PlayingKarrde

    PlayingKarrde

    Joined:
    Aug 19, 2013
    Posts:
    38
    Yes potentially. I might ping them instead then and see if they might have any insights as to what's causing the issue. Thanks.
     
  25. joshcamas

    joshcamas

    Joined:
    Jun 16, 2017
    Posts:
    1,277
    I've been struggling to get something working for hours, and I have absolutely no clue what's happening, so posting here is just out of sheer desperation.

    I have a controller playing on a layer, and I want to crossfade into playing another clip instead. This works! For one character. For the other it doesn't, and my mind is being blown since I cannot comprehend why this is the case. It's the exact same code, with supposedly the exact same state. For reference, it's working for my thirdperson character but not the first person.

    I also tried not even crossfading at all, and instead just setting the weight of the controller to 0, and setting the weight of the new clip to 1, then playing it. Still doesn't work. Instead, it simply acts as if no clip is playing. The interface acts like it is playing. If I drag and drop a clip into the layer manually, then it works.

    What in the world could be going on? What would stop this clip from playing even if the values in the inspector look correct? (playing, weight, etc)
     
  26. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Are you by any chance using a Serializable that is being shared between multiple objects (such as if it's defined in a ScriptableObject)? If so, you may be running into the issue 00christian00 had back here.

    Otherwise if the Inspector is showing an animation playing and it's the correct type of animation (humanoid anim on humanoid rig or generic anim on an appropriate generic rig) then I can't think of any other reason why it wouldn't be working.

    Are you able to play other animations on that layer? Does the problem still occur if you don't use layers?

    If you want to upload a small repro project, I'll take a look and see what I can make of it.
     
  27. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    @00christian00

    I've started working on Animancer v4.0 and I thought of a solution to the problem of sharing serializables between multiple objects. Basically, I would split the State property into 3:
    • State works as it does now, but if you use the serializable on a second object it gets flagged so that any more attempts to use that property will throw an exception to explain how it all works.
    • RecentState is the state from the object that the serializable was most recently used on (basically the same as using the state returned by the Transition method).
    • RecentStateCast is just a wrapper around RecentState that does the type cast for you (since it won't always be needed).
    Thoughts?
     
    Tymac and slimshader like this.
  28. 00christian00

    00christian00

    Joined:
    Jul 22, 2012
    Posts:
    1,035
    That could work. I would also add some initialization method, that just do the transition so that it is more clear.
    One method that was missing and had to implement myself was firing a specific animation in the controller without using transitions.
     
  29. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    I'm not sure what you mean. You would still call animancer.Transition(serializable) to trigger the transition and it would still return the state, these properties just give you another way to access that state without needing to cast it.

    I'm not sure what you mean by that either. You can call controllerState.Playable.Play("StateName") to go to any state immediately.
     
  30. joshcamas

    joshcamas

    Joined:
    Jun 16, 2017
    Posts:
    1,277
    Is there a way to make a animator controller state instantly "pop" any transitions they have to their transition goal?
     
  31. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    I've never done anything like that, but you could try calling GetNextAnimatorStateInfo then passing the hash and time into Play.
     
  32. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    I'm considering a slight change in the way cross fading works and figured I'd ask for some other opinions.

    Currently, if you are playing an idle animation and call CrossFade(walk, 0.5f), that fade will take 0.5 seconds. Then if you wait 0.25 seconds so the fade is half done and call CrossFade(idle, 0.5f), the second fade will still take 0.5 seconds even though it is already half playing that animation, it will simply fade slower. This is how it works in Mecanim, Simple Animation, and probably Legacy.

    It also has a special case for repeated calls so that if you call CrossFade(walk, 0.5f) every frame, it will see that the existing fade from the previous frame will complete faster than the new one so it will continue the existing fade instead of constantly slowing down the fade speed to only finish 0.5 seconds after the last call you make.

    The change I'm considering would still have the initial CrossFade(walk, 0.5f) take 0.5 seconds, but then if you wait 0.25 seconds and call CrossFade(idle, 0.5f), the second fade will calculate the fade speed as if its weight had to go all the way from 0 to 1 even though it is already at 0.5 weight (half faded out from before), so the second fade will go at the same speed and therefore only take 0.25 seconds to complete. This seems like a more elegant solution because it avoids the need for that special case, but I'm hesitant to just make the change because no other system works like that so there might be a good reason why not.

    Thoughts?
     
  33. ratking

    ratking

    Joined:
    Feb 24, 2010
    Posts:
    350
    Is it possible to somehow support both, with a simple, well documented public bool switching between both cross fade functionalities?

    TBH I often use the repeated calls, especially when doing small projects, and fully expect it not to change anything about the fade speed.
     
  34. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Repeated calls to fade to the same animation will continue the same initial fade with either approach, it's just that the current implementation only does so by specifically checking for it where the proposed change would naturally achieve that result. I do that all the time as well and definitely wouldn't want it to work any differently.

    The only difference would be when interrupting a fade out of an animation to have that animation fade back in. The current implementation would slow down the fade so that it always takes the specified amount of time even if it were only going from 0.9 back to 1 where the proposed change would have it fade at full speed and complete that small fade much faster even though the caller would still specify a fade duration rather than a speed.

    Supporting both would be easy enough, though I'd probably do it as an alternate method you can call rather than an Inspector field. I'd just need to think of an appropriate method name for whichever one isn't the default.
     
  35. ratking

    ratking

    Joined:
    Feb 24, 2010
    Posts:
    350
    I understand now, thanks! The change sound sensible and I support it, but maybe comparison GIFs between the two methods would help more people to decide what is best.
     
  36. ratking

    ratking

    Joined:
    Feb 24, 2010
    Posts:
    350
    I think I found a bug with CrossFadeFromStart(). I have this code:

    Code (CSharp):
    1. public class AnimTest : MonoBehaviour {
    2.     [SerializeField] Animancer.AnimancerComponent anim = null;
    3.     [SerializeField] AnimationClip clip = null;
    4.  
    5.     void Update() {
    6.         if (Input.GetKeyDown(KeyCode.T)) {
    7.             anim.CrossFadeFromStart(clip);
    8.         }
    9.     }
    10.  
    11.     void OnGUI() {
    12.         if (anim == null || anim.CurrentState == null || anim.CurrentState.Clip == null) { return; }
    13.         GUI.Label(new Rect(10,10,300,100),
    14.             anim.CurrentState.Clip.name + ": isPlaying:" + anim.IsPlaying(clip) + " Time:" + anim.CurrentState.Time);
    15.     }
    16. }
    "clip" is an AnimationClip that does not loop. When I press T the first time, anim.IsPlaying(clip) returns true as it should. The second time, it returns false. After that, true again. And so on, it's alternating between true and false.

    Of course it might also be that I just oversaw something. In any case, I have to use CrossFadeFromStart() because CrossFade() does not reset the animation. Play().Time = 0f works as it should, but then I'm missing the fading.

    EDIT: Ah, I guess I understand. I need to use anim.IsPlayingClip(clip) because it can happen that there is a new state, playing the clip.
     
    Last edited: Sep 28, 2019
  37. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Yeah, you got it with your edit. IsPlaying only checks the state registered with the given key which is only the first state and won't detect the extra ones created by CrossFadeFromStart while IsPlayingClip will search through all states for any that have that clip (which is obviously less efficient if you don't need it).
     
  38. nicosuave

    nicosuave

    Joined:
    Nov 16, 2015
    Posts:
    5
    Hey Kybernetik,

    Thanks for writing this great package!

    I've tried using Unity's Animation Rigging in 2019.2 for advanced IK in combination with Animancer, but Animation Rigging fails, failing to find the rig:

    Could not resolve 'Rig/BlockAim' because it is not a child Transform in the Animator hierarchy.
    Could not resolve 'Rig' because it is not a child Transform in the Animator hierarchy.

    I tried setting DefaultApplyAnimatorIK and ApplyAnimatorIK both to true on all layers, but this had no effect. What do you know about compatibility between Animation Rigging and Animancer? Is this a Unity or Animancer bug? My understanding is that Animation Rigging uses Playables under the hood, so they _should_ work well together.

    Thanks,
    Nick
     
  39. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    It's on my list of things to look into, but it's a very long list. :)

    The first thing to try would be using a regular Animator Controller so you can determine whether you've set something up wrong or if it's actually caused by Animancer.

    It definitely should be possible to get Animation Rigging to work with Animancer, but whether or not they can work together straight out of the box is likely dependant on what sort of assumptions Animation Rigging makes about the structure of the PlayableGraph and how it inserts itself into that system. It could be expecting things to be set up in a particular way as Animator Controllers do it, but really I have no idea because I haven't looked at it myself.

    Sorry I can't be more helpful.
     
    slimshader likes this.
  40. jh092

    jh092

    Joined:
    Sep 2, 2018
    Posts:
    55
    Hi and firstly thank you for this wonderful asset!!!

    Please can you advise, is there a way to know at what speed to set an animation so that a walking humanoid (for example) does not have sliding feet during a "walk" animation?

    I can play around manually and get the values right but was wondering if there is a way to set the speed precisely so the feet don't slide for walking, running and so forth?

    many thanks

    John
     
  41. ratking

    ratking

    Joined:
    Feb 24, 2010
    Posts:
    350
    Usually I make my walking animation exactly one second long. Then I know that at a character velocity with the magnitude of one I set the animation speed to 1. If I'm running at 5m/s, I set the anim speed to 5. If the character is slowly walking with 0.5m/s, the speed factor is 0.5. And so on.
     
    hopeful likes this.
  42. jh092

    jh092

    Joined:
    Sep 2, 2018
    Posts:
    55
    Ah, understood, thank you so much.

    John
     
  43. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    An animation won't necessarily move at a fixed speed the whole time (for example, a walk with a limp will vary between slow and fast movements), but if it is relatively consistent then the AnimationClip.averageSpeed will tell you how fast it goes (and it's wrapped by AnimancerState.AverageVelocity). So you should be able to just set the state.Speed = desiredSpeed / state.AverageVelocity.magnitude.
     
  44. Lars-Steenhoff

    Lars-Steenhoff

    Joined:
    Aug 7, 2007
    Posts:
    3,527
    Would you be able to make a few simple playmaker actions?
    It seems like a perfect fit for playmaker
     
  45. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Unfortunately I don't have Playmaker so I wouldn't be able to test anything I made and their documentation doesn't look anywhere near good enough for me to be able to make something without testing it.
     
  46. joshcamas

    joshcamas

    Joined:
    Jun 16, 2017
    Posts:
    1,277
    Is there any way to force all current transitions of a animator controller to "pop" to their goal? I switch between third and first person, which causes annoying transition issues, since on activation it starts blending
     
  47. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Isn't that the same question you asked before which I answered at the time?
     
  48. joshcamas

    joshcamas

    Joined:
    Jun 16, 2017
    Posts:
    1,277
    Aw sorry I didn't see this reply aaaa
     
  49. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Hi Everyone,

    I just updated the v4.0 Change Log with my progress so far as well as explanations of some major API changes that I'm considering. Please let me know what you think.
     
    Last edited: Oct 14, 2019
    joshcamas and ikemen_blueD like this.
  50. hopeful

    hopeful

    Joined:
    Nov 20, 2013
    Posts:
    5,685
    To me, the API change seems logical and good.
     
    Kybernetik likes this.