Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

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,555
    Re-download and re-import Animancer Lite in the Package Manager. You probably downloaded it in a different version of Unity so the DLLs are compiled for that version.
     
  2. Rusted_Games

    Rusted_Games

    Joined:
    Aug 29, 2010
    Posts:
    135
    Indeed, after opening the project for the third time the warning was displayed
    Animancer.Lite.dll was compiled for Unity 2019.4+ but the correct target for this version of Unity would be 2021.2+.
    You should download the appropriate version of Animancer from https://kybernetik.com.au/animancer/lite
    Or you could ignore this warning and delete this file to disable it, but then some features might not work correctly.
    Unity is to blame with its lousy package handling -_-
     
  3. MoonBeam91

    MoonBeam91

    Joined:
    Jan 28, 2021
    Posts:
    35
    Hello again friend @Kybernetik I am getting the below error when I run my game in build mode. In the editor mode the game runs fine. I have also increased the Depth to '10000000' to be safe. Again, the game works great in editor mode but throws this error in my build, and my animations don't work properly


    WindowsPlayer(DESKTOP-I10UU1O) ArgumentOutOfRangeException: AnimancerLayer.GetOrCreateWeightlessState has created 100 states for a single clip. This is most likely a result of calling the method repeatedly on consecutive frames. This can be avoided by using a different FadeMode or calling AnimancerLayer.SetMaxStateDepth to increase the threshold for this warning.
    Parameter name: depth
     
  4. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,555
    Are you repeatedly calling Play using FadeMode.FromStart or a Transition with its Start Time set (meaning it will use FadeMode.FromStart)?
     
  5. MoonBeam91

    MoonBeam91

    Joined:
    Jan 28, 2021
    Posts:
    35
    I am using clip transitions , I was able to fix the problem by changing FixedUpdate to Update
     
  6. zedz

    zedz

    Joined:
    Aug 31, 2013
    Posts:
    245
    I'm not understanding the logic of the layers thing

    (from the docs)
    2 animations = walking, shooting

    you press shoot,
    animancer.Play(shoot); // good
    you walk and then press shoot animancer.Layers[1].Play(shoot); // good, legs are walking torso shoots

    you stop walking press shoot, nothing happens?
    animancer.Play(shoot);

    Surely you want the shoot animation to exist on both layers? i.e. dont move from base layer to layer 1, but just create an identical additional animation on layer 1?
     
  7. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,555
    That example is just explaining what the function will do if you call it like that. The old Layers example before Animancer v7.3 used to move its action animation between layers like that, but the new Dynamic Layers example has a copy on both layers as you're suggesting.
     
    zedz likes this.
  8. Rusted_Games

    Rusted_Games

    Joined:
    Aug 29, 2010
    Posts:
    135
  9. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,555
    It already plays it in Awake so there'd be no point in telling it to play again every Update.
     
  10. Rusted_Games

    Rusted_Games

    Joined:
    Aug 29, 2010
    Posts:
    135
    If I don't call it again in the Update method, in the implementation I have then animation gets stuck in the idle one, which is the first AnimationClip in the _Move MixerTransition2D.
    EDIT: Fixed Code working
    Code (CSharp):
    1. public sealed class StrafeLocomotionState : CharacterState
    2.     {
    3.         [SerializeField]
    4.         private MixerTransition2D _Move;
    5.         [SerializeField]
    6.         private MixerTransition2D _FastMovement;
    7.  
    8.         [SerializeField, Range(0, 1)]
    9.         private float _MovementLevel;
    10.  
    11.         private LinearMixerState _MovementMixer;
    12.  
    13.         public override bool CanEnterState => Character.CharacterMovement.LockOnTarget;
    14.  
    15.         private void Awake()
    16.         {
    17.             _MovementMixer = new LinearMixerState();
    18.  
    19.             // Allocate 2 child states.
    20.             _MovementMixer.Initialize(2);
    21.  
    22.             // Create Child [0] using the Regular Movement transition with a Threshold of 0.
    23.             // Create Child [1] using the Injured Movement transition with a Threshold of 1.
    24.             _MovementMixer.CreateChild(0, _Move, 0);
    25.             _MovementMixer.CreateChild(1, _FastMovement, 1);
    26.  
    27.             // Optionally give the mixers names to show in the Inspector
    28.             _MovementMixer.SetDebugName("Strafe Movement");
    29.             _Move.BaseState.SetDebugName("SlowMovement");
    30.             _FastMovement.BaseState.SetDebugName("FastMovement");
    31.  
    32.            //Character.Animancer.Play(_MovementMixer);
    33.         }
    34.  
    35.         public override void OnEnterState()
    36.         {
    37.             base.OnEnterState();
    38.             Character.Animancer.Play(_MovementMixer);
    39.         }
    40.  
    41.         private void Update()
    42.         {
    43.             if (Character.MovementDirection.magnitude > 0.99)
    44.             {
    45.                 _MovementLevel = 1;
    46.             }
    47.             else
    48.             {
    49.                 _MovementLevel = 0;
    50.             }
    51.  
    52.              _MovementMixer.Parameter = _MovementLevel;
    53.  
    54.              _Move.State.Parameter = Character.CharacterMovement.MoveDirection;
    55.              _FastMovement.State.Parameter = Character.CharacterMovement.MoveDirection;
    56.  
    57.             // Character.Animancer.Play(_MovementMixer);
    58.         }
    59.  
    60.     }
     
    Last edited: Jul 29, 2022
  11. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,555
    Awake is only called once on startup.

    If you want it to happen every time you enter the state, use OnEnable or OnEnterState.
     
    Rusted_Games likes this.
  12. Rusted_Games

    Rusted_Games

    Joined:
    Aug 29, 2010
    Posts:
    135
    if I remove the Play in Update, and replacing the Awake by OnEnable or OnEnterState make no difference, Mixer state only plays the first clip (Idle)
     
  13. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,555
    You only want to play it in OnEnable but still leave the rest in Awake. It doesn't make sense to repeat all the other setup every time the state is entered, though I'm not sure if that would cause your problem.
     
  14. Rusted_Games

    Rusted_Games

    Joined:
    Aug 29, 2010
    Posts:
    135
    Understood now, I fixed the code above, now it is working
     
    hopeful likes this.
  15. zedz

    zedz

    Joined:
    Aug 31, 2013
    Posts:
    245
    OK thanks, sorry for not replying earlier (I thought I was gonna get an alert when I reply happened)
    Guess I'll upgrade then, Im currently using 4.4.
    Hopefully it doesnt break anything.

    PS - I already had it working by having 2 names for all the animations that could be in the torso like "shoot" & "Torsoshoot" (same clip but diff names) which get setup during the init phase, but I'ld prefer to not pass around strings, plus create a lot of action animation states that I may not use. 7.3 seems a lot cleaner (perhaps faster/less memory to boot as well)
     
  16. nTu4Ka

    nTu4Ka

    Joined:
    Jan 27, 2014
    Posts:
    69
    Hey!

    I started moving my project to Animancer and this tool is awesome!
    Mecanim started becoming overly complicated.

    I'm working on a 2D action platformer.

    There are some tasks ahead and I would like to ask few advices:

    1. Is it possible to start animation with offset?
    I can have multiple enemies of the same time standing and would like their idle animation being out of sync.

    2. Is it possible to play several animation clips at the same time on single Animancer component?
    Currently I created separate gameobjects with animators for character model, character shadow and VFX.

    3. What is the better way of identifying that a specific animation finished?
    I'm using following code and it looks really cumbersome
    Code (CSharp):
    1. _Animancer.IsPlayingClip(CharacterVFXAnimation)
    2. &&
    3.  _Animancer.States.Current.NormalizedTime >= _Animancer.States.Current.NormalizedEndTime
    One note.
    There are no "States" on this "_Animancer". It can be a "random" animation out of multiple.
     
  17. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,555
    You might also be interested in my Platformer Game Kit then.
    animancer.Play(clip).NormalizedTime = Random.Range(0f, 1f);

    You're probably looking for Layers or Mixers.
    End Events.
     
  18. nTu4Ka

    nTu4Ka

    Joined:
    Jan 27, 2014
    Posts:
    69
    Thank you very much Kybernetik!

    And thank you for this great package and 2D Platformer Game Kit.


    Unfortunately I won't be able to use events due to my architecture limitation.

    There is a separate chain of classes that handle game logic which is partially tied to animations (see it as MK 11 Fatal Blow or Epic 7 skill 3) and checking for animation end is integrated inside it.
    I cannot use "State.Events.OnEnd" because at the moment animation is started I don't know what will happen after it finishes. There is branching managed by CombatActions classes.

    Simplified example:
    Code (CSharp):
    1. public abstract class CombatAction
    2. {
    3.     // Run action logic
    4.     public abstract void StartAction();
    5.  
    6.     // Check if current action and its child actions finished
    7.     public virtual bool CheckActionFinished() {}
    8. }
    9.  
    10.  
    11. public class Action_Super : CombatAction
    12. {
    13.     public override void StartAction() {
    14.    
    15.         ... some logic here ...
    16.     }
    17.  
    18.  
    19.     public override bool CheckActionFinished() {
    20.    
    21.    
    22.         if ( ... some conditions here ... &&
    23.             _Animancer.IsPlayingClip(CharacterVFXAnimation) &&  _Animancer.States.Current.NormalizedTime >= _Animancer.States.Current.NormalizedEndTime
    24.         ) {
    25.  
    26.            ... start next action in queue of actions ...
    27.             return true;
    28.         }
    29.  
    30.         return false;
    31.     }
    32. }
     
  19. nTu4Ka

    nTu4Ka

    Joined:
    Jan 27, 2014
    Posts:
    69
    Kybernetik I need your help on following definitely.

    I get
    It started happening out of a sudden.
    Sometimes when I close and open Unity Editor issue disappears.

    Issue happens when I change "Loop Time" on one of sprite animations from "On" to "Off" before I hit "Play".

    If I change "Loop Time" and restart Unity Editor - no error happens.

    Code that creates animation states:
    Code (CSharp):
    1.  
    2. public AAnimationController(AnimancerComponent animancer, List<Animation_Code> appliedTypes, Dictionary<Animation_Code, AnimationClip> allAnimations) {
    3.          
    4.     _Animancer = animancer;
    5.     _allAnimations = allAnimations;
    6.  
    7.     foreach (Animation_Code Code in appliedTypes) {
    8.      
    9.         if (allAnimations.ContainsKey(Code) && allAnimations[Code] != null) {
    10.             _Animancer.States.Create(Code.ToString(), allAnimations[Code]);
    11.         }
    12.     }
    13. }
    14.  
    Code that runs sequence of animations:
    Code (CSharp):
    1. public void PlayAnimationQueue(Queue<Animation_Code> animationQueue, Queue<float> startTime, float animationSpeed = 1f) {
    2.          
    3.     State = _Animancer.TryPlay(animationQueue.Dequeue().ToString());
    4.  
    5.     if (State != null) {
    6.      
    7.         State.Speed = animationSpeed;
    8.         State.NormalizedTime = startTime.Dequeue();
    9.  
    10.         if (animationQueue.Count > 0) {
    11.  
    12.             State.Events.OnEnd = () => { PlayAnimationQueue(animationQueue, startTime, animationSpeed); };
    13.         }
    14.     }
    15. }
     
    Last edited: Aug 1, 2022
  20. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,555
    You don't need to know what will happen beforehand, you just register a method for the event to call and the method can decide what to do when the event is triggered. And End Events are triggered every frame after their time has passed, so you can put whatever other conditions you want in there to have it do nothing until you're ready.

    But if you do want to explicitly check it:
    1. Find the IsPlayingAndNotEnding methods in AnimancerNode, AnimancerState, and AnimancerLayer and change them from protected to public. I'll make that change for the next version of Animancer.
    2. Write an extension method like this:
    Code (CSharp):
    1. public static class Extensions
    2. {
    3.     public static bool IsPlayingAndEnding(this IAnimancerComponent animancer, object key) =>
    4.         animancer.Playable.States.TryGet(key, out var state) &&
    5.         state.IsPlaying &&
    6.         !state.IsPlayingAndNotEnding();
    7. }
    Then you can just call
    _Animancer.IsPlayingAndEnding(CharacterVFXAnimation))
    .
    I have no idea how toggling the Loop Time could cause that, but I've posted a simple fix here.
     
  21. Ruchir

    Ruchir

    Joined:
    May 26, 2015
    Posts:
    934
    Would it be possible to add a utility class that helps us preview the animations at runtime?
    Right now I just call the play function in the editor for the animation I want to play and it works fine, but could add a utility component that could help us preview all the animation in the animator component (maybe a button to retrieve the animations in the controller) and preview them frame by frame, instead of just time (in sec)

    It would be beneficial and I think a lot of people could use a component like this :p
     
  22. nTu4Ka

    nTu4Ka

    Joined:
    Jan 27, 2014
    Posts:
    69
    Wow! Thanks again Kybernetik for clear and extended answer!

    I can confirm the change resolves the issue and game plays.
    Thank you!
     
    Last edited: Aug 2, 2022
  23. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,555
  24. Ruchir

    Ruchir

    Joined:
    May 26, 2015
    Posts:
    934
  25. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,555
    Edit mode too, but yes, editor only.

    Iterating through all Animancer's current states in a script is easy.

    Making a script to manually step through an animation frame by frame would also be easy as long as the animation's frame rate actually matches the steps you want. That might not be the case because keyframes can be placed at any time value so they might not line up with the frame rate value and there can be large gaps with no keyframes and each bone can have keyframes at completely different times. But just stepping through based on the frame rate would probably still be useful.

    How would you want to control it? Just with some keyboard hotkeys? Or would you want a UI to show the current animation details as well?
     
  26. Ruchir

    Ruchir

    Joined:
    May 26, 2015
    Posts:
    934
    I was using this asset as an inspiration:
    Animation Preview Pro | Animation Tools | Unity Asset Store
     
  27. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,555
    So you'd want a UI like that, but in a runtime build? That would take a lot more effort than doing it in the editor, but would still be doable.
     
  28. Ruchir

    Ruchir

    Joined:
    May 26, 2015
    Posts:
    934
    No, I just need it for editor, but if it's not that hard then I could use it during runtime as well.
    Thank you for taking this into consideration :oops:
     
  29. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,555
  30. Ruchir

    Ruchir

    Joined:
    May 26, 2015
    Posts:
    934
    Maybe add the same when previewing transitions(which is a really helpful feature btw, thanks so much)

    For the UI could you make the isPlaying bool to maybe a pause/play button and add the step button to step back and front like in the animation window?
    Thank you.
     
  31. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,555
    Being in the AnimancerComponent Inspector will mean it's automatically included in the Transition Preview Window.

    Replacing the toggle with a button is easy and will likely look a bit better.

    Unfortunately, stepping back wouldn't be possible with the same method. All it does currently is update the graph with the given time step. It could try to reverse that by going through every state and reducing their time, but reversing a fade between animations would be impossible if it has already ended and then it would also need to somehow revert the events on every state to avoid leaving the character in the wrong state without an event to end it.

    But doing it in the Transition Preview Window where there's a specifically known set of transitions and nothing else interfering would be much more feasible. I could basically replace the Play Transition button with the same buttons as the Animation Window:

    upload_2022-8-6_9-7-6.png
     
    Ruchir likes this.
  32. MoonBeam91

    MoonBeam91

    Joined:
    Jan 28, 2021
    Posts:
    35
    the value of this always goes beyond 1,
    animTime = _Animancer.Play(PAnimator.CombatAnim.AnimClips[j]).NormalizedTime;

    while in the inspector NormalizedTime resets to Zero at the end of animation loop.

    How do I keep this value between 0 and 1
     
  33. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,555
  34. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,555
    NormalizedTime, not NormalizedEndTime.
     
  35. MoonBeam91

    MoonBeam91

    Joined:
    Jan 28, 2021
    Posts:
    35
    Sorry, I tried it again with NormalizedTime , it returns the value '0' as it returns the remainder


    animTime = _Animancer.Play(PAnimator.CombatAnim.AnimClips[j]).NormalizedTime%1;
    Debug.Log(animTime);

    I am looking for this value

    upload_2022-8-5_19-36-6.png
     
  36. MoonBeam91

    MoonBeam91

    Joined:
    Jan 28, 2021
    Posts:
    35
    Fixed it !

    used this code

    var state = _Animancer.Play(PAnimator.CombatAnim.AnimClips[j]);
    animTime = state.NormalizedTime%1;
     
  37. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,555
    You must have been doing something else wrong, because that would give exactly the same value as the code in your previous post.
     
  38. FuseBox-Games

    FuseBox-Games

    Joined:
    Jan 15, 2014
    Posts:
    8
    Hi! Is there a way to prevent the UnityEvents associated with each AnimancerEvent from showing up in the editor (Pro version)? Our workflow is entirely based on event names, and these cause two workflow issues:
    - they make it way harder to configure transitions with a lot of events, as they take an enormous amount of space
    - I don't want our designers to see them, as inevitably someone will plug callbacks in directly, bypassing the whole combat framework and causing hard to track bugs

    I've found the AnimancerTransitionAssetEditor, but I can't find the AnimancerTransitionEditor to modify.
     
  39. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,555
    The class you're looking for is
    SerializableEventSequenceDrawer
    :
    • In
      CalculateEventHeight
      , remove the bits under
      // Callback.
    • In
      DoEventGUI
      , remove the call to
      DoCallbackGUI
    I'll add a display option for it in the next version.

    Edit: here's what I've done:

    In
    AnimancerSettings
    :
    Code (CSharp):
    1. [SerializeField]
    2. [Tooltip("Should Animancer Event Callbacks be hidden in the Inspector?")]
    3. private bool _HideEventCallbacks;
    4.  
    5. /// <summary>Should Animancer Event Callbacks be hidden in the Inspector?</summary>
    6. public static bool HideEventCallbacks => Instance._HideEventCallbacks;
    In
    SerializableEventSequenceDrawer.CalculateEventHeight
    :
    Code (CSharp):
    1. // Callback.
    2. if (!AnimancerSettings.HideEventCallbacks || context.Callbacks.Count > 0)
    3. {
    4.     // Existing code goes here.
    5. }
    In
    SerializableEventSequenceDrawer.DoEventGUI
    don't comment out
    DoCallbackGUI
    .
    In
    SerializableEventSequenceDrawer.DoCallbackGUI
    add this at the start:
    Code (CSharp):
    1. if (AnimancerSettings.HideEventCallbacks && context.Callbacks.Count == 0)
    2.     return;
     
    Last edited: Aug 6, 2022
  40. FuseBox-Games

    FuseBox-Games

    Joined:
    Jan 15, 2014
    Posts:
    8
    Tried to do it properly by following up with your above instructions, but hit several issues:

    - "Instance" in AnimancerSettings is of type AnimancerSettingsInternal (so can't find _HideEventCallbacks)

    - SerializableEventSequenceDrawer doesn't exist

    And then I realized that I installed Animancer Lite to check if your package was a good fit, decided it was, bought the Pro version... and promptly forgot to import it. I've been rebuilding our new character controller using Light 7.2, need to upgrade to Pro 7.3 (hope everything goes smooth), before I can confirm that your fix worked. My apologies, and thanks for the fast answer.
     
  41. FuseBox-Games

    FuseBox-Games

    Joined:
    Jan 15, 2014
    Posts:
    8
    Your above fix works perfectly, thank you.
     
  42. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,555
  43. FuseBox-Games

    FuseBox-Games

    Joined:
    Jan 15, 2014
    Posts:
    8
    I've got a weird behaviour - setup a Mixer for idle/run animation, with two FOOTSTEP events. When looping either on full idle or on partial/full run, events fire properly. However, when I transition between full idle and partial/full run (ie. when LinearMixerState.Parameter changes from/to 0), I get a burst of several events which shouldn't be there.

    During the bug, sequence is as follow:

    - AnimancerStates.EventDispatcher.CheckGeneralEvents(): 1 call
    - AnimancerStates.EventDispatcher.InvokeAllEvents(): 1 call
    - AnimancerEvent.Sequence[].Invoke(): 4 calls

    Transition setup is as follow:
    https://i.gyazo.com/ff6f0f12a0b5b6b48b6441e035a7f56d.png
     
  44. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,555
    I tried a quick test but wasn't able to replicate what you're describing.

    Can you send me a minimal reproduction project? Either as a private message here or email animancer@kybernetik.com.au.
     
  45. FuseBox-Games

    FuseBox-Games

    Joined:
    Jan 15, 2014
    Posts:
    8
    I'll try - it's a 65K+ assets project, so kinda hard to separate things for a repro. I'll do my best.
     
  46. kana1939

    kana1939

    Joined:
    Jan 4, 2022
    Posts:
    11
    it works! fade layer, not the state
     
  47. FuseBox-Games

    FuseBox-Games

    Joined:
    Jan 15, 2014
    Posts:
    8
    Sent you an email - Title is "Duplicated events" coming from --------@FuseBox-Games.---, contains a WeTransfer link to a stripped down repro project. Open the only scene, footstep should play in a loop. Press the up arrow key, animation should transition and extra footstep sounds play the moment you press the key. Same when you release it. Sound gets louder and louder, so I guess something accumulates.

    Post here if you didn't get the email, we've had issues with lost emails in the past.
     
  48. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,555
    I didn't get the email and there's nothing in my Spam folder. Maybe just send me a personal message here.
     
  49. FuseBox-Games

    FuseBox-Games

    Joined:
    Jan 15, 2014
    Posts:
    8
    Started a conversation with you, I assume that's what you meant by private message?
     
  50. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,555
    Yep, I got it. I'm busy at the moment, but I'll take a look later today.