Search Unity

Animancer - Less Animator Controller, More Animator Control

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

  1. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    I don't quite understand the issue you're having (possibly because I haven't worked with multiple controllers myself). Perhaps you could give a more specific example?
     
  2. joshcamas

    joshcamas

    Joined:
    Jun 16, 2017
    Posts:
    1,278
    Here's an example.

    Module 1: base movement (walking, jumping, crouching, swimming, etc)
    Module 2: weapon (aiming, attacking, etc)

    When using controllers, mixing between these is super easy. Just have two controllers, and mix em! In a way, a controller state acts as a "parent state" with a whole bunch of logic underneath.
    But if I say... wanted to use code logic instead of controllers, this is no longer the case (as far as I can tell). There is no way of having a "parent state", and instead it's just a collection of clips and stuff, with no easy way to modularize it (group it). This makes crossfading between "modules" tough.

    That's just something that's on my mind. For now, I've just decided to use controllers.

    Speaking of that, is there any way to remove a state from a layer other than disposing it? I'm crashing whenever I try to dispose my controllerstate for some reason.

    Also, so sorry for all of the annoying questions
     
  3. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    If I'm understanding you correctly, there are two ways you could approach something like that:
    1. Use layers and fade them in and out. You could make an extension method along the lines of CrossFadeLayer(this AnimancerController animancer, int layer, float fadeDuration) for convenience.
    2. Or do pretty much the same thing with a MixerState. Either inherit from ManualMixerState and add a CrossFade method or do it as an extension method.
    You should be able to call call SetParent(null, whatever) on any state to disconnect it, but disposing it definitely should not cause a crash. I just tested it in Unity 2018.1 and it worked fine. Which version are you using?
     
  4. joshcamas

    joshcamas

    Joined:
    Jun 16, 2017
    Posts:
    1,278
    I'm using 2018.3. I tried setting parent to null, which works, except it throws this error:

    Code (csharp):
    1. NullReferenceException: Object reference not set to an instance of an object
    2. Animancer.AnimancerState.get_ParentPlayable () (at Assets/Plugins/Animancer/Internal/AnimancerState.cs:92)
    3. Animancer.AnimancerNode.ApplyWeight () (at Assets/Plugins/Animancer/Internal/AnimancerNode.cs:342)
    4. Animancer.AnimancerNode.Update (System.Boolean& needsMoreUpdates) (at Assets/Plugins/Animancer/Internal/AnimancerNode.cs:539)
    5. Animancer.AnimancerState.Update (System.Boolean& needsMoreUpdates) (at Assets/Plugins/Animancer/Internal/AnimancerState.cs:899)
    6. Animancer.AnimancerNode.UpdateNode (System.Boolean& needsMoreUpdates) (at Assets/Plugins/Animancer/Internal/AnimancerNode.cs:501)
    7. Animancer.AnimancerPlayable.PrepareFrame (UnityEngine.Playables.Playable playable, UnityEngine.Playables.FrameData info) (at Assets/Plugins/Animancer/Internal/AnimancerPlayable.cs:1505)
    After rewriting the get_ParentPlayable function to not throw this error, it ends up throwing other similar errors as well
     
    Last edited: Jun 21, 2019
  5. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    That looks like it will need a bit of refactoring to fix so I won't be able to do it until I'm done with the Inspector Gadgets update. For now you could just leave the Weight at 0 or try calling DisconnectFromGraph instead, depending on your needs.

    You should definitely be able to dispose the state though. I'm not getting any errors in 2018.3 either. I tested it in the Locomotion/Linear Blending example by adding the following method to the LinearBlendTreeLocomotion script:
    Code (CSharp):
    1. private void Update()
    2. {
    3.     if (Input.GetKeyDown(KeyCode.C))
    4.         _Controller.State.Dispose();
    5. }
     
  6. joshcamas

    joshcamas

    Joined:
    Jun 16, 2017
    Posts:
    1,278
    I'll look into it more tomorrow! I'm guessing it has something to do with controllers, I'll do a few tests on a empty project.

    For now I'll do as you say, and just disconnect the state without destroying it. Works for now! :D
     
  7. joshcamas

    joshcamas

    Joined:
    Jun 16, 2017
    Posts:
    1,278
    Welp, I cannot replicate it. So for now I'm just going to ignore it LOL
     
  8. AndreasO

    AndreasO

    Joined:
    Sep 25, 2012
    Posts:
    90
    Hi, any advice on how to use LOD Group and Animancer components together? I have three LODs of a character each with an Animator component that I want to change to use Animancer. Should I use one Animancer component on the root game object or three components? How would that work when LOD is changed by LOD Group?

    EDIT:
    What I want to achieve is to synchronize my model animations despite the fact that it uses LOD. Right now the model will stop animating when the LOD changes. I couldn't really find helpful information on that topic so I hope someone here has an idea how to do this correctly. :rolleyes:
     
    Last edited: Jun 22, 2019
    joshcamas likes this.
  9. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    I've never used LODs before, but whatever approach works for regular Animators should work the same with Animancer. The hierarchy pictured below the answer here looks like how I imagine it would work, so the LODs can swap out the renderer without the animation system knowing or caring about it, it just animates the bones and whichever renderer is currently active reacts to those bones as usual.
     
  10. joshcamas

    joshcamas

    Joined:
    Jun 16, 2017
    Posts:
    1,278
    I'm guessing your LOD swapping also either swaps the bone structure and/or the animator component. This of course is unneeded. Simply LOD the skinned mesh renderers.
     
  11. AndreasO

    AndreasO

    Joined:
    Sep 25, 2012
    Posts:
    90
    Thanks, will try that out when I'm back at my computer.
     
    joshcamas likes this.
  12. frostymm

    frostymm

    Joined:
    Jun 21, 2013
    Posts:
    34
    Just nabbed the pro version and so far I'm digging it. Getting animation info from the default animator was a huge pain in the butt and I need to set up modular animations for my game. I replaced all of my animator specific code really easily but I ran into a bit of an issue. This only happens on one of my animations and only when trying to switch from another specific animation.

    I've got an idle animation that I call with AnimancerComponent.Play(clip) and it works flawlessly when transitioning from all my animations except my landing animation. If I call play(Idle) while my landing animation is happening, nothing happens. The function is receiving the exact same arguments, it just doesn't work while the landing animation is playing. Any ideas as to why that might happen?
     
  13. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    I'm glad you like it.

    I can't think of any reason why something like that would happen. Unlike an Animator Controller, Animancer doesn't impose any limitations on which animations can transition into each other.

    Try putting in a Debug.Break(); to pause the editor when you try to play the idle and select the object in the hierarchy so the inspector shows the details of its animations. Then step through it frame by frame to see if you can narrow down the issue.
     
  14. frostymm

    frostymm

    Joined:
    Jun 21, 2013
    Posts:
    34
    I did not realize the inspector updated during runTime with your state info, that's super nifty. Found the problem, I wasn't stopping a coroutine properly. Thanks for the quick response!
     
  15. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Now that Inspector Gadgets v6.0 is out of the way, I've started working on another Animancer update (probably v3.1, not v4.0) and I'm trying to improve the introductory text for the store page to hopefully give a better idea of what Animancer actually does and get people interested enough to keep reading. So I'd like to ask what everyone thinks about the following ideas:

    #0 (the current wording)

    Animancer gives you the ability to play and control animations dynamically at runtime using simple scripts instead of manually configuring inflexible Animator Controllers that encourage bad programming practices.

    Check out the Documentation and Examples to get started.

    #1

    Animancer gives your scripts more power to play and control animations. You can just play AnimationClips directly, without needing to set up an Animator Controller. Or you could mix an Animator Controller with separate animations depending on the situation, or even mix multiple Animator Controllers.

    Grab Animancer Lite for FREE and check out the Documentation, Frequently Asked Questions, and Examples to get started.

    #2

    Animancer is a powerful animation system which gives your scripts more control, flexibility, and protection from bugs.

    You can avoid many of the common problems with Mecanim by directly referencing the AnimationClips you want to play, without ever creating an Animator Controller. Or you could use a hybrid approach where an Animator Controller handles some things with separate AnimationClips for other things, or you could even mix multiple Animator Controllers. You get total freedom to structure your project to suit your needs.

    It has detailed documentation, lots of examples, great performance, high compatability with other plugins, you can try out all the features for FREE with Animancer Lite, and you get the full source code with Animancer Pro.

    -----

    Which one do you prefer? Feel free to rearrange them or make any other suggestions you can think of.
     
    Last edited: Jul 26, 2019
  16. slimshader

    slimshader

    Joined:
    Jun 11, 2013
    Posts:
    187
    Amazing asset, thank you for giving us a way out from Mecanim hell. As I am just learning about Avatar Masks and animation layers, what would be Animancers version of [this] article
     
  17. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Masks and layers are basically the same as in Mecanim, just that you set them using code instead of doing it manually in an Animator Controller.

    There's a documentation page and an example that explain them.
     
  18. slimshader

    slimshader

    Joined:
    Jun 11, 2013
    Posts:
    187
    I went through all of the (fantastic) docs but I was curious about specific case and specific Animation Controller conversion to Animancer, for example how to handle usage of boolean animation properties in combination with "has exit time" parameter (that is blocking clip restart).

    Linked blog post uses free assets and is hosted on GitHub to demonstrate things. Maybe a fork of Animancer (free that is) equivalent ;) ?
     
  19. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Animancer doesn't have pre-defined parameters or transitions like that. Instead of setting a parameter that might cause a transition to another animation, you just tell it to CrossFade to that animation. And there are a few different want of Waiting For Animations to finish.

    I'm not sure what you mean by that. Animancer Lite lets you try out all the examples for free.
     
  20. slimshader

    slimshader

    Joined:
    Jun 11, 2013
    Posts:
    187
    I know, that is why I am asking your advice on how to code Animancer version of the controller built in the article.

    I mean maybe you could look at the project / article and show how to provide same functionality with Animancer. Would be very helpful for newcomers to understand better how to translate from Animator Controller graphs to Animancer equivalents. Article is very short but covers popular use case so I believe it would be valuable learning resource.

    As I mentioned, I went through the docs, but I don't see solution to the "has exit time" in relation to boolean transition parameter. It is one thing to know when clip has finished and not allowing it to be interrupted. If you would take a look at graph in the article I linked it woul dbe much easier to dicuss
     
  21. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Examples of how to translate/deconstruct Animator Controllers to use Animancer are on my list of things to do for the update I've just started working on.

    In the meantime, if you have any specific questions I'd be happy to answer them. That would also help me get a better idea of what needs to be explained when I make those examples.
     
  22. slimshader

    slimshader

    Joined:
    Jun 11, 2013
    Posts:
    187
    Example I asked help with is actually small and specific, maybe a good idea to take a look for inspiration for an update?
     
  23. slimshader

    slimshader

    Joined:
    Jun 11, 2013
    Posts:
    187
    Specific question #1 then:
    There are 2 animations, Idle and Walk.
    Unit can only be in 1 of those states
    Both animations have looping enabled
    FSM logic is this: after requesting it, unit can go from Idle to Walk, change should be fairly immediate (so I assume CrossFade() is ok in that case)
    -> after requesting also, unit can go from Walk to Idle BUT only after full Walk cycle is finished.

    I am trying to implement it using I(Owned)State<T> where Idle and Walk are those specific states, playing respective animations OnEnterState(). I am guessing I should implement CanExitState() for Walk to make sure cycle is finished but not sure how to do that. I am then planning to use InputBuffer to make sure Idle state change request is not lost.
     
  24. slimshader

    slimshader

    Joined:
    Jun 11, 2013
    Posts:
    187
    I think there is a bug in InputBuffer. In the TrySetState() (line 59 InputBuffer1.cs). Code tries to enter buffered state first. It then compares if buffered state is null with the current state which is also null, resulting in the false positive and returning true without ever trying to enter passed state.

    While at it, could InputBuffer.Update() optionally take elapsedTime as parameter (would be used instead of Time.elapsed/unscaledElapsed)
     
  25. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    You could use the animation's OnEnd callback. For looping animations it gets triggered each time it passes the end of the loop. So it can set a bool to indicate that the animation just ended, then you reset it in the next Update.

    Or you could have CanExitState always return false and check the InputBuffer in the OnEnd event itself. If it's trying to return to Idle, just use ForceSetState to skip the "Can" checks. Doesn't seem right to be checking input in the movement script though, so I'd probably go for the first idea.

    You're right, that doesn't make sense. I can see why I never noticed though; it would still set the BufferedState so it would just enter that state as soon as it gets Updated and only be one frame late.

    I've attached the fixed file, including a new Update(float deltaTime) method.
     

    Attached Files:

  26. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Another way to set the flag for CanExitState would be to use Animation Events. That way you could set it twice per loop (once per step).
     
  27. slimshader

    slimshader

    Joined:
    Jun 11, 2013
    Posts:
    187
    I am actually trying to simulate it with CanExitState() => _clipState.NormalizedTime % 1 > .9, what do you think? Looks like "has exit state" emulation with Animancer is not a trivial thing to reproduce


    That was the problem tho, BufferedState was NOT assigned because of early return making buffer unassigned and not updated in the next Update() phase. I looked at the code because state was never entered, even when Updating and long timeouts
     
  28. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    I don't like that solution because if the animation is short enough, speed high enough, and delta time large enough, it could skip over that window. But that's obviously pretty unlikely, so it should generally be reliable enough.

    It's definitely worth more consideration though. Could probably do with an example scene of its own. Can you think of anything else you would use a conditional exit time transition for?
     
  29. hungrybelome

    hungrybelome

    Joined:
    Dec 31, 2014
    Posts:
    336
    Last edited: Jul 7, 2019
  30. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Unfortunately the Playables API doesn't support mirroring individual playables, but you can still mirror animations using their import settings. So if you need both mirrored and unmirrored of an animation, you could just make a copy of the animation. Obviously that would waste a bunch of RAM and loading time, but it would actually be slightly more efficient over time because mirroring requires some additional calculations.
     
    hopeful likes this.
  31. hungrybelome

    hungrybelome

    Joined:
    Dec 31, 2014
    Posts:
    336
    Darn! My animations are embedded within the model file, so I don't think I could create a mirrored copy unless I duplicate the entire .fbx file. And I'm worried that Unity would include the entire duplicated fbx file into the build, even if I use the original model's avatar. Oh well
     
  32. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    I don't think it's possible to directly duplicate an animation in a model, but you should be able to just add another animation in the Animations tab and set it to the same start and end frame. You can also select the clip asset itself and use Ctrl + D to create a copy which will no longer be part of the model.
     
  33. hungrybelome

    hungrybelome

    Joined:
    Dec 31, 2014
    Posts:
    336
    Thanks for the tip!
     
  34. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    A few people have recently asked me for clarification about where Animancer fits into the development process, so I've made a diagram to replace the old Mecanim vs. Animancer introduction:
    Any feedback is welcome.
     
  35. hopeful

    hopeful

    Joined:
    Nov 20, 2013
    Posts:
    5,686
    What about Animancer versus Playables? What does Animancer do for you that you don't already get as a result of using Playables?
     
  36. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    That's hard to answer because the Playables API is so low level that it barely does anything without a bunch of effort on your part and since Animancer is built on that API, it's just a question of where you want to draw the line between "the Playables API can do this" and "this is a feature added by Animancer".

    I didn't include the Playables API in the Feature Comparison table because it wouldn't even have half a dozen boxes ticked on its own and even then some of those would be up for discussion. For example, "No need for Animator Controllers": sure, Playables let you play animations without them, but does that actually count when you need to implement something as simple as Cross Fading yourself?

    Using the Playables API allows you to make an animation system (like Animancer). Using Animancer allows you to actually make a game. It's an apples to oranges comparison.
     
  37. slimshader

    slimshader

    Joined:
    Jun 11, 2013
    Posts:
    187
    The old one was more informative to me thb (programmer here so like the details). Would love to see real-life examples of Mecanim/Animator controller => Animancer examples especially with usage of layers, masks and IK (say strafing wile shooting weapon)
     
  38. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    So this does use mecanim? I'm assuming so, but actually what I'm looking for is a layer over legacy. So thought I'd ask just in case this can use legacy.
     
  39. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    No, it uses the Playables API which is the same low level system as Mecanim.

    Animancer is much closer to Legacy in the way it lets you play animations freely without setting up Animator Controllers, but it still gets to use features like Humanoid animation retargeting, root motion, and IK, which aren't available in Legacy.

    Whatever you're using Legacy for, Animancer can probably do it better.
     
  40. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    Mecanim is roughly 2x the main thread usage if you are just using features supported by legacy. For our use case that's very significant and why we are on legacy.
     
  41. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    You're probably comparing legacy to a Humanoid rig, but humanoids have significant extra costs to support retargeting. When I tested a Generic rig against Legacy, the difference was much smaller.

    But if performance is your main priority, then Legacy is still king. Animancer is only slightly faster than Mecanim in most cases.
     
  42. JoeStrout

    JoeStrout

    Joined:
    Jan 14, 2011
    Posts:
    9,859
    I need to mirror the animations (i.e. flip them left/right) when my character is facing left instead of right. With Mecanim I think I would just set mirror to true. But I can't find any such property in Animancer.

    Is there any way to solve this in Animancer, short of having the animators make flipped versions of every animation? :eek:
     
  43. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
  44. 00christian00

    00christian00

    Joined:
    Jul 22, 2012
    Posts:
    1,035
    Hi, I just bought your assets and love it. Seem very polished and clear.
    I couldn't find it in the docs, how do I manually control rootmotion with animancer?
    With mecanim I would use OnAnimatorMove and animator.deltaPosition and animator.deltaRotation.

    EDIT
    Nevermind, I just saw that the same code works here, awesome.
     
    Last edited: Aug 6, 2019
  45. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Yeah, all root motion stuff should work exactly the same.
     
  46. 00christian00

    00christian00

    Joined:
    Jul 22, 2012
    Posts:
    1,035
    Hi,
    In some part of the game I need to control the animation playback manually by setting the normalized time in LateUpdate.
    How do I do that with Animancer?
    I saw that I can set normalizedspeed to zero and normalizedtime with animationclips, but the same code doesn't works when using animatorcontrollers and blendtrees.
    Is it a bug?
    It seem that everything is overridden by some code later.
     
  47. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    The Speed and Time page in the introduction compares how you can control those values in a regular Animator Controller with how you can do it when playing AnimationClips through Animancer. The Spider Bot example covers the Animancer side in more detail.

    Playing an Animator Controller inside Animancer works basically the same, but any methods you would normally call on the Animator (SetFloat, GetCurrentStateInfo, etc.) will instead need to be called on the ControllerState.
     
  48. 00christian00

    00christian00

    Joined:
    Jul 22, 2012
    Posts:
    1,035
    I get the usage, but in reality it doesn't work.
    Let's say for example I want to change the playback speed of the animation playing in the animator controller.
    Setting the speed variable of the controllerState does nothing. Same thing for Time.
     
  49. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    That's because ControllerState.Speed and Time are inherited from the base AnimancerState class. They do technically work, it's just that they control the root AnimatorControllerPlayable which manages its own states internally and doesn't propogate those values onto its contents.

    That's why you have to control the contents of the Animator Controller through the ControllerState.Playable in the same way you would normally control it through an Animator. So you do the things described in the Mecanim column on the Speed and Time page, but instead of calling methods on the _Animator reference, you use your controllerState.Playable.
     
  50. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    I don't know anything about your controllerState class, I'm referring to the Animancer.ControllerState class which is part of Animancer and has a Playable property.

    The Animator Controllers page which I linked earlier has some code that shows how you can control things via that property. I'm not sure what other code you might be looking for.