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
    Is it a non-looping animation? If so, setting a negative time should work:
    _animancer.Play(clip).Time = -1.5f;
    should make it take 1.5 seconds before it starts.

    Otherwise, coroutines are a good way of adding delays in your code.
     
    alexandergikalo likes this.
  2. alexandergikalo

    alexandergikalo

    Joined:
    Oct 24, 2019
    Posts:
    19
    I'll try again to appeal to the collective mind)
    How to make animation with variable values through animator/animancer (for example changing hp bar)?
    Why is this necessary and why not do it through code? I'm using an animator and I want all the animations to work in the same way so that it's easy to pause them and stuff like that.
     
  3. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    If your health bar doesn't need to constantly move, you could just make an animation that starts empty and ends full, set its Speed to 0, and set its NormalizedTime = CurrentHealth / MaxHealth.

    Having it animate continuously could be done with a Linear Mixer and at least two animations: one empty and one full. Then you just set the mixer's Parameter like above.
     
    alexandergikalo likes this.
  4. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Hey everyone, just letting you know that Animancer v7.3 won't quite be ready for release by the end of the month like I had originally planned, but it's getting very close. I don't know exactly how much work I'll need to do on the remaining examples to bring them up to standard, but as long as nothing unforeseen comes up it will likely be ready by early next week.

    On the plus side, I've completely re-written even more examples (Events, State Machines, and Layers) based on user feedback, with a particular focus on explaining why things are done instead of just how to do them. And fixed quite a few more minor bugs around the place.
     
    Rusted_Games and ratking like this.
  5. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Rusted_Games likes this.
  6. Rusted_Games

    Rusted_Games

    Joined:
    Aug 29, 2010
    Posts:
    135
    Hello folks,
    Based on your experience, how would you go to implement a slightly advanced locomotion system having for example, Idle, IdleToWalk, Walk, WalkToIdle, and similarly for jogging?
     
  7. Ghetaldus

    Ghetaldus

    Joined:
    Jul 15, 2017
    Posts:
    46
    Take a look at locomotion examples:
    Animancer - 03 Locomotion (kybernetik.com.au)

    For locomotion you want to use mixers. Those are equivalent to Blend Trees in Unitys Mecanim.
    Animancer - Mixers (kybernetik.com.au)
     
  8. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    @Rusted_Games I've never tried anything like that, but playing IdleToWalk and using its End Event to play Walk should be easy.

    It would be a bit more complex if you only want to play that transition animation after certain animations (Idle) but not others (Jog, Attack, etc.). In that case, you'd need a way to check which type of animation was playing before. The Interruptions example explains a couple of ways you can give states an identifier in Animancer's State Machine system and the Dynamic Layers example just stores a bool each time an animation is played to indicate how it should be handled in the future.
     
  9. Rusted_Games

    Rusted_Games

    Joined:
    Aug 29, 2010
    Posts:
    135
    @Ghetaldus thanks for the reply, I'm using a MultiState approach from the 2D Platformer Game Kit, to have more control when to play the walk and jog states, focused on gamepad, when you need to tilt the left stick to actually start jogging instead of having a blend transition, which even with Mixers I can't get it right. Here's the tutorial I made about it

    @Kybernetik Thanks also for your reply, I'll take a look at your suggestions, and of course share it here when I get it working ;)
     
    Willbkool_FPCS likes this.
  10. MoonBeam91

    MoonBeam91

    Joined:
    Jan 28, 2021
    Posts:
    35
    I using Animation events (NOT animancer) to play sounds and I am getting this error, the game is running, but don't understand this
    ArgumentOutOfRangeException: Events on looping animations are triggered every loop and must be within the range of 0 <= normalizedTime < 1.
    WalkFwdLoop (ClipState)
     
  11. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    You must be using Animancer Events somewhere. There's no way for that error to be triggered otherwise.
     
  12. jeromeWork

    jeromeWork

    Joined:
    Sep 1, 2015
    Posts:
    429
    I'm struggling to get state keys working (to play the same animation on multiple layers) tried to follow the docs but getting the following error:
    Code (CSharp):
    1. InvalidOperationException: AnimancerNode.Root hasn't been set so it's Playable hasn't been created. It can be set by playing the state or calling SetRoot on it directly. SetParent would also work if the parent has a Root.
    2. • State: tableSettingLoop (ClipState)
    This was my original code (which only played one of the override layers):
    Code (CSharp):
    1. var state = character.animancer.Play(idleFace.idleFaceAnim);
    2.                 state.NormalizedTime = Random.Range(0f, 1f); // random start time (normalised between 0 and 1)
    3.                
    4.                 character.animancer.Layers[OverrideLegsLayer].ApplyFootIK = true;
    5.                 character.animancer.Layers[OverrideLegsLayer].SetMask(avatarLegsMask);
    6.                 character.animancer.Layers[OverrideArmsLayer].SetMask(avatarArmsMask);
    7.                 character.animancer.Layers[OverrideArmsLayer].Play(character.mainIdleBodyAnim);
    8.                 character.animancer.Layers[OverrideLegsLayer].Play(character.mainIdleBodyAnim);
    And this is my attempted refactor:
    Code (CSharp):
    1. var state = character.animancer.Play(idleFace.idleFaceAnim);
    2.                 state.NormalizedTime = Random.Range(0f, 1f); // random start time (normalised between 0 and 1)
    3.  
    4.                 var stateLegs = new ClipState(character.mainIdleBodyAnim);
    5.                 stateLegs.Key = "LegsAnim";
    6.                 var stateArms = new ClipState(character.mainIdleBodyAnim);
    7.                 stateLegs.Key = "ArmsAnim";
    8.                 stateLegs.ApplyFootIK = true;
    9.                 stateArms.LayerIndex = OverrideArmsLayer;
    10.                 stateLegs.LayerIndex = OverrideLegsLayer;
    11.                 stateArms.Layer.SetMask(avatarArmsMask);
    12.                 stateLegs.Layer.SetMask(avatarLegsMask);
    13.                 /* stateArms.Play(); // this didn't work for some reason
    14.                 stateLegs.Play(); */
    15.                 character.animancer.TryPlay("LegsAnim");
    16.                 character.animancer.TryPlay("ArmsAnim");
    Can anyone help?
     
  13. jeromeWork

    jeromeWork

    Joined:
    Sep 1, 2015
    Posts:
    429
    Ah... got it. This works:
    Code (CSharp):
    1. var state = character.animancer.Play(idleFace.idleFaceAnim);
    2.                 state.NormalizedTime = Random.Range(0f, 1f); // random start time (normalised between 0 and 1)
    3.                
    4.  
    5.                 var stateLegs = character.animancer.States.Create("LegsAnim", character.mainIdleBodyAnim);
    6.                 var stateArms = character.animancer.States.Create("ArmsAnim", character.mainIdleBodyAnim);
    7.                 stateLegs.ApplyFootIK = true;
    8.                 stateArms.LayerIndex = OverrideArmsLayer;
    9.                 stateLegs.LayerIndex = OverrideLegsLayer;
    10.                 stateArms.Layer.SetMask(avatarArmsMask);
    11.                 stateLegs.Layer.SetMask(avatarLegsMask);
    12.                 /* stateArms.Play(); // Still don't know why this doesn't work
    13.                 stateLegs.Play(); */
    14.                 character.animancer.TryPlay("LegsAnim");
    15.                 character.animancer.TryPlay("ArmsAnim");
    I'm not sure why
    stateArms.Play();
    doesn't work. Would be good to know.

    Also might be worth updating or explaining how to use the last option in the
    PlayAnimation()
    example here: https://kybernetik.com.au/animancer/docs/manual/playing/states#keys
    My 1st version was trying the "Or you can create the state and set its key manually" version.
     
  14. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    The problem in your first refactor attempt was that you tried to play the state without actually giving it any connection to the thing you want it to play on.

    Using
    character.animancer.States.Create
    solves that by connecting the new state to the
    character.animancer
    .

    Another way to do it would be
    character.animancer.Layers[OverrideArmsLayer].Play(stateArms);
    because that also connects the state to that layer.

    There's also another important distinction:
    • state.Play() will only affect that state.
    • layer.Play(state) will connect and play that state, stop playing all other states on that layer, and also set the layer's Weight to 1, which are all desirable in most cases.
    The Dynamic Layers example demonstrates how I'd approach playing one animation on two layers.

    Now that Animancer v7.3 is out, I've got a list of other things to look at in the documentation, so I'll add this to the list.
     
    jeromeWork likes this.
  15. jeromeWork

    jeromeWork

    Joined:
    Sep 1, 2015
    Posts:
    429
    Thanks @Kybernetik Incredibly helpful, as always :)

    Can I quickly ask about using Animancer together with a more traditional AnimatorController set-up. (before I delved into the docs to try to work this out).

    One of my characters has a traditional mecanim animator which (for now) I'd like to keep doing its thing.

    I've added an AnimancerComponent to just trigger face animations on an override layer (with a Face-Only avatar mask)
    Code (CSharp):
    1. var state = character.animancer.Layers[OverrideLayer].Play(idleFace.idleFaceAnim);
    2. character.animancer.Layers[OverrideLayer].SetMask(avatarMask);
    However I find that if my Animancer layer has any weight greater than 0, the animations on the body (from the animation controller) don't play at all (although they still show as running under 'Native Animator Controller' ). I've tried both a baseLayer (index 0) and an override layer (index = 1) and the same happens.

    Is it possible to have both playing together?

    EDIT: never mind, just found HybridAnimancerComponent Will do some reading and learning!
     
  16. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    If your character has a Humanoid rig, then the Animator Controllers page explains that you'd need to use a HybridAnimancerComponent. Same Animator Controller, but you call Play/SetFloat/etc. methods on that component instead of the Animator.

    If your character has a Generic rig, it would normally work but I haven't tested it with masks on all Animancer layers. It's possible that mask based blending only works within Animancer's AnimationLayerMixerPlayable but its output might not blend with a native Animator Controller, so a HybridAnimancerComponent is probably also the solution there.
     
    jeromeWork likes this.
  17. ZeroPi

    ZeroPi

    Joined:
    Apr 6, 2021
    Posts:
    4
    Hi, I made a small demo using Animancer and wanted to make the character change the animation (forward, backward, left, right) depending on the input. Also, mix it with running animations as well. The problem I have here is that Fade Duration does not work at all when I change the directions. I tried changing Fade duration in inspector and in the script and nothing worked. It would be great if you point out what I did wrong :)

    (Sorry for messy code and unused variables)

    Code (CSharp):
    1. public class AnimationController : MonoBehaviour
    2. {
    3.     private AnimancerComponent anim;
    4.    
    5.     private float movementSpeed;
    6.  
    7.     private Vector2 animationMovement;
    8.  
    9.     [Header("Animation Clips")]
    10.  
    11.     [SerializeField] private MixerTransition2D regularMovement;
    12.     [SerializeField] private MixerTransition2D runMovement;
    13.  
    14.     public AnimationClip idle;
    15.     public AnimationClip falling;
    16.     public AnimationClip landing;
    17.    
    18.     private LinearMixerState _MovementMixer;
    19.  
    20.     private MyCharacterController controller;
    21.  
    22.     private void Start()
    23.     {
    24.         anim = GetComponent<AnimancerComponent>();
    25.         controller = GetComponent<MyCharacterController>();
    26.         _MovementMixer = new LinearMixerState();
    27.         _MovementMixer.Initialize(1);
    28.        
    29.         _MovementMixer.CreateChild(0, regularMovement, 0);
    30.         // _MovementMixer.CreateChild(1, runMovement, 1);
    31.  
    32.         anim.Play(_MovementMixer);
    33.     }
    34.  
    35.     private void Update()
    36.     {
    37.         //_MovementMixer.Parameter = controller.relativeSpeed;
    38.        
    39.         animationMovement.x = controller.input.moveY;
    40.         animationMovement.y = controller.input.moveX;
    41.        
    42.         regularMovement.State.Parameter = animationMovement;
    43.     }
    44.  
    45. }
    (Just noticed my X and Y movement values are messed up)

    Here is also a screenshot of this script in the inspector, hope it's helpfull.
     

    Attached Files:

  18. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
  19. MoonBeam91

    MoonBeam91

    Joined:
    Jan 28, 2021
    Posts:
    35
    I am using scriptable objects to run animations with animancer and I have this code

    _Animancer.Play(PAnimator.CombatAnim.AnimClips[0]).Events.OnEnd =
    _Animancer.Play(PAnimator.CombatAnim.AnimClips[1]).Play;

    where CombatAnim is a list of scriptable objects and AnimClips is a list of animations. The above code always seem to skip the animation on AnimClips[0] and starts playing AnimClips[1]. I want the AnimCLip[0] to play and end , then play AnimClips[1]
     
  20. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    ...OnEnd = SomeMethod().Play;
    will call SomeMethod immediately and use its return value to create a delegate of its Play method to assign to OnEnd.

    You want
    ...OnEnd = () => _Animancer.Play(PAnimator.CombatAnim.AnimClips[1]);


    Though a ClipTransitionSequence might already do that all for you.
     
  21. Airship

    Airship

    Joined:
    Sep 10, 2011
    Posts:
    260
    Is using a LinearMixerState to blend between a MixerTransition2D and LinearMixerTransition recommended?

    I have a setup similar to the NestedMixerExample from https://kybernetik.com.au/animancer/docs/manual/blending/mixers/#nesting

    For some reason it seems to work well and then after a few seconds of blending between the states I get 1000s of errors in console and FPS drops to 0.

    Code (CSharp):
    1.  
    2. ArgumentOutOfRangeException: Time must not be NaN or Infinity
    3. Parameter name: weightedNormalizedTime
    4. Actual value was Infinity.
    5. Animancer.MixerState.ApplySynchronizeChildren (System.Boolean& needsMoreUpdates) (at Assets/Plugins/Animancer/Internal/Mixer States/MixerState.cs:927)
    6. Animancer.MixerState.Update (System.Boolean& needsMoreUpdates) (at Assets/Plugins/Animancer/Internal/Mixer States/MixerState.cs:543)
    7. Animancer.AnimancerNode.Animancer.IUpdatable.Update () (at Assets/Plugins/Animancer/Internal/Core/AnimancerNode.cs:283)
    8. Animancer.AnimancerPlayable.UpdateAll (Animancer.Key+KeyedList`1[T] updatables, System.Single deltaTime) (at Assets/Plugins/Animancer/Internal/Core/AnimancerPlayable.cs:1275)
    9. UnityEngine.Debug:LogException(Exception, Object)
    10. Animancer.AnimancerPlayable:UpdateAll(KeyedList`1, Single) (at Assets/Plugins/Animancer/Internal/Core/AnimancerPlayable.cs:1280)
    11. Animancer.AnimancerPlayable:PrepareFrame(Playable, FrameData) (at Assets/Plugins/Animancer/Internal/Core/AnimancerPlayable.cs:1245)
    12.  
     
    Last edited: Jul 6, 2022
  22. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Nested mixers are supported, but the way Synchronization is implemented when you have nested mixers is a bit finicky. It's entirely possible (likely even) that there's a bug, but for starters make sure you disable synchronization on things that don't need it. In this case, you probably want no synchronization in the parent mixer because the turn animations won't correspond to any particular part of the locomotion animations.

    If you wouldn't mind sending me a minimal reproduction project (as a private message here or to animancer@kybernetik.com.au), I'll see if there's a better way for Animancer to handle your use case.
     
  23. Airship

    Airship

    Joined:
    Sep 10, 2011
    Posts:
    260
    Thanks, yeah using DontSynchronizeChildren() fixes the issue. I'll try to throw together a reproduction unity scene and send it along too.
     
  24. florianBrn

    florianBrn

    Joined:
    Jul 31, 2019
    Posts:
    53
    Hello!

    I'm trying to emulate the simplicity of Unity's animation events to trigger stuff like VFX/SFX, but in a more reliable and performant way (i.e without those pesky messages).
    For that, I tried using Clip Transition assets in mixers instead of animation clips, which kinda works, though the way it has to be done isn't the cleanest. To access the events, I need to do something along these lines:

    Code (CSharp):
    1. Object[] anims = m_animationAsset.Transition.Animations;
    2. ((ClipTransitionAsset)anims[x]).Transition.State.Events.AddCallback();
    It would be nice if there was another event sequence containing all the events from the mixer and its subelements (preferably recursively with no depth limit), so we could just AddCallback/RemoveCallback to all animations easily.

    Another drawback to this method is that events are always called, no matter if the animation they're linked to has any weight.
    For example, if you have a Mixer the blends between an idle, a walk and a run, you want footstep sounds to be triggered at some specific position on both the walk and the run, and none at all during the idle. With this technique, all the events from the 3 animations are called even when the idle animation is at full weight.

    Is there any better way of doing this that I am missing (that doesn't involve using Unity's animation events)? If not, I'd definitely love a better handling of events in the case of nested transitions!
     
  25. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    To add events recursively, you could make an extension method like this:
    Code (CSharp):
    1. public static class Extensions
    2. {
    3.     public static void AddCallbackToChildren<TMixer>(this ManualMixerTransition<TMixer> transition, string eventName, Action callback)
    4.         where TMixer : ManualMixerState
    5.     {
    6.         for (int i = 0; i < transition.Animations.Length; i++)
    7.         {
    8.             var child = transition.Animations[i];
    9.             if (child is ITransitionWithEvents transitionWithEvents)
    10.             {
    11.                 var index = transitionWithEvents.Events.IndexOf(eventName);
    12.                 if (index >= 0)
    13.                     transitionWithEvents.Events.AddCallback(index, callback);
    14.  
    15.                 if (transitionWithEvents is ManualMixerTransition<TMixer> childMixer)
    16.                     childMixer.AddCallbackToChildren(eventName, callback);
    17.             }
    18.         }
    19.     }
    20. }
    You can also place events on the parent mixer in case you weren't aware. It will trigger them based on the weighted average normalized time of its children.

    And you can access the state currently triggering an event using AnimancerEvent.CurrentState. So you could just check if its Weight is 0 or below whatever threshold you decide.
     
    florianBrn likes this.
  26. ZeroPi

    ZeroPi

    Joined:
    Apr 6, 2021
    Posts:
    4
    Thanks. That worked))
     
  27. florianBrn

    florianBrn

    Joined:
    Jul 31, 2019
    Posts:
    53
    Perfect, thanks! :)
     
  28. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Hey everyone. A few people have mentioned that they find the auto-playing unstoppable gifs in the documentation to be a bit annoying so I've found a way to add pause controls, but I'm not sure exactly what behaviour would be best (pause by default? play on click? play on hover? etc). I made this post explaining the available choices I can think of, so please let me know if you have an opinion.
     
  29. Rusted_Games

    Rusted_Games

    Joined:
    Aug 29, 2010
    Posts:
    135
    I'd vote for Play by default, then click to play/pause, for those who find it annoying
     
  30. Skokon

    Skokon

    Joined:
    Jul 23, 2017
    Posts:
    74
    Helllo, are these issues still in the asset?

    upload_2022-7-9_0-24-55.png
     
  31. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    > Only ClipState can fade into itself

    Yes, but what other system are you comparing it to? I don't know of any other system that allows any kind of state to fade into itself so I don't think that actually qualifies as a Con for Animancer.

    Fading a state into itself is achieved by making a clone of it, which is fine for a ClipState, but for a Mixer that would mean cloning all of its children (a much larger performance hit which would occur in the middle of gameplay) and you would then have two (or more) mixers which you need to control the parameters of (and there's nothing explicitly telling you when it creates a clone either, so you'd have to check for it every time you play the mixer(s). Those aren't insurmountable problems, but they would take a significant amount of work and would likely still give a worse result than implementing it manually, by which I mean:
    1. Create and configure your mixer.
    2. Create and configure as many copies of it as you want to allow simultaneous fades.
    3. Keep all those mixers in a list. You could use the same trick that FadeMode.FromStart uses where each clone uses the previous as its Key, but then you'd need to type cast them when you want to set their parameters.
    4. When you want to play the mixer, check the weight of the first mixer in the list. If it's 0, play it. Otherwise, go to the next mixer in the list and repeat.
    5. If you reach the end of the list, you have a couple of options: A) keep track of whichever mixer has the lowest Weight as you go through the list so you can set its Time to the start with the least visual impact (and consider making more copies available from the start), or B) if you don't mind the performance cost of creating a mixer in the middle of gameplay, you could simply create another copy of the mixer and add it to the list.
    6. When you want to set the mixer's Parameter, go through the list and set the same value on all of them.
    If that's actually a feature you'd want, I can probably implement something to make it easier, but so far everyone who has brought up the idea seemed to lose interest once I explained why it's not already supported.

    If I ever figure out how to implement Inertialization, it would (in theory) avoid the need to clone any states at all. Then self-fading should simply work on anything, including Animator Controllers and Timelines. But I've tried a couple of times and never managed to get it to work.

    > Events sometimes don't work

    I'm currently aware of no bugs in the Animancer Event system so if you're experiencing one, you need to actually tell me about it if you want it fixed.

    The only situation I know of where events don't work as people expect is when they misunderstand that events are Cleared Automatically whenever you play something else (unless AnimancerState.AutomaticallyClearEvents is set to false).

    > Layer weight fade sometimes don't work

    I fixed a minor issue relating to layer fading in Animancer v7.3, but I have no idea if that's what you're experiencing. Again, if you want bugs fixed you have to report them.

    > Not as stable as Unity Animator Controllers

    Not as stagnant as Unity Animator Controllers either. I'm actively fixing bugs and making improvements while most of the animation related bugs I report to Unity get the response of "yeah that's a bug, but we aren't going to fix it because people might be depending on the current behaviour".

    And I'm not currently aware of any stability issues.

    > Very heavy use of code

    Yes, and I'd put that in the Pros, not Cons. You have to define your logic somewhere and logic belongs in scripts, not scattered arbitrarily between scripts and assets.

    People occasionally ask about integration with Visual Scripting systems, but so far every time I've pointed out that most visual scripting systems can call methods on regular scripts (meaning they should be able to control Animancer just like any script) and said I'm happy to work with them if there's any more specific integration needed, I get no further response from them.

    > Not friendly with Empty States

    What is an empty state supposed to do?

    If you want it to stay in whatever pose it was in before, you can call animancer.Playable.PauseGraph();

    If you want it to go to a default pose, just export that pose as an animation and play it normally.

    Or if your character's Rig is Generic, setting the Weight of all layers to 0 will put it back in the pose it had on startup. A Humanoid would go into the Default Humanoid Pose, which is unlikely to be desirable. Implementing an actual EmptyState class is extremely easy and would give that same behaviour:

    Code (CSharp):
    1. public class EmptyState : AnimancerState
    2. {
    3.     public override float Length => 0;
    4.  
    5.     protected override void CreatePlayable(out UnityEngine.Playables.Playable playable)
    6.         => playable = UnityEngine.Playables.Playable.Create(Root.Graph);
    7. }
    Note that I skewed the legs before entering Play Mode to show that the EmptyState is returning to that pose, not staying at the end of the previous animation.

    empty-state.gif

    Is that a state you would actually find useful? It doesn't seem very useful to me, but I can include it in Animancer if anyone actually wants it.
     
    Rusted_Games likes this.
  32. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
  33. Rusted_Games

    Rusted_Games

    Joined:
    Aug 29, 2010
    Posts:
    135
    Hi @Kybernetik I'm getting a warning in the console from my Respawn state:
    "Possible Bug Detected: The AnimancerEvent.Sequence being modified should not be modified because it was created by a Transition."
    The Respawn state has a single ClipTransition, it will transition to a Idle state which is a MultiState, show cased in this tutorial:


    The offending line: _MainAnimation.Events.OnEnd = Character.StateMachine.ForceSetDefaultState;
    Code (CSharp):
    1.     public sealed class RespawnState : CharacterState
    2.     {
    3.         [SerializeField] private ClipTransition _MainAnimation;
    4.  
    5.         private void Awake()
    6.         {
    7.             _MainAnimation.Events.OnEnd = Character.StateMachine.ForceSetDefaultState;
    8.             Character.StateMachine.TrySetState(this);
    9.         }
    10.  
    11.         public override void OnEnterState()
    12.         {
    13.             Character.CharacterAnimancerComponent.Play(_MainAnimation);
    14.         }
    15.  
    16.         public override bool CanExitState => false;
    17.     }
    Is there something I need to improve or fix? I'd like to have the console clean but not to the cost of disabling the Locked Events warning.
     
  34. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    That's a new warning in Animancer v7.3.

    Character.Awake must be entering this state before RespawnState.Awake is called.

    I'd fix that by giving RespawnState (or maybe the base CharacterState) a [DefaultInitializationOrder] attribute with a negative value to make it Awake before other scripts.

    You might want to use the new Serialized State Machine pattern which won't solve the execution order problem, but I find that being able to see the current state in the Inspector is very useful. It would also let you avoid needing separate fields for the Character to reference the Idle and Respawn states since they'd be referenced by the state machine's Default and Current states respectively. Though if the character can enter the Respawn state again after startup, you'll still need a separate reference to it.

    Also, RespawnState.Awake doesn't need to tell the StateMachine to enter itself if you already set it as the DefaultState in the Character.
     
  35. Rusted_Games

    Rusted_Games

    Joined:
    Aug 29, 2010
    Posts:
    135
    Thanks for the prompt reply, I added the [DefaultExecutionOrder] attribute, I couldn't find the [DefaultInitializationOrder] in Unity's API, and that fixed the warning.
     
  36. Skokon

    Skokon

    Joined:
    Jul 23, 2017
    Posts:
    74


    Hello, thanks for the reply

    actually that was a screenshot of some video on the youtube

    i found out about animancer before i watch that video and when i watched it i saw these (Cons)

    Actually as you said most of them aren't cons but i was mentioning to the bugs like events sometimes doesn't get invoked, etc

    after commenting here
    i did read the change log of the last version

    I've seen the fixes and all of them are mentioned as fixed

    So i saw the lite version and decided to give it a try

    the examples are really good and literally including everything

    as a programmer i give the asset 10/10

    some people may want to use the asset but doesn't have that much of experience

    so i suggest to include full body ik and direction locomotion and state machines & layers

    best wishes
     
  37. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    What was the video?
     
  38. MoonBeam91

    MoonBeam91

    Joined:
    Jan 28, 2021
    Posts:
    35
    this is only code that I have for animations, but for some reason all the animations are working even without calling the animancer.play. The if statement is empty
    if (_Animancer.Play(PAnimator.MovementAnim.AnimClip).NormalizedTime >= 0.8f)
    {

    }
    else
    {
    PAnimator.MovementAnim.AnimClip.Events.OnEnd = Idle;
    }


    I wanted to check this
    if (_Animancer.Play(PAnimator.MovementAnim[6].AnimClip).IsPlaying)
    {
    vaultOverride = true;
    }
    else
    {
    vaultOverride = false;
    }

    but for somereason Animation number 6 is playing even though it was never called
     
  39. Rusted_Games

    Rusted_Games

    Joined:
    Aug 29, 2010
    Posts:
    135
    Hi MoonBeam91
    By calling _Animancer.Play(.. you are actually calling the animation to be played
     
  40. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Yeah, putting
    [I][I]_Animancer.Play(PAnimator.MovementAnim[6][/I][/I]
    inside an
    if
    statement doesn't somehow prevent that line from playing the animation. So you are telling the animation to play in both cases.
     
  41. MoonBeam91

    MoonBeam91

    Joined:
    Jan 28, 2021
    Posts:
    35
    My aim is to check a particular animation whenever it is running, so that I can control some input boolean functions. With the current method I have been struggling with it. Basically I need other animations to run only if the current animation is completed
     
  42. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    _Animancer.IsPlaying(clip) will tell you if something is currently playing and the Basic Action example explains how you can set up an End Event to do something when an animation ends.
     
  43. Rusted_Games

    Rusted_Games

    Joined:
    Aug 29, 2010
    Posts:
    135
    Hi @Kybernetik I'm getting a new warning
    *************************************
    Possible Bug Detected: An End Event did not actually end the animation:
    - State: Backstep (ClipState)
    - Callback: DodgeState.OnAnimationEnded

    End Events are triggered every frame after their time has passed, so if that is not desired behaviour then it might be necessary to explicitly set the state.Events.OnEnd = null or simply use a regular event instead.
    *****************************************
    Started after merging the Roll, and Backstep States into a single DodgeState, the reason being that both uses the same input button, but the condition is whether the character is already in movement.
    On both ClipTransition I have manually set where I wanted the clip to end to trigger the callback


    Code (CSharp):
    1. using UnityEngine;
    2. using Animancer;
    3.  
    4. namespace RustedGames
    5. {
    6.     public sealed class DodgeState : CharacterState
    7.     {
    8.         [SerializeField]
    9.         private ClipTransition _Backstep;
    10.         public ClipTransition Backstep => _Backstep;
    11.  
    12.         [SerializeField]
    13.         private ClipTransition _Roll;
    14.         public ClipTransition Roll => _Roll;
    15.  
    16.         public override bool CanEnterState => Character.CharacterMovement.Dodge;
    17.  
    18.         public override bool CanExitState => true;
    19.  
    20.         public ClipTransition CurrentAnimation
    21.         {
    22.             get
    23.             {
    24.                 if (Character.MovementDirection != Vector2.zero)
    25.                 {
    26.                     return _Roll;
    27.                 }
    28.                 else
    29.                 {
    30.                     return _Backstep;
    31.                 }
    32.             }
    33.         }
    34.  
    35.         public override void OnEnterState()
    36.         {      
    37.             Character.CharacterAnimancerComponent.Play(CurrentAnimation);
    38.         }
    39.  
    40.         private void Awake()
    41.         {
    42.             _Backstep.Events.OnEnd = OnAnimationEnded;
    43.             _Roll.Events.OnEnd = OnAnimationEnded;
    44.         }
    45.  
    46.         private void OnAnimationEnded()
    47.         {
    48.             Character.CharacterMovement.VariableHandler.SetWithoutNotify(GameConstants.DodgeObjectVariableKey, false);
    49.             // Fix to warning after Kybernetik guidance
    50.             Character.StateMachine.TrySetDefaultState();
    51.  
    52.         }
    53.  
    54.     }
    55. }
    I have tried several things but none of them gets rid of the warning, except of course using a regular clip event as suggested in the warning message.
     
    Last edited: Jul 10, 2022
  44. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    If SetWithoutNotify isn't playing something else, then Animancer doesn't know whether you're actually going to, in which case the End Event would keep getting triggered every frame which is often not the intended behaviour. There's nothing wrong with disabling the warning if that is your intended behaviour or using a regular event instead.
     
    Rusted_Games likes this.
  45. Rusted_Games

    Rusted_Games

    Joined:
    Aug 29, 2010
    Posts:
    135
    That makes sense now, the SetWithoutNotify is just changing a variable back into ORK Framework integration.
    By calling Character.StateMachine.TrySetDefaultState(); afterwards got rid of the warning.
    Thanks a lot!
     
  46. MoonBeam91

    MoonBeam91

    Joined:
    Jan 28, 2021
    Posts:
    35
    Hello, let's say I have animation that is triggered by LShift, W, and Space, but during the game the user one of those conditions or all the those conditions become false. How do I make sure that the current animation plays completely before the next animation plays ? Is there an animancer method I could use, or do I need to work on script logic ?
     
  47. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Animancer doesn't impose any limitations to which animations you can play when, so it's up to your scripts to only tell it to play stuff you want to play. The Basic Character example introduces a simple way of doing it and the Interruptions example goes into more complex systems.
     
  48. bugcamper

    bugcamper

    Joined:
    Mar 15, 2022
    Posts:
    11
    I get this error when I close my game: unity 2021.3.6f1, animacer pro 7.3

    NullReferenceException
    at (wrapper managed-to-native) UnityEngine.AnimationClip.get_length(UnityEngine.AnimationClip)
    at Animancer.ClipState.get_Length () [0x00000] in E:\plastic\RGame\game\unity\Assets\Plugins\Animancer\Internal\Core\ClipState.cs:52
    at Animancer.AnimancerState.get_NormalizedTime () [0x00001] in E:\plastic\RGame\game\unity\Assets\Plugins\Animancer\Internal\Core\AnimancerState.cs:565
    at Animancer.AnimancerState+EventDispatcher.OnTimeChanged () [0x00001] in E:\plastic\RGame\game\unity\Assets\Plugins\Animancer\Internal\Core\AnimancerState.EventDispatcher.cs:456
    at Animancer.AnimancerState.set_Time (System.Single value) [0x0007a] in E:\plastic\RGame\game\unity\Assets\Plugins\Animancer\Internal\Core\AnimancerState.cs:494
    at Animancer.AnimancerState.Stop () [0x00010] in E:\plastic\RGame\game\unity\Assets\Plugins\Animancer\Internal\Core\AnimancerState.cs:361
    at Animancer.AnimancerLayer.Stop () [0x00020] in E:\plastic\RGame\game\unity\Assets\Plugins\Animancer\Internal\Core\AnimancerLayer.cs:792
    at Animancer.AnimancerPlayable.Stop () [0x00011] in E:\plastic\RGame\game\unity\Assets\Plugins\Animancer\Internal\Core\AnimancerPlayable.cs:851
    at Animancer.AnimancerComponent.Stop () [0x0000e] in E:\plastic\RGame\game\unity\Assets\Plugins\Animancer\AnimancerComponent.cs:574
    at Animancer.AnimancerComponent.OnDisable () [0x0003b] in E:\plastic\RGame\game\unity\Assets\Plugins\Animancer\AnimancerComponent.cs:252
     
  49. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    I haven't been able to reproduce an error message like that, so could you please make a minimal reproduction project and send it to animancer@kybernetik.com.au (or as a personal message here) so I can take a look?
     
  50. Rusted_Games

    Rusted_Games

    Joined:
    Aug 29, 2010
    Posts:
    135
    Hello @Kybernetik I was preparing a template for the patreons I have to follow along the tutorials, and downgraded the Animancer 7.3 Pro to Lite version. Now I'm getting this exception about a Missing UnityEngine.Animations.AnimationLayerMixerPlayable.Create(UnityEngine.Playables.PlayableGraph,int)
    *******************************************************************************************************************************
    MissingMethodException: Method not found: UnityEngine.Animations.AnimationLayerMixerPlayable UnityEngine.Animations.AnimationLayerMixerPlayable.Create(UnityEngine.Playables.PlayableGraph,int)
    Animancer.AnimancerPlayable.OnPlayableCreate (UnityEngine.Playables.Playable playable) (at <eec0db380d5148aaa807ae138a692142>:0)
    UnityEngine.Playables.ScriptPlayable`1:Create(PlayableGraph, AnimancerPlayable, Int32)
    Animancer.AnimancerPlayable:Create()
    Animancer.AnimancerComponent:InitializePlayable() (at Assets/Plugins/Animancer/AnimancerComponent.cs:305)
    Animancer.AnimancerComponent:get_Playable() (at Assets/Plugins/Animancer/AnimancerComponent.cs:77)
    Animancer.AnimancerComponent:play(ITransition) (at Assets/Plugins/Animancer/AnimancerComponent.cs:499)
    RustedGames.RespawnState:OnEnterState() (at Assets/0_DarkARPG/Scripts/GamePlay/Behaviors/Characters/States/RespawnState.cs:18)
    Animancer.FSM.StateMachine`1:ForceSetState(CharacterState) (at Assets/Plugins/Animancer/Utilities/FSM/StateMachine1.cs:343)
    Animancer.FSM.StateMachine`1:TryResetState(CharacterState) (at Assets/Plugins/Animancer/Utilities/FSM/StateMachine1.cs:289)
    Animancer.FSM.StateMachine`1:TrySetState(CharacterState) (at Assets/Plugins/Animancer/Utilities/FSM/StateMachine1.cs:257)
    RustedGames.RespawnState:Awake() (at Assets/0_DarkARPG/Scripts/GamePlay/Behaviors/Characters/States/RespawnState.cs:13)
    UnityEngine.Object:Instantiate(GameObject, Vector3, Quaternion)
    GamingIsLove.Makinom.UnityInstantiateSetting:Instantiate(GameObject, Vector3, Quaternion)
    GamingIsLove.Makinom.UnityWrapper:Instantiate(GameObject, Vector3, Quaternion)
    GamingIsLove.ORKFramework.Combatants.CombatantObject:Spawn(SpawnPoint, Vector3)
    GamingIsLove.ORKFramework.CombatantAccessHandler:Spawn(Combatant, SpawnPoint, Vector3)
    GamingIsLove.ORKFramework.Combatants.CombatantObject:SpawnAccess(SpawnPoint, Vector3)
    GamingIsLove.ORKFramework.ORKPlayerHandler:SpawnPlayer(Int32, Vector3)
    GamingIsLove.ORKFramework.Schematics.Nodes.SpawnORKPlayerNode:Execute(Schematic)
    GamingIsLove.Makinom.Schematics.Schematic:ExecuteNextNode()
    GamingIsLove.Makinom.Schematics.Schematic:NodeFinished(Int32)
    GamingIsLove.Makinom.Schematics.Schematic:Tick()
    GamingIsLove.Makinom.MachineUpdate:Tick()
    GamingIsLove.Makinom.Maki:FireTick()
    GamingIsLove.Makinom.MakinomHandler:Update()
    *************************************************************************************************************************************
    I have tried cleaning the library folder without luck