Search Unity

Animancer - Less Animator Controller, More Animator Control

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

  1. ikemen_blueD

    ikemen_blueD

    Joined:
    Jan 19, 2013
    Posts:
    341
    The V4.0 sounds really good. I seriously can't wait. Can I assign Event details in the Inspector, from ClipState.Serializable? Thanks.
     
  2. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Yeah, that's the plan. The Inspector will have a basic interface for it, but you'll generally want to do it in the new animation preview window since it will have more room to show a proper timeline and let you see the exact pose of the model at the event time.

    What are your thoughts on the API changes?
     
    spacefrog likes this.
  3. ikemen_blueD

    ikemen_blueD

    Joined:
    Jan 19, 2013
    Posts:
    341
    Overall, the API changes make senses to me. I often use Transition method, instead of Play or CrossFade. So, 1 method name Play would make less misunderstandings I guess. I hope you give examples how to integrate popular Animations plugins like Final IK, PuppetMaster, Simple Waypoint System, etc. How easy to hook Animancer to follow a path, a curve? I really like the way you write your tutorials. I'm looking forward to see Documentation new updates, new examples.
     
    spacefrog, hopeful and Kybernetik like this.
  4. mons00n

    mons00n

    Joined:
    Sep 18, 2013
    Posts:
    304
    is there any way to play an animation in reverse? Setting the speed to -1 doesn't seem to be doing the trick for me.
     
  5. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    If the animation is looping, Speed = -1 should do the trick. Otherwise if you want to play backwards from the end of the animation, you'll also need to set NormalizedTime = 1 (or Time = clip.length).
     
    hopeful likes this.
  6. Mathius123

    Mathius123

    Joined:
    May 21, 2018
    Posts:
    14
    Hi, @Kybernetik - loving the asset. Can you please point me to the right area in the documentation to learn how to keep my existing Animator Controller, but with the ability to use Animancer to call individual AnimationClips that aren't part of the Animator Controller that override 100% whatever is playing on any of the layers inside the Animator Controller using a custom AvatarMask?
     
  7. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    The newly added Basics/Hybrid System example explains most of what you want. If you want to sometimes play another AnimationClip that 100% replaces whatever the Animator Controller is doing, then that's all you'll need. But if you want to use an AvatarMask so the other AnimationClip only replaces some body parts, then the Layers example shows how to use additional layers and set masks.
     
    hopeful likes this.
  8. Mathius123

    Mathius123

    Joined:
    May 21, 2018
    Posts:
    14
    @Kybernetik - I must be doing something wrong. I added the LayerExample script onto the LowPolyMan, assigned the Animancer Component to both the basic and layered exposed fields (otherwise I get null reference exceptions), assigned the 3 animations, and assigned the upper body Avatar Mask. I created two buttons: one to call ToggleRunning and the other to call PerformAction. When I click Play and then click the ToggleRunning button and then the PerformAction button, the LowPolyMan performs the upper body action, but the lower half just falls through the floor. When the animation is finished, he is stuck in the default floating animation as if the Animator is acting that it has no animation to play.

    When looking at the Animancer Component in the inspector, I have two yellow warnings:

    Base Layer: "The total weight of all states in this layer does not equal 1, which will likely give undesirable results."
    Upper Body: "There are no override layers at weight 1, which will likely give undesirable results."
     
    Last edited: Oct 24, 2019
  9. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    The LayerExample script is very specific to that particular example because it is meant to control both a single layer character and a multi-layer character at the same time for the sake of comparison. It's not surprising that it wouldn't work if you tell it to apply both sets of commands to the same character at once.

    You will need to write your own script that combines the concepts introduced in those two examples. Start with the Hybrid System example to play your Animator Controller with other AnimationClips that override it, then change those AnimationClips to use a masked layer as shown in the Layers example.
     
  10. Mathius123

    Mathius123

    Joined:
    May 21, 2018
    Posts:
    14
    @Kybernetik - thanks for the quick response. I appreciate the help.
     
  11. betaFlux

    betaFlux

    Joined:
    Jan 7, 2013
    Posts:
    112
    Hello. I just wanted to ask if this tool is able to handle directional blending with arcs and strafe animations. Does animancer offer some kind of blend tree alternative? Thanks!
     
  12. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Mixers are covered in the documentation.
     
  13. bonickhausen

    bonickhausen

    Joined:
    Jan 20, 2014
    Posts:
    115
    How easy is it to replicate animations over the network? I take it Unet's NetworkAnimator won't work here, will it?
     
  14. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Yeah, a NetworkAnimator won't work without an Animator Controller. Animancer doesn't have any networking support specifically built in because it doesn't act as a state machine so it wouldn't make sense to synchronise the animations separately from whatever state machine system you use. But if you synchronise your state machines, then they will each play their associated animations in sync as well and Animancer gives you easy access to the animation details (such as time) so you can implement any prediction / interpolation / latency compensation algorithms you want.

    Unfortunately I don't have much experience with networking in Unity and I've been putting off looking into it further because I've heard Unet wasn't very good and they're replacing it with something new anyway.
     
  15. Player7

    Player7

    Joined:
    Oct 21, 2015
    Posts:
    1,533
    Actually Unet has been replaced by the open source project Mirror ...it is better in everyway and don't think will be seeing Unity's official network being ready anytime soon... https://assetstore.unity.com/packages/tools/network/mirror-129321

    Would actually be great to see Animancer with some Mirror examples.
     
    Willbkool_FPCS likes this.
  16. classofthirteenth

    classofthirteenth

    Joined:
    Nov 1, 2019
    Posts:
    16
    Hey, I'm new here using Animancer plugin, is it possible to call a sequence of named animations?
    I have tried to achieve this with regular for each but it doesn't work, thank you
     
  17. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    There's no reason why you couldn't combine the concepts introduced in the Sequence Coroutine example with the Named Animations example. What does your code look like, what does it actually do, and what is it supposed to do?
     
  18. classofthirteenth

    classofthirteenth

    Joined:
    Nov 1, 2019
    Posts:
    16
    The scenario is: whenever the input field of text is entered, the application will generate the animation based on the text

    Example input: "hello there"
    then the output will be the gesture animation of "hello" and "there" sequentially

    this is my code
    Code (CSharp):
    1. foreach (string word in sentence) {
    2.       _Animancer.CrossFade(kata);  
    3. }
    4.  
     
  19. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Each CrossFade in that loop will immediately replace the previous one because you aren't waiting for the animation to finish or anything, the entire loop just runs all in one go in a single frame. Take a look at the Sequence Coroutine example.
     
  20. classofthirteenth

    classofthirteenth

    Joined:
    Nov 1, 2019
    Posts:
    16
    I figured out the way to play the animation sequentially now, thank you very much
    But I have encountered another problem to get the animation clip duration based on their "name"
     
  21. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Assuming you're using a NamedAnimancerComponent with all your clips already added:

    Code (CSharp):
    1. var state = animancer.Play(word);
    2. yield return state;
    3. // Or
    4. yield return state.Length;
    Or if you want to get it without actually playing it:

    Code (CSharp):
    1. var state = animancer.GetState(word);
    2. // state.Length, state.Clip, etc.
     
    classofthirteenth likes this.
  22. classofthirteenth

    classofthirteenth

    Joined:
    Nov 1, 2019
    Posts:
    16
    Thank you! It works
     
    hopeful likes this.
  23. MarkvDrimmelen

    MarkvDrimmelen

    Joined:
    Oct 13, 2017
    Posts:
    13
    Hi Kybernetik,

    I was wondering if you could help me out with an issue regarding a combination of IK lookAt and IK positioning of a hand. I'm currently playing an animation holding a rifle which I have parented to my right hand bone. I have been attaching the character's left hand to the rifle using IK in onAnimatorIK, which worked out just fine. Today I decided to implement LookAt according to your Puppet sample, which worked out pretty well except for the existing left hand IK handle on the rifle. What seems to be going on is that the LookAt hasn't processed yet before setting the IK position of the left hand, causing issues with positioning.

    I've found a thread where someone has the same problem https://forum.unity.com/threads/ikposition-not-working.266885/ here. The suggested answer was to run the animation on a different layer and then do either LookAt or setting the left hand with IK according to the layerIndex. This solution doesn't work out for me because for some reason the layerIndex is always 0. What I've also tried to do is creating an additional script only for the purpose of performing the LookAt and made it execute before the one that handles the hand IK. While both OnAnimatorIK got called in the right order, this unfortunately didn't seem to fix the issue either.

    Kind regards,

    MarkvDrimmelen
     
  24. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    I don't really know anything about IK except for what I did for those examples, but I'll try to work through the problem with you.

    The first thing to check is your Unity version. Anything before 2018.3 had some notable bugs in IK when using the Playables API.

    I suppose because Animancer has a slightly different PlayableGraph structure to what Mecanim uses, it doesn't know how to correctly determine which layer the IK is coming from. There's probably nothing I can do about that.

    Did you try the solution posted in the other thread at the bottom of that one? (this)
     
  25. MarkvDrimmelen

    MarkvDrimmelen

    Joined:
    Oct 13, 2017
    Posts:
    13
    Thank you for your quick response.

    Currently i'm on Unity 2018.3 so there should be no issues there.

    I haven't been able to try any of those solutions because they all refer to a different animation asset (FinalIK) and the code posted there refers to functionality that's specificly for that asset.

    I think this would be a major showstopper for using the combination of SetLookAtPosition and SetIKPosition using Animancer as I found another thread that suggests doing the exact same thing; doing SetLookAtPosition in one Layer and SetIKPosition in another layer.
    I've been digging on the forums some more and found this answer of a Unity developer confirming that onAnimatorIK is called for each layer with IK ticked. I've added some print lines and am quite sure that using Animancer it is only called once, eventhough an animation is ran on two layers with ApplyAnimatorIK set to true.

    So unless you know a way to force the Animator to update in between SetLookAtPosition and SetIKPosition I think it's quite essential to be able to get an onAnimatorIK call for each IK layer. I've also tried calling both pieces of code in turn the next (SetLookAtPosition in one onAnimatorIK, and then SetIKPosition in the next onAnimatorIK) but that doesn't resolve the issue either because that just displays two different frames instead of an aggregated result.
     
  26. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    I've confirmed that OnAnimatorIK only ever gets given a 0, even in a recent version of Unity 2019.2. :( I guess I'll report a Unity bug for it, though that's a super slow fix at best.

    So it's probably not possible to do what you want with Animancer + the old IK system, but you likely could do it with the new Animation Rigging system if you're willing to upgrade to Unity 2019.1+ (and obviously spend time figuring out how to use it). I haven't had a chance to use it myself, but I've heard it's really powerful and I know at least one person has gotten it working with Animancer.
     
  27. classofthirteenth

    classofthirteenth

    Joined:
    Nov 1, 2019
    Posts:
    16
    Hi Kybernetik,

    Is it possible to logging or get the joint coordinates of the animation clip (including the transition if using crossfade) for each frame? The reason why I ask this because I need to do a comparison of the animation with and without transition effect (in this case crossfade)

    Thank You
     
  28. MarkvDrimmelen

    MarkvDrimmelen

    Joined:
    Oct 13, 2017
    Posts:
    13
    Too bad It won't work but thank you for your quick support. We will likely upgrade soon and perhaps I'll have some time on my hands to look into the Animation Rigging system, although I'm not sure how it could help out at first glance.
     
  29. Kybernetik

    Kybernetik

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

    If you call GetComponentsInChildren<Transform>(), it will give you all the transforms so you can record/log whatever details you like. And you can get the fade progress (specifically the change in weight) by using animancerComponent.GetState to get the details of each state you're interested in.
     
  30. classofthirteenth

    classofthirteenth

    Joined:
    Nov 1, 2019
    Posts:
    16
    Thank you for the speedy response, I still don't get it, in where should I call the method GetComponentsInChildren<Transform>()

    Am I doing this right?
    Code (CSharp):
    1. private IEnumerator CoroutineAnimationSequence(List<string> sentences)
    2. {
    3.     foreach (string word in sentences) {
    4.             _Animancer.CrossFade(word);
    5.          
    6.             Transform[] ats = _Animancer.GetComponentsInChildren<Transform>();
    7.             foreach ( var a in ats) {
    8.                 Debug.Log(a.transform);
    9.                 Debug.Log(a.rotation);
    10.             }
    11.  
    12.             yield return new WaitForSeconds(_Animancer.GetState(word).Length);
    13.     }
    14.     _Animancer.CrossFade("idle");
    15. }
    The Log file did show the transformation and rotation, but I don't know which state is the transformation occurred

    Also, I'm curious would you like to share the resources (paper or literature behind the CrossFade transition)? Is it using linear interpolation? If you don't mind hehe
     
    Last edited: Nov 9, 2019
  31. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    You probably want something more like this:

    Code (CSharp):
    1. private IEnumerator CoroutineAnimationSequence(List<string> sentences)
    2. {
    3.     // Get the transforms outside the loop because it's an expensive operation so we only want to do it once.
    4.     // Not actually important here since logging is really inefficient anyway, but it's a good habit.
    5.     var transforms = _Animancer.GetComponentsInChildren<Transform>();
    6.  
    7.     AnimancerState previousState = _Animancer.CurrentState;
    8.  
    9.     foreach (string word in sentences)
    10.     {
    11.         // Use the state returned by CrossFade instead of calling GetState(word) separately.
    12.         var state = _Animancer.CrossFade(word);
    13.        
    14.         // It sounded like you want to log the details every frame, not just once at the start of each animation.
    15.         // So instead of WaitForSeconds to skip the entire duration, you want yield return null to wait one frame at a time.
    16.         while (state.Time < state.Length)
    17.         {
    18.             yield return null;
    19.  
    20.             // Since there are probably quite a few transforms, use some LogWarnings to clearly separate each frame.
    21.  
    22.             if (previousState == null)
    23.                 Debug.LogWarning("Previous State: null");
    24.             else
    25.                 Debug.LogWarning("Previous State: " + previousState.Clip.name + ", Time=" + previousState.Time + ", Weight=" + previousState.Weight);
    26.  
    27.             Debug.LogWarning("Current State: " + state.Clip.name + ", Time=" + state.Time + ", Weight=" + state.Weight);
    28.  
    29.             foreach (var transform in transforms)
    30.             {
    31.                 Debug.Log(transform + " " + transform.rotation);
    32.  
    33.                 // If you want the X/Y/Z rotation values you would see in the Inspector, use localEulerAngles instead of rotation.
    34.             }
    35.         }
    36.  
    37.         previousState = state;
    38.     }
    39.  
    40.     _Animancer.CrossFade("idle");
    41. }
    Cross fading is pretty simple linear interpolation and should be easier to understand when you see what those logs do to the weights over time. The Weight of each state determines how much it affects the final output: 1 = full effect, 0.5 = half, 0 = none. So if you have an animation playing normally (at 1 weight) and call CrossFade, it will start moving the weight of the previous animation down to 0 while moving the weight of the new animation up to 1 at the same time such that at each frame their combined weight will still add up to 1. If you were to graph the weight of each animation over time, you would see them as straight lines starting at 1 and 0 then ending at 0 and 1 respectively, meaning they cross over each other, hence the name "Cross Fade".

    You might also find it useful to select the object and watch the AnimancerComponent in the Inspector to see how the weights change in real-time. Note that you can Ctrl + Click on a state in the Inspector to manually trigger a CrossFade for the sake of testing.
     
  32. classofthirteenth

    classofthirteenth

    Joined:
    Nov 1, 2019
    Posts:
    16
    Thank you for your detailed and well-written explanation about this subject matter, this is really helpful to me. Is the "time" in "seconds"? also can we specify only several joints in our character rig to be logged when we called the GetComponentsInChildren<Transform>()? (eg. only the hand parts, not the whole body)

    sorry if I asked too much question here hehe, and thank you for your time!

    EDIT : After I read the documentation about Transform in Unity, I think i found a way to select several joints since it's a game object, I can call GetChild() to get only the hand part
     
  33. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Yes, Time is in seconds.

    You could filter out certain transforms by simply removing them from the array.

    Code (CSharp):
    1. var transforms = _Animancer.GetComponentsInChildren<Transform>();
    2.  
    3. for (int i = 0; i < transforms.Length; i++)
    4. {
    5.     if (transforms[i].name.Contains("hand"))
    6.         transforms[i] = null;
    7. }
    8.  
    9. // Then later when you go through to log them you'll need to skip over any that are null.
    10. foreach (var transform in transforms)
    11. {
    12.     if (transform == null)
    13.         continue;
    14.  
    15.     Debug.Log(...
    16. }
    Or if you want to get a specific bone, you could find it and only get the transforms on its children:
    Code (CSharp):
    1. var bone = _Animancer.transform.Find("RightHand");
    2. var transforms = bone.GetComponentsInChildren<Transform>();
    Or if you're using a Humanoid Rig, you can get the bone from the Animator without needing to know its actual name:
    Code (CSharp):
    1. var bone = _Animancer.Animator.GetBoneTransform(HumanBodyBones.RightHand);
     
  34. classofthirteenth

    classofthirteenth

    Joined:
    Nov 1, 2019
    Posts:
    16
    Thank you very much! That's exactly what I really want to achieve :D
     
  35. Kybernetik

    Kybernetik

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

    I assume you've moved on already, but I just thought I'd let you know I got a response from bug report I submitted. Turns out it is by design that OnAnimatorIK always gets 0 when using playables and the documentation actually says so (which is honestly surprising because most of the playables documentation is useless).
     
    hopeful likes this.
  36. classofthirteenth

    classofthirteenth

    Joined:
    Nov 1, 2019
    Posts:
    16
    Hi kybernetik, I want to upgrade from lite version to pro, do I have to delete the lite version first or I can install the pro version right away from the asset store? thanks
     
  37. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    You can import Pro straight over the top of Lite.
     
  38. classofthirteenth

    classofthirteenth

    Joined:
    Nov 1, 2019
    Posts:
    16
    Hi Kybernetik, when I tried to call the same animation clip twice with sequence couroutine why the plugin only play it once?

    For example : "clip1", "clip1", "clip2"
    The output animation only play the "clip1" once

    Thank you
     
  39. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Play or CrossFade will only ensure that the animation is playing, but not change the time, so it plays once and finishes then if you tell it to play again without doing anything else it will still be at the end of the animation and end again in the next frame.

    The Playing and Fading example explains why it works like that and how to do what you want.
     
  40. ColpColtran

    ColpColtran

    Joined:
    Oct 9, 2014
    Posts:
    27
    Not sure if bug or not, but it seemed to me that when I want to call animancer.play from a script on an object that doesnt have an animancer component(yet has valid reference), it is not possible.

    Not a big problem, moved my animation logic scripts on the object with animator and its working. Just wondering.
     
  41. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Animancer has no way of knowing where you are calling Play from, so you must have been doing something else wrong.
     
  42. ColpColtran

    ColpColtran

    Joined:
    Oct 9, 2014
    Posts:
    27
    Strange, I must had missed something then. Anyways, havent done much debugging since it was fixed after I moved the script.

    By the way, do you have any tips on how to create animation clips from 2D spritesheet? I know its not directly connected to Animancer, but maybe you know the fix. Since I dont use animation controller, it wont let me edit animation clips until I make controller and put animations in :-/
     
  43. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    The only time I've ever worked with sprite animations was to make the examples for Animancer so unfortunately I'm not really familiar with the process, but I did write this script which adds a bunch of context menu functions to texture assets. Stuff like slicing them into sprites and naming them with a specific pattern and then creating AnimationClips out of selected sprites with the same name based on their numbers. It's all very hard coded so you'll need to edit the script to fit your needs, but I hope it helps. I'd like to make it into a more flexible system which I can add to a future version of Animancer at some point, but I'm not likely to be able to do that any time soon (my Animancer ToDo list is very long).
     
  44. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Hey everyone, I just updated the Change Log with the Progress of v4.0 and added an Alpha Test for anyone interested in trying out the new features.
     
    hopeful likes this.
  45. StevenPicard

    StevenPicard

    Joined:
    Mar 7, 2016
    Posts:
    859
    If I buy Animancer Pro now is the 4.0 upgrade free?
     
  46. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Yes, it says that in bold near the top of both the pages I just linked. :)
     
  47. StevenPicard

    StevenPicard

    Joined:
    Mar 7, 2016
    Posts:
    859
    I actually didn't look at the links. I came here from the Asset Store page and then saw the mention of 4.0 coming out.

    Thanks for letting me know though.
     
  48. jthomason1212

    jthomason1212

    Joined:
    Oct 2, 2019
    Posts:
    2
    Hey so I just got this plugin and really think it's what I'm looking for but I've come across an annoying issue. For context I'm using Unity 2019.2.6 and the Lite version.

    A few days ago I followed the Sequence Coroutine example to experiment how it would work with 2d sprite animation and got it all working just fine. After opening the project today and moving the logic into my own script, I got a strange issue where the ClipState.Serializable objects don't seem to be defaulting to the proper values, most notably Speed defaulting to 0 rather than 1.
    I double checked with the original script I wrote alongside the tutorial which was almost a line by line copy, but the issue was there as well.
    I also opened up the scene from that example, reset the size of Animations to 0, added a new element and that new element's parameters all also default to 0.
    Obviously I can just set the default speed to be 1 on my end but I'd like to resolve the issue in the plugin itself and figure out why this is happening.
     
  49. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Unfortunately that's an issue with Unity's serialization system (which I reported back in Unity 5 and it still isn't fixed).

    You can test it with a simple script like this:

    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3.  
    4. public sealed class NewBehaviourScript : MonoBehaviour
    5. {
    6.     public SerializableClass single;
    7.  
    8.     public SerializableClass[] array;
    9. }
    10.  
    11. [Serializable]
    12. public class SerializableClass
    13. {
    14.     public float value = 1;
    15. }
    The "single" field will properly initialise its value to 1, but when you resize the "array" from 0 it won't actually run the SerializableClass constructor or field initialisers, it just allocates a blank block of memory, meaning all 0s. And unfortunately I've never been able to come up with a reliable workaround.
     
  50. jthomason1212

    jthomason1212

    Joined:
    Oct 2, 2019
    Posts:
    2
    I see. That works for me then as this was more for testing purposes anyway. Thanks for the quick reply.