Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Animancer - Less Animator Controller, More Animator Control

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

  1. GoldFireStudios

    GoldFireStudios

    Joined:
    Nov 21, 2018
    Posts:
    154
    Right, but how do I have it fade out over the last 0.5s of the animation rather than fading out when the OnEnd event is triggered? Is there a built-in way to do that, or do I have to separately poll the status of the animation in Update and trigger the fade at the appropriate time?
     
  2. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    Just set the end time to be 0.5s before the end of the animation. If you're using a transition, you can put "-0.5" on the end of the number in the s field and Unity will do the subtraction to calculate the value. Or in a script you could set
    state.NormalizedEndTime = 1 - 0.5f / state.Length;
     
  3. GoldFireStudios

    GoldFireStudios

    Joined:
    Nov 21, 2018
    Posts:
    154
    Ah, perfect, thanks for the help!
     
  4. JohnTomorrow

    JohnTomorrow

    Joined:
    Apr 19, 2013
    Posts:
    135
    Has anyone had a positive experience implementing Unity's animation rigging with Animacer? I created several rigs and have moved the targets out from the animator object as stated here (https://kybernetik.com.au/animancer/docs/examples/integration/animation-rigging/) but the playables are resetting the rig weights. I tried managing these weights by another script but there is still an awful flicker.
     
  5. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    Looks like I'll have to submit another bug report because they closed the last one as "By Design" and told me to move the objects out from under the Animator, but that doesn't work for the Rig or IK Constraint so it doesn't help with the weights.

    The only workaround I know of is:
    1. If your Rig is Humanoid, set animancer.Playable.KeepChildrenConnected = true;. If your Rig is Generic, it will already be true by default so you can skip this step.
    2. Call animancer.Layers[0].GetOrCreateState(...); for every animation (yes, this is very inconvenient, and you need to initialise them on the correct layer if you're using multiple layers).
     
  6. JohnTomorrow

    JohnTomorrow

    Joined:
    Apr 19, 2013
    Posts:
    135
    Venting here. Very frustrating. Unity doesn't take features to 100%. Its a consistent theme when trying to do anything complex the engine fails. They need to start developing games with their own tech to feel the pain Unity game developers feel day to day. Looks like several developers have complained about this poor design. Shame we don't have source access, this would be forgiven if it was possible to fix it ourselves.

    Anyway. Should I be concerned about any performance costs to enabling KeepChildrenConnected to true?
     
    hopeful likes this.
  7. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    I wouldn't expect any performance issues from it. Even when I benchmarked it, the difference was so small that I'm not actually certain I'm correct about which way is faster in which circumstances.
     
    JohnTomorrow likes this.
  8. JohnTomorrow

    JohnTomorrow

    Joined:
    Apr 19, 2013
    Posts:
    135
    Going through the code and adding this functionality. How should I go about adding LinearMixerStates? Should I call AddChild instead?
     
  9. JohnTomorrow

    JohnTomorrow

    Joined:
    Apr 19, 2013
    Posts:
    135
    So I've modified Animacer's source to have a StrictPlay method that checks that the state is there before playing. Otherwise it throws an exception. This way I can ensure I have cached all the states and if not it will be obvious I did not (to avoid missing bugs). Obviously it would be better if Unity fixed this bad resetting behavior with playables but if they do not maybe this is worth adding to the the source?

    Edit: Actually, I was able to just extend the logic to avoid modifying the source. In case its useful for others here's the code:

    Code (CSharp):
    1.    
    2. [DefaultExecutionOrder(-5000)]
    3.     public class ModifiedAnimacerAnimator : AnimancerComponent {
    4.         public AnimancerState StrictPlay(AnimationClip clip) {
    5.             var s = Playable.TryPlay(clip);
    6.             if (s == null) {
    7.                 throw new Exception($"Missing state {clip}");
    8.             }
    9.             return s;
    10.         }
    11.  
    12.         public AnimancerState StrictPlay(AnimationClip clip, float fadeDuration, FadeMode mode = FadeMode.FixedSpeed) {
    13.             var s = Playable.TryPlay(clip, fadeDuration, mode);
    14.             if (s == null) {
    15.                 throw new Exception($"Missing state {clip}");
    16.             }
    17.             return s;
    18.         }
    19.  
    20.         public AnimancerState StrictPlay(AnimancerState state) {
    21.             var s = Playable.TryPlay(state.Key);
    22.             if (s == null) {
    23.                 throw new Exception($"Missing state {state.Key}");
    24.             }
    25.             return s;
    26.         }
    27.  
    28.         public AnimancerState StrictPlay(AnimancerState state, float fadeDuration, FadeMode mode = FadeMode.FixedSpeed) {
    29.             var s = Playable.TryPlay(state.Key, fadeDuration, mode);
    30.             if (s == null) {
    31.                 throw new Exception($"Missing state {state.Key}");
    32.             }
    33.             return s;
    34.         }
    35.  
    36.         public AnimancerState StrictPlay(ITransition transition) {
    37.             var s = Playable.TryPlay(transition.Key, transition.FadeDuration);
    38.             if (s == null) {
    39.                 throw new Exception($"Missing state {transition.Key}");
    40.             }
    41.             return s;
    42.         }
    43.  
    44.         public AnimancerState StrictPlay(ITransition transition, float fadeDuration, FadeMode mode = FadeMode.FixedSpeed) {
    45.             var s = Playable.TryPlay(transition.Key, fadeDuration, mode);
    46.             if (s == null) {
    47.                 throw new Exception($"Missing state {transition.Key}");
    48.             }
    49.             return s;
    50.         }
    51.  
    52.         public AnimancerState StrictPlay(object key) {
    53.             var s = Playable.TryPlay(key);
    54.             if (s == null) {
    55.                 throw new Exception($"Missing state {key}");
    56.             }
    57.             return s;
    58.         }
    59.  
    60.         public AnimancerState StrictPlay(object key, float fadeDuration, FadeMode mode = FadeMode.FixedSpeed) {
    61.             var s = Playable.TryPlay(key, fadeDuration, mode);
    62.             if (s == null) {
    63.                 throw new Exception($"Missing state {key}");
    64.             }
    65.             return s;
    66.         }
    67.  
    68.         //
    69.         public AnimancerState StrictPlay(int layer, AnimationClip clip) {
    70.             var s = Layers[layer].TryPlay(clip);
    71.             if (s == null) {
    72.                 throw new Exception($"Missing state {clip}");
    73.             }
    74.             return s;
    75.         }
    76.  
    77.         public AnimancerState StrictPlay(int layer, AnimationClip clip, float fadeDuration, FadeMode mode = FadeMode.FixedSpeed) {
    78.             var s = Layers[layer].TryPlay(clip, fadeDuration, mode);
    79.             if (s == null) {
    80.                 throw new Exception($"Missing state {clip}");
    81.             }
    82.             return s;
    83.         }
    84.  
    85.         public AnimancerState StrictPlay(int layer, AnimancerState state) {
    86.             var s = Layers[layer].TryPlay(state.Key);
    87.             if (s == null) {
    88.                 throw new Exception($"Missing state {state.Key}");
    89.             }
    90.             return s;
    91.         }
    92.  
    93.         public AnimancerState StrictPlay(int layer, AnimancerState state, float fadeDuration, FadeMode mode = FadeMode.FixedSpeed) {
    94.             var s = Layers[layer].TryPlay(state.Key, fadeDuration, mode);
    95.             if (s == null) {
    96.                 throw new Exception($"Missing state {state.Key}");
    97.             }
    98.             return s;
    99.         }
    100.  
    101.         public AnimancerState StrictPlay(int layer, ITransition transition) {
    102.             var s = Layers[layer].TryPlay(transition.Key, transition.FadeDuration);
    103.             if (s == null) {
    104.                 throw new Exception($"Missing state {transition.Key}");
    105.             }
    106.             return s;
    107.         }
    108.  
    109.         public AnimancerState StrictPlay(int layer, ITransition transition, float fadeDuration, FadeMode mode = FadeMode.FixedSpeed) {
    110.             var s = Layers[layer].TryPlay(transition.Key, fadeDuration, mode);
    111.             if (s == null) {
    112.                 throw new Exception($"Missing state {transition.Key}");
    113.             }
    114.             return s;
    115.         }
    116.  
    117.         public AnimancerState StrictPlay(int layer, object key) {
    118.             var s = Layers[layer].TryPlay(key);
    119.             if (s == null) {
    120.                 throw new Exception($"Missing state {key}");
    121.             }
    122.             return s;
    123.         }
    124.  
    125.         public AnimancerState StrictPlay(int layer, object key, float fadeDuration, FadeMode mode = FadeMode.FixedSpeed) {
    126.             var s = Layers[layer].TryPlay(key, fadeDuration, mode);
    127.             if (s == null) {
    128.                 throw new Exception($"Missing state {key}");
    129.             }
    130.             return s;
    131.         }
    132.     }
    133. }
     
    Last edited: Jun 16, 2021
  10. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    Yeah, AddChild for mixers.

    You could make those extension methods so you don't need to swap out the component you're using:
    Code (CSharp):
    1. public static class Extensions
    2. {
    3.     public AnimancerState StrictPlay(this AnimancerComponent animancer, AnimationClip clip)
    4.     {
    5.         var s = animancer.TryPlay(clip);
    6.         if (s == null)
    7.             throw new Exception($"Missing state {clip}");
    8.         return s;
    9.     }
    10. }
     
    JohnTomorrow likes this.
  11. GoldFireStudios

    GoldFireStudios

    Joined:
    Nov 21, 2018
    Posts:
    154
    I'm guessing I'm doing something stupid, but I can't seem to figure out what is going on here. I'm using the hybrid component at the moment because all of the locomotion is still set up with a blend tree. I'm running an animation on the takeover layer through Animancer and then fading it out at the end. However, when the weight changes from 1 to 0, it causes the character to squat even though there is no state in the animation or in the idle that is being blended back into that is like this. As soon as weight hits 0, it goes back to normal, so it must be something in the blend. There is no other animation being played, just blending from Vault back to Idle in the animator controller.

     
  12. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    Clear the Controller field on your Animator component. Native Animator Controllers can't blend with Humanoid Rigs so you need to actually use the Hybrid component instead. I've added a warning about that which will be in the next version.
     
    hopeful and GoldFireStudios like this.
  13. GoldFireStudios

    GoldFireStudios

    Joined:
    Nov 21, 2018
    Posts:
    154
    Thanks, that partly fixed my issues. However, if the animation is on a separate layer and the animation is faded out rather than the entire layer, then the distortion still happens. The reason I'm wanting to fade out the animation rather than the layer (and again, maybe I'm thinking about this wrong) is that I've got instances where I want to fade from one animation to another in the same layer while both animations are running. If I fade the layer, then the 2nd animation ends up getting cut off because the layer is being faded out while that animation is starting to play.

     
  14. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    Layer mixing is different from the mixing of states within a layer. I don't know exactly what they do internally, just that one uses an AnimationLayerMixerPlayable while the other uses an AnimationMixerPlayable so any differences in the way they blend is likely by design and can't be modified by Animancer.

    Calling the Play method with a fade duration automatically calls StartFade(1, fadeDuration); which will fade the layer back in if it was fading out. Have you actually tried it to see if it gives result you want?
     
    Last edited: Jun 17, 2021
  15. GoldFireStudios

    GoldFireStudios

    Joined:
    Nov 21, 2018
    Posts:
    154
    Yes, that is how I had it set up originally and the 2nd animation that was supposed to be fading in only played for a fraction of a second and then stopped unless I removed the fade out of the prior animation. That is what led me to trying the above and running into this different issue.
     
  16. GoldFireStudios

    GoldFireStudios

    Joined:
    Nov 21, 2018
    Posts:
    154
    After thinking through what you said about the fading and how it should take over, I dug through the code again and found that my callback triggering the next animation was on the line before the fade out rather than after. Swapping those two lines fixed the issue. Still curious what the internal difference is with states/layers, but thankfully solving that doesn't appear necessary in this case. Thanks again for the help!
     
  17. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    The main differences we can see externally are that layers can be either Override or Additive and can have a LayerMask. Based on the behaviour we're seeing, I would guess that it works something like this:
    • Base Layer: Weight = 1
      • Player Animator: Weight = 1
    • Takeover Layer: Weight = 1
      • Vault: Weight = 0.5
    The layer mixer sees that Takeover has 1 weight and is set to Override so it uses 100% of that layer, leaving 0% of the Base Layer. But with the Vault state at only 0.5 weight, that means the final output actually only has 0.5 weight. And when a Humanoid Rig doesn't have everything in the final output add up to 1 weight, the model ends up proportionally closer to this hunched over pose which is based on the muscle limits in the avatar definition:
     
  18. GoldFireStudios

    GoldFireStudios

    Joined:
    Nov 21, 2018
    Posts:
    154
    Ah, yes that makes perfect sense!
     
  19. JohnTomorrow

    JohnTomorrow

    Joined:
    Apr 19, 2013
    Posts:
    135
    I have been integrating Animation Rigging with Animacer. After doing the workarounds mentioned I am able to set targets and weights which is good. However there is a small issue. In my use case I am using IK to position hands while playing a climbing animation. The hands position correctly but there is a very minor but noticeable jitter occurring on the animation where the hands are placed. I have not worked directly with playables yet but I am assuming this is happening due to different update rates between Animacer and Animation Rigging since they both have their own playable graphs. How would you recommend to go about fixing this issue? Should I attempt to add the animation rigging nodes to the Animacer graph? I started looking through the package code and it seems this will be possible.
     
  20. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    I wouldn't expect that to be necessary since a problem like that should show up with a regular Animator Controller + Rigging setup as well, in which case it wouldn't be possible to add the Rigging to the Animator Controller's graph and surely someone would have noticed a problem like that by now.

    Animancer doesn't do anything special to set the update rate, I just build a PlayableGraph and tell it to play and Unity takes care of updating it during the animation update of that object and I'm pretty sure Animation Rigging is supposed to work the same because it takes the output of one graph (either Animancer's or a regular Animator Controller one) to use as input into its graph so it should also be running at that same rate.

    Are you using Animate Physics mode on your Animator component? Maybe Animancer is running in that mode but Animation Rigging isn't for some reason.
     
  21. JohnTomorrow

    JohnTomorrow

    Joined:
    Apr 19, 2013
    Posts:
    135
    I have tried normal and animated physics. It didn't change the jitter :(
     
  22. JohnTomorrow

    JohnTomorrow

    Joined:
    Apr 19, 2013
    Posts:
    135
  23. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    I can't think of anything else to try. If you want to make a minimal reproduction project and send it to me I'd be happy to take a closer look (and probably report a Unity bug for it).
     
  24. JohnTomorrow

    JohnTomorrow

    Joined:
    Apr 19, 2013
    Posts:
    135
    Took a look at my other systems. It was caused by interpolation smoothing on the character controller. :facepalm:
     
    hopeful likes this.
  25. Ksanone

    Ksanone

    Joined:
    Feb 7, 2015
    Posts:
    39
    Hi, thanks for the product, makes a lot of sense, better than Mecanim, but I can't figure out why my animations won't fade, any pointers to my error would be appreciated

    Code (csharp):
    1.  
    2. public AnimancerComponent anim;
    3. public MixerState.Transition2D _Move;   //inspector loaded with idle, strafe forward,back,left,right animations - fade value set in inspect - idle set to loop and not sync
    4.  
    5. void Awake()
    6. {
    7.     anim.States.GetOrCreate(_Move);
    8. }
    9.  
    10. void Start()
    11. {
    12.     anim.Play(_Move, 0.5f);  //fade value put here just in case
    13. }
    14.  
    15. void Update()
    16. {
    17.     _Move.State.Parameter = CharacterActions.movement.value; //correct animation plays but no fade between - movement.value returns a Vector2 which is roughly the same as Unity input.getaxis horizontal and vertical
    18. }
    19.  
    I have tried
    anim.Play(_Move) in Update
     
  26. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    The fade duration applies to the transition between the previous state and that mixer state, it has nothing to do with setting the parameter. The Smoothing section on the Mixers page explains how you can smoothly modify the parameter.
     
  27. Ksanone

    Ksanone

    Joined:
    Feb 7, 2015
    Posts:
    39
    so obvious, thanks

    dope product by the way, Unity should buy you up.
     
    Last edited: Jun 20, 2021
    Kybernetik likes this.
  28. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    I've added that link to the tooltip for the next version:
    Capture.PNG
     
    Ksanone likes this.
  29. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
  30. hopeful

    hopeful

    Joined:
    Nov 20, 2013
    Posts:
    5,625
    Is there any trick people can do, like saving off transition meta files, upgrading, then bringing those meta files back in?
     
  31. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    I just added a couple of ideas for potential ways to keep the data.
     
  32. jimmygladfelter

    jimmygladfelter

    Joined:
    May 3, 2020
    Posts:
    83
    Im trying to only update a parameter of a LinearStateMixer when it loops playing ...aka when normalized time == 0, however, normalized time keeps going up and doesnt seem to reflect the actual normalized time. I see it looping in the inspector but I dont know when I can get the property I need to check against this.

    upload_2021-6-26_12-33-6.png

    upload_2021-6-26_12-35-37.png
    Here's a look at the properties of the mixerstate at runtime:
    upload_2021-6-26_12-36-23.png

    My animations are set to loop and I have the pro version of animancer.
    Any ideas?
     

    Attached Files:

  33. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
  34. jimmygladfelter

    jimmygladfelter

    Joined:
    May 3, 2020
    Posts:
    83
  35. prhzn

    prhzn

    Joined:
    May 11, 2020
    Posts:
    5
    I'm attempting something similar to this, where I have an attack animation playing in a secondary layer, and I want to fade in/out from it. Like above, I want to ensure the fade ends exactly at the end of the underlying clip.

    However, in my case, the speeds of different states can change dynamically. If the attack speed changes mid-transition, it seems the fade will no longer align correctly with the new duration.

    Any advice for handling this situation?
     
  36. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    Whenever you change the speed, try something like this:
    Code (CSharp):
    1. var speed = state.Speed;
    2. state.Speed = ...
    3. state.FadeSpeed *= state.Speed / speed;
     
  37. prhzn

    prhzn

    Joined:
    May 11, 2020
    Posts:
    5
    Nice, didn't notice FadeSpeed. I'll give it a shot.
     
  38. prhzn

    prhzn

    Joined:
    May 11, 2020
    Posts:
    5
    Hey, another Animancer question...
    Is additive blending of timelines something that's expected to work?

    As an example, I have one timeline looping a walk cycle. And I want to play a hit reaction animation in an additive layer--also stored as a timeline, this time with an avatar mask set on the animation track so only the upper body is affected.

    I'm getting unwanted translation/rotation of my character when I trigger the additive animation. I was thinking this could be related to how Timeline offsets things, but no combination of "Track Offsets" settings seems to change the behavior.

    If I use an animator controller with the same clips and mask from the timelines, everything looks correct.

    I'd be grateful for any pointers here.
     
  39. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    I don't know of any specific reason why it wouldn't work.

    What if you use the clips and masks directly in Animancer without Timeline?
     
  40. Houtamelo

    Houtamelo

    Joined:
    Jan 3, 2020
    Posts:
    31
    I have a specific use case where i have a bunch of animations and each animation has a defined "trigger time", which is the time it is supposed to trigger an action (such as in the middle of a punch, it needs to damage the object being punched), the issue is that i cannot realibly use events callbacks for this since i need to yield until that "trigger time" comes, the solution i came up with is returning a custom IEnumerator that waits until the state stops playing or the state.Time is bigger than the "trigger time".
    Here's the code:

    Code (CSharp):
    1. public class HoutaAnimancer : MonoBehaviour
    2.     {
    3.         [SerializeField] private AnimancerComponent animancerComponent;
    4.         public IEnumerator WaitUntilTrigger(HoutaTransition houtaTransition)
    5.         {
    6.             AnimancerState currentState = animancerComponent.Play(houtaTransition);
    7.             return MiddleEnumerator(currentState, houtaTransition.TriggerTime);
    8.  
    9.             IEnumerator MiddleEnumerator(AnimancerState state, float triggerTime)
    10.             {
    11.                 while (state.IsPlaying && state.NormalizedTime < triggerTime)
    12.                     yield return null;
    13.             }
    14.         }
    15.     }
    16.  
    17. [Serializable]
    18.     public class HoutaTransition : ClipState.Transition
    19.     {
    20.         [field: SerializeField] public float TriggerTime { get; private set; }
    21.     }
    I just bought your asset so i'm still learning the ropes, my question is, i am over engineering this problem? Or does your asset provides an easier way of doing this, another issue i have is that in some cases instead of having a single ITransition that has this "trigger time" i have a sequence of animationClips that need to be played in an order and a single clip in the middle of that sequence that also have this "trigger time". Is the better solution simply to inherit AnimancerState and code this from the ground up?
     
  41. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    I would likely use Animancer Events for something like that rather than coroutines. That should also make it easier to handle a sequence of animations where only one of them has a trigger time.
     
    Houtamelo likes this.
  42. prhzn

    prhzn

    Joined:
    May 11, 2020
    Posts:
    5
    Clips and masks without Timeline look correct. I also tried placing the mask on the Animancer layer rather than the Timeline track, and that fixed my rotation/offset issue. But there are still issues with the blending.

    I attached a gif showing the issue in a sample project and the code that produced it (Animancer not included).
     

    Attached Files:

  43. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    It looks like additive blending is the problem. With just the mask they both look identical.

    Unfortunately I don't think we'll be able to do anything about it on our end because all that low level blending is handled internally by the Playables API.

    I'll submit a bug report for it, but I wouldn't expect a fix any time soon.
     
    prhzn likes this.
  44. Rob-Fireproof

    Rob-Fireproof

    Joined:
    Dec 18, 2012
    Posts:
    55
    Hi there,

    I'm after a little bit of advice on the best way to go about something. Say I've got a character who I want to have playing a looping idle at the start of the level. At some point, he'll be triggered to play a timeline, where there will be a combination of full-body animations with masked facial animations played on him on various layers, with blending between clips and all that good stuff. At the end of the timeline, he'll go back to an idle. (The timeline will also have, say, audio being triggered, explosions going off, etc.)

    As far as I can tell there's no built-in way to control Animancer from timeline (like, an equivalent of the AnimationTrack but which operates on Animancer layers). So one option is to write a new track type for that, but making it work nicely with blending, and work nicely in the editor to preview the sequence, sounds like a pretty big job. Has anyone tried that sort of thing?

    My alternative thought is that maybe Animancer isn't really suited to that sort of thing, and I should go back to Unity's built-in system.

    Any thoughts?

    Thanks!
    Rob.
     
  45. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    A PlayableAssetState.Transition will let you reference the Timeline Asset and set its Bindings like you normally would on a PlayableDirector, except that you don't specify a binding for the first Animation Track because it automatically gets used on the character that plays it. Then you can just play it like the Introduction in the Platformer example. So the Timeline isn't actually controlling Animancer as such, but its animation output is used by Animancer just like any other state.
     
  46. AnemicPizza

    AnemicPizza

    Joined:
    Aug 17, 2017
    Posts:
    6
    Hello,
    Trying to expand upon
    Examples: 06 State Machines: 06 Weapons
    To incorporate Combo Branches/ Delayed Combos depending when you give an input during an animation.
    i.e. You have a 3 Hit Combo
    1 --> 2a --> 3a
    But if you don't press an input until after like 60% first animation already playing, it'll change the combo to.
    1 --> 2b --> 3b

    Main Question: Checking how long an animation has already played.
     
  47. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    Creature.Animancer.States.Current.Time will tell you how long it has been played. Or if you want 60%, you'd use ...NormalizedTime > 0.6f.

    But if the exact time is going to vary depending on the animation, you'd be better off using an Animancer Event so that you can adjust its timing using the transition preview window. You could simply add a
    private bool _IsNearEnd
    in the AttackState class, set it to false when the state is entered, and have the event set it to true.
     
  48. hibbzy

    hibbzy

    Joined:
    Jul 30, 2018
    Posts:
    5
    I'm making a first person shooter with dual wielding, I'm using the Animancer Finite State Machine.

    Each weapon is its own state and each action the weapon can do (equip,unequip,idle,use,etc) is its own state.

    for my dual wielding I'm using 2 separate state machines. one for left arm, one for right arm.

    when i equip a weapon i store what arm/statemachine is using it in an int so that i can play the associated animations.

    it works however its very messy having if statements everywhere.

    Code (CSharp):
    1.   if (_Equipment.Hand == 1)
    2.         {
    3.               _Arms.Layers[1].Play(L_anim)
    4.         }
    5.         else if (_Equipment.Hand == 2)
    6.         {
    7.              _Arms.Layers[2].Play(R_anim);
    8.  
    9.         }
    Is there a way to get what statemachine the state is currently being used by? it would allow me to simplify a lot of my code.
     
  49. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    For that specific code block, you could replace it with
    _Arms.Layers[_Equipment.Hand].Play(R_anim);
    but that might not work for other situations.

    You could make a class like this:
    Code (CSharp):
    1. public class HandStateMachine : StateMachine<YourState>
    2. {
    3.     public AnimancerLayer layer;
    4.  
    5.     // Whatever constructors you need.
    6. }
    Then simply set the layer when you create each HandStateMachine and change
    _Equipment.Hand
    to be a reference to the HandStateMachine instead of an int.

    Another option would be to give your character a GetStateMachine method which you give a state and it just checks if that's the current state in either hand and returns the machine for that hand.
     
  50. AnemicPizza

    AnemicPizza

    Joined:
    Aug 17, 2017
    Posts:
    6
    Code (CSharp):
    1.         private void OnEnable()
    2.         {
    3.             Creature.Animancer.Animator.applyRootMotion = true;
    4.             var animation = _Equipment.Weapon.AttackAnimations[0];
    5.            
    6.  
    7.             if (ShouldRestartCombo())
    8.             {
    9.                 _AttackIndex = 0;
    10.                 _IsNearEnd = false;
    11.             }
    12.             else
    13.             {
    14.                 _AttackIndex++;
    15.  
    16.                 if (Creature.Animancer.States.Current.NormalizedTime < 0.6f)
    17.                 {
    18.                     Debug.Log("NormalizedTime: " + Creature.Animancer.States.Current.NormalizedTime);
    I'm fooling around with input being read at certain intervals in an animation. Looks like I'm only getting values close to 0 and never close to 1 no matter when I press the Attack Key to enter AttackState. I'm a bit unsure why this is happening.
    upload_2021-7-3_22-53-10.png
     

    Attached Files: