Search Unity

Animancer - Less Animator Controller, More Animator Control

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

  1. zedz

    zedz

    Joined:
    Aug 31, 2013
    Posts:
    250
    OK I thought I had solved my separation of the torso from the legs problem (until 5 minutes later I noticed there was a case where it failed) :confused:
    Heres my code, Does anyone have any ideas where its failing
    You will have my eternal gratification

    Code (CSharp):
    1. [SerializeField] private AvatarMask _LegsMask;
    2.         [SerializeField] private AvatarMask _ActionMask;
    3.  
    4.         /************************************************************************************************************************/
    5.  
    6.         private const int BaseLayer = 0;
    7.         private const int LegsLayer = 1;
    8.         private const int ActionLayer = 2;
    9.  
    10.         private const float FadeDuration = 0.25f;
    11.  
    12.         /************************************************************************************************************************/
    13.  
    14.         private void OnEnable()
    15.         {
    16.  
    17.             _LayeredAnimancer.Play(_Idle);
    18.  
    19.             _LayeredAnimancer.Layers[LegsLayer].SetMask(_LegsMask);
    20.             _LayeredAnimancer.Layers[LegsLayer].SetName("Legs Layer");
    21.  
    22.             _LayeredAnimancer.Layers[ActionLayer].SetMask(_ActionMask);
    23.             _LayeredAnimancer.Layers[ActionLayer].SetName("Action Layer");
    24.  
    25.             _LayeredAnimancer.States.Create("Idle", _Idle );
    26.             _LayeredAnimancer.States.Create("Run", _Run );
    27.             _LayeredAnimancer.States.Create("Action", _Action );
    28.  
    29.             _LayeredAnimancer.Layers[LegsLayer].CreateState("LegsIdle", _Idle );
    30.             _LayeredAnimancer.Layers[LegsLayer].CreateState("LegsRun", _Run );
    31.             //_LayeredAnimancer.Layers[LegsLayer].CreateState("LegsAction", _Action );
    32.  
    33.             //_LayeredAnimancer.Layers[ActionLayer].CreateState("TorsoIdle", _Idle );
    34.             //_LayeredAnimancer.Layers[ActionLayer].CreateState("TorsoRun", _Run );
    35.             //_LayeredAnimancer.Layers[ActionLayer].CreateState("TorsoAction", _Action );
    36.         }
    37.  
    38.         /************************************************************************************************************************/
    39.  
    40.         private bool _IsAction;
    41.         private bool _IsRunning;
    42.  
    43.         public void ToggleRunning()
    44.         {
    45.             // Swap between true and false.
    46.             _IsRunning = !_IsRunning;
    47.  
    48.             if ( _IsAction )
    49.             {   var animation = _IsRunning ? "LegsRun" : "LegsIdle";
    50.                 var state = _LayeredAnimancer.Play(animation, FadeDuration);
    51.                 state.Events.OnEnd = () => LegsFinished();
    52.             }
    53.             else
    54.             {   var animation = _IsRunning ? "Run" : "Idle";
    55.                 _LayeredAnimancer.Play(animation, FadeDuration);
    56.             }
    57.         }
    58.  
    59.         public void LegsFinished() {
    60.  
    61.             _LayeredAnimancer.Layers[LegsLayer].StartFade(0, FadeDuration);
    62.  
    63.            // if ( _IsRunning )
    64.            //   _LayeredAnimancer.Play( "Run", FadeDuration);
    65.            // else
    66.            //   _LayeredAnimancer.Play( "Idle", FadeDuration);
    67.         }
    68.  
    69.         /************************************************************************************************************************/
    70.  
    71.         public void PerformAction()
    72.         {
    73.             if ( _IsAction )
    74.                 return;
    75.             _IsAction = true;
    76.             if (_IsRunning)
    77.             {
    78.                 var state = _LayeredAnimancer.Layers[ActionLayer].Play( "Action", FadeDuration, FadeMode.FromStart);
    79.                 state.Events.OnEnd = () => ActionFinished();
    80.             }
    81.             else
    82.             {
    83.                 var state = _LayeredAnimancer.Play( "Action", FadeDuration, FadeMode.FromStart);
    84.                 state.Events.OnEnd = () => ActionFinished();
    85.             }
    86.         }
    87.  
    88.         public void ActionFinished() {
    89.  
    90.             _IsAction = false;
    91.  
    92.             _LayeredAnimancer.Layers[ActionLayer].StartFade(0, FadeDuration);
    93.             _LayeredAnimancer.Layers[LegsLayer].StartFade(0, FadeDuration);
    94.  
    95.             if ( _IsRunning )
    96.                 _LayeredAnimancer.Play( "Run", FadeDuration);
    97.             else
    98.                 _LayeredAnimancer.Play( "Idle", FadeDuration);
    99.         }
    EDIT I think I might have it!
    So may be of use to others, I can not believe it took me over 100 hours to come up with this

    Code (CSharp):
    1. [SerializeField] private AvatarMask _LegsMask;
    2.         [SerializeField] private AvatarMask _ActionMask;
    3.  
    4.         /************************************************************************************************************************/
    5.  
    6.         private const int BaseLayer = 0;
    7.         private const int LegsLayer = 1;
    8.         private const int ActionLayer = 2;
    9.  
    10.         private const float FadeDuration = 0.25f;
    11.  
    12.         /************************************************************************************************************************/
    13.  
    14.         private void OnEnable()
    15.         {
    16.  
    17.             _LayeredAnimancer.Play(_Idle);
    18.  
    19.             _LayeredAnimancer.Layers[LegsLayer].SetMask(_LegsMask);
    20.             _LayeredAnimancer.Layers[LegsLayer].SetName("Legs Layer");
    21.  
    22.             _LayeredAnimancer.Layers[ActionLayer].SetMask(_ActionMask);
    23.             _LayeredAnimancer.Layers[ActionLayer].SetName("Action Layer");
    24.  
    25.             _LayeredAnimancer.States.Create("Idle", _Idle );
    26.             _LayeredAnimancer.States.Create("Run", _Run );
    27.             _LayeredAnimancer.States.Create("Action", _Action );
    28.  
    29.             _LayeredAnimancer.Layers[LegsLayer].CreateState("LegsIdle", _Idle );
    30.             _LayeredAnimancer.Layers[LegsLayer].CreateState("LegsRun", _Run );
    31.         }
    32.  
    33.         /************************************************************************************************************************/
    34.  
    35.         private bool _IsAction;
    36.         private bool _IsRunning;
    37.  
    38.         public void ToggleRunning()
    39.         {
    40.             _IsRunning = !_IsRunning;
    41.  
    42.             if ( _IsAction )
    43.             {   var animation = _IsRunning ? "LegsRun" : "LegsIdle";
    44.                 var state = _LayeredAnimancer.Play(animation, FadeDuration);
    45.             }
    46.             else
    47.             {   var animation = _IsRunning ? "Run" : "Idle";
    48.                 _LayeredAnimancer.Play(animation, FadeDuration);
    49.             }
    50.         }
    51.  
    52.         public void PerformAction()
    53.         {
    54.             if ( _IsAction )
    55.                 return;
    56.             _IsAction = true;
    57.             if (_IsRunning)
    58.             {
    59.                 var state = _LayeredAnimancer.Layers[ActionLayer].Play( "Action", FadeDuration, FadeMode.FromStart);
    60.                 state.Events.OnEnd = () => ActionFinished();
    61.             }
    62.             else
    63.             {
    64.                 var state = _LayeredAnimancer.Layers[BaseLayer].Play( "Action", FadeDuration, FadeMode.FromStart);
    65.                 state.Events.OnEnd = () => ActionFinished();
    66.             }
    67.         }
    68.  
    69.         public void ActionFinished() {
    70.  
    71.             _IsAction = false;
    72.  
    73.             _LayeredAnimancer.Layers[ActionLayer].StartFade(0, FadeDuration);
    74.             _LayeredAnimancer.Layers[LegsLayer].StartFade(0, FadeDuration);
    75.  
    76.             if ( _IsRunning )
    77.                 _LayeredAnimancer.Play( "Run", FadeDuration);
    78.             else
    79.                 _LayeredAnimancer.Play( "Idle", FadeDuration);
    80.         }
    81.  
     
    Last edited: May 13, 2020
  2. fujindevil

    fujindevil

    Joined:
    Oct 3, 2012
    Posts:
    71
    Initializing a LinearMixerState with an array of Animationclips throws a StackOverflowException at:

    Code (CSharp):
    1. Animancer.LinearMixerState.Initialise (UnityEngine.AnimationClip[] clips, System.Single minThreshold, System.Single maxThreshold) (at Assets/Plugins/Animancer/Internal/Mixer States/LinearMixerState.cs:31)
     
  3. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Looks like it's just using the wrong overload. Changing that line to
    base.Initialise(clips);
    should fix it.
     
  4. ILJI

    ILJI

    Joined:
    Nov 1, 2016
    Posts:
    6
    Hey, it's been a while - just getting back to the Unity playground, thinking about implementing Animancer again. ;)
    Before heading in the wrong direction, maybe you can give me a hint: I would like to use a keyboard brain and use Foot IK as well. I guess the More Brains example could be a good starting point having the brain set up already. Now I was wondering, where/how should I add the Foot IK code - to an existing script or as aeparate one - do you have a recommendation? Or is it easier the other way around, starting from the Uneven Ground example and adding the keyboard brain? Sorry, I am still a noob/novice at scripting and don't want to get lost right away trying to set this up...
    Thanks!
     
  5. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    The More Brains example is the more complex of the two so it's probably a more useful starting point.

    Deciding whether to use a separate script or not is mostly a question of how separate you want the new system to be. Does it actually directly relate to any of the existing scripts in a way that will make it notably easier to implement as part of them or will it be easy enough to set up whatever communication you need between it and the other scripts?
     
    ILJI likes this.
  6. ILJI

    ILJI

    Joined:
    Nov 1, 2016
    Posts:
    6
    Thanks for your suggestion! I will try to set this up today and think about the separation again. Making use of the Foot IK for the idle and locomotion states will probably point me to a suitable scenario.
     
  7. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Hey everyone, I've recently started work on Animancer v5.0 and just wanted to let you know that I'm dropping support for Unity versions older than 2018.4 LTS (2017.4 will be deprecated by the time Animancer v5.0 is ready for release). This has several benefits:
    • I can start looking more seriously into the Animation Jobs system. I just made a post about How to use Animation Jobs in Animancer which explains how you can do it in v4.4 (requires Animancer Pro since you need to add a method to one of the internal scripts).
    • I can also use the .NET 4.x features like expression bodied methods, ref returns, out variables, and nameof expressions. This has no real effect on the public API, but makes some areas of the source code significantly cleaner.
    • Easier for me to update. Uploading v4.4 took over an hour of constant fiddling around because I had to import and test it in 6 different Unity versions for both the Lite and Pro versions. Going forward I'll only be supporting the two LTS versions and latest non-Beta release.
    • Easier for me to support. Loads of things changed in the Playables API between 2017.1 and 2018.3 which was a massive pain to handle, both in the API differences and the behaviour differences which lead to bugs.
    If you have any feature suggestions for Animancer v5.0, now's the time to let me know (preferably using Github Issues since it's much easier to manage individual topics instead of having everything in a single thread here).
     
    hopeful and ILJI like this.
  8. chrisk

    chrisk

    Joined:
    Jan 23, 2009
    Posts:
    704
    Hi, it's nice to know you are working on improving Animancer constantly.
    For me, one show-stopper is the ability to use Animancer with the exisiting Mechanim Animator.
    The reason is that I use a 3rd-party controller and it is very difficult to convert them to Animancer.
    Please let me know if it is possible to support this feature in 5.0.
    Thanks.
     
  9. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Unfortunately the regular Animator control methods don't work with the Playables API and there's nothing I can do about that, but most of the exact same methods exist in HybridAnimancerComponent so it should be easy to just change the 3rd party system to use that instead of an Animator.
     
  10. craigjwhitmore

    craigjwhitmore

    Joined:
    Apr 15, 2018
    Posts:
    135
    When previewing a Float1ControllerState.Transition. I can't change the parameter to view the transition blend in the preview.

    The parameter is changed, but when Play is pressed, the parameter value resets to 0. If I pause the animation while playing, then set the parameter value, as soon as I move the animation time slider (red line in blue animation timeline slider) the parameter value again resets to 0.

    How do I preview the transition for a particular parameter value without the parameter resetting?

    animancer 1.png
     
  11. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    That's a pretty simple bug. When doing either of those things it needs to recreate its playable in case you have assigned a different Animator Controller or modified it in the Animator window, so I'll just need to make it store the parameter values beforehand and re-assign them afterwards.
     
    hopeful likes this.
  12. petey

    petey

    Joined:
    May 20, 2009
    Posts:
    1,824
  13. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Nothing inbuilt, but since you can control the weights of everything you could write a separate script to do it.

    It's on my to do list for v5.0 though. I'm not sure how it will work yet, so any suggestions for how you think it should be implemented are welcome.
     
    hopeful likes this.
  14. craigjwhitmore

    craigjwhitmore

    Joined:
    Apr 15, 2018
    Posts:
    135
    Just some thoughts on the subject.

    Perhaps a variant of
    StartFade
    function to take in an enumerated fade mode, where the fade type could be decided upon, whether it be linear or hyperbolic etc.
    A good blend function might be a sigmoid which yields a range between 0 < y < 1, it gradually blends in and out of the function at the limits.
     
  15. greengremline

    greengremline

    Joined:
    Sep 16, 2015
    Posts:
    183
    Hey, love this piece of software so much! Had a quick question - if I want to fire an end event on a looping animation but only fire it once each time the animation loops, how would you recommend going about that?

    I tried this but no dice
    Code (CSharp):
    1.     public override void OnEnterState() {
    2.       fireState = gunAnimator.animancer.Play(_fireAnimation);
    3.       _fireAnimation.Events.Sequence.SetCallback(0, OnFireEvent);
    4.       fireState.Events.OnEnd += OnAnimationComplete;
    5.     }
    6.  
    7.     private void OnAnimationComplete() {
    8.       Debug.LogError("Firing state: On animation complete");
    9.       if (clip.IsEmpty() || chamberEmpty || !gunAnimator.isFiring) {
    10.         gunAnimator.stateController.stateMachine.ForceSetState(gunAnimator.stateController.idleState);
    11.       }
    12.       fireState.Events.OnEnd -= OnAnimationComplete;
    13.       fireState.Events.OnEnd += OnAnimationComplete;
    14.     }
    15.  
     
  16. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    The whole point of end events is that they get called every frame after the time is passed to ensure that you don't get stuck in an unexpected state if you happen to register the event too late. If you don't want it to do that then you can just use a regular event like you are for the OnFireEvent.
     
  17. petey

    petey

    Joined:
    May 20, 2009
    Posts:
    1,824
  18. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    I'm leaning towards implementing curved fading as an entirely separate script in order to avoid affecting the performance of people who don't use it. That would also allow it to be much more flexible because it could have several different variations (an enum of easing functions, animation curves, or even a custom delegate).

    I'd also be interested to try something like the DampingJob from the Animation Job Samples (using the technique for adding it to Animancer I described in the post I linked earlier) to give individual bones some actual momentum during the transition rather than just fading based on an arbitrary curve. But I doubt I'll actually get to it with how long my to do list already is.
     
    hopeful and petey like this.
  19. Thoranar

    Thoranar

    Joined:
    Oct 5, 2013
    Posts:
    22
    I'm working on a locomotion system that has Idle, walk start, walk loop, and walk stop. While I'm able to correctly call all the associated animations based on the character movement, I'm for some reason not able to reset the Walk Start or Walk Loop mixers time to equal 0. I've tried various approaches of state.time = 0, normalized time = 0, etc. but none of that seems to have any effect on the mixers. Am I not able to modify the start time on the mixer? Do I need to loop through all the underlining animation/states and set them each to time = 0?

    My setup is currently:
    • Idle - solo clip
    • Walk start - LinerMixerTransition - This is setup as a liner blend tree because I have start walk forward, start walk left, start walk right, etc. It's driven by a turn value parameter
    • Walk loop - traditional directional mixerstate
    • Walk stop - I actually have two walk stop solo clips. One for left foot stop, one for right foot stop. This is controlled by the normalized time % 1 of the walk loop to determine which one to play.
    All the logic/transitions work great, the only issue again is that because the walk loop and walk start do not both start at time=0, the character does not look right when transitioning.
     

    Attached Files:

  20. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    You should be able to, but I'm currently rewriting the time control system for v5.0 because the current version has a couple of bugs which don't have simple solutions, one of which causes mixers to sometimes ignore the time you set.

    As a temporary workaround, you can put this method in the MixerState class and use it to set the time instead of the regular property:
    Code (CSharp):
    1.     public void SetNormalizedTimeImmediate(float value)
    2.     {
    3.         for (int i = ChildCount - 1; i >= 0; i--)
    4.         {
    5.             var child = GetChild(i);
    6.             if (child == null)
    7.                 continue;
    8.    
    9.             child._Playable.SetTime(value * child.Length);
    10.         }
    11.     }
    12.  
    There are a bunch of things that won't work for (like nested mixers), but for what you've described it should be fine. Also, if you're using Unity's regular Animation Events you'll need to call SetTime twice in a row per child to avoid triggering events between the old and new time.
     
  21. Thoranar

    Thoranar

    Joined:
    Oct 5, 2013
    Posts:
    22
    Worked like a charm. Thank you!
     
  22. petey

    petey

    Joined:
    May 20, 2009
    Posts:
    1,824
    Hi,
    I'm using mirrored animations throughout my game and I thought I'd check this workflow. I was just thinking of setting up a bunch of linear mixers for the mirrored clips and using them as states.
    So rather than playing a specific clip, I'd play a LinearMixer and use the parameter to blend the the current animation.
    Does that sound okay? Thanks!
     
  23. KristianDoyle

    KristianDoyle

    Joined:
    Feb 3, 2009
    Posts:
    63
    I've no idea why the hate on how Unity (and those ex-Motion Builder guys who know the domain like no others) implemented a very nice visual state solution for controlling how and when your data blends. If you compare it to the best alternatives out there - I'd say, Natural Motion morpheme would have been the gold standard until they discontinued the 3rd party product thing .. well - it's completely comparable. It just is. If you understand that you really should never try to build game logic in your animation state representation - I think that simply solves 80% of the issues people have, and the 'selling points' stated for Animancer (great name btw). The other 20% might be dealing with a few powerful root motion functionality choices you get through the individual clip properties and avatar setup choices you make. It's been a clean, working solution since it arrived I think, and just got better - and I've done maybe 20 nicely complex projects with it. Now, if you want to simply not deal with learning the visual system made for you, and learning how to manage everything from your game state which simply directs the controller (be a technical animator), and instead want a code solution for all your control needs (be more like a programmer who is sure they don't want visual tools) - I think that's great, and this one looks fantastic. Unity themselves have just presented another route to realtime performance which is Kinematica - which also happens to involve bypassing visual state, blendtrees and layers type thing - however they rightly explain that it's a divergence from a method, rather than the implementation of that system being crap.
     
  24. Kybernetik

    Kybernetik

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

    There's no reason why it wouldn't work, but if you don't actually want to mix the two animations then it might not be the most convenient or efficient approach.

    Another way to do it would be to make a custom transition class like in the Root Motion example and give it a second AnimationClip field then have it choose which one to use in the CreateState method and Key property. That way you could easily share the same transition settings and events for both animations.

    It might also be possible for me to implement an Animation Job to mirror animations at runtime for v5.0 so you don't need to.
     
    hopeful likes this.
  25. petey

    petey

    Joined:
    May 20, 2009
    Posts:
    1,824
    Hey thanks @Kybernetik, I do blend back and forth between them a bit so I might try the mixer approach.
     
  26. Kybernetik

    Kybernetik

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

    The Why Replace Mecanim page in the documentation gives a thorough explanation of my objections to the Animator Controller system. If you have any counter-arguments I'd love to hear them.

    As for many of the selling points being the same ... of course they are. If you compare any two systems with the same general purpose they they are going to have many similar features. I can't simply base all my descriptions on the differences between Animancer and Mecanim because not everyone is actually familiar with Mecanim. Many users can clearly see the problems with that system before they waste much time on it. If you want to compare the differences between systems then you should take a look at the Features page.
     
  27. petey

    petey

    Joined:
    May 20, 2009
    Posts:
    1,824
    Okay, I've run into something that looks like it might be a bug.
    If I keep hitting this button, the regular clip always plays, while sometimes the mixer clip doesn't :confused:

    Here's my code -
    Code (CSharp):
    1.     [SerializeField] private AnimancerComponent animancer;
    2.     [SerializeField] private AnimancerComponent animancer2;
    3.     [SerializeField] private AnimationClip clip;
    4.     public LinearMixerState mixedClip;
    5.  
    6.     void Awake()
    7.     {
    8.         mixedClip = new LinearMixerState();
    9.         mixedClip.Initialise(clip, clip);
    10.         mixedClip.SynchroniseChildren = new bool[] { false, false };
    11.     }
    12.  
    13.     public void Input_Test_A(InputAction.CallbackContext context)//New input system button press <<
    14.     {
    15.         if (context.phase != InputActionPhase.Performed) return;
    16.  
    17.         animancer.Play(clip).Time = 0;
    18.         animancer2.Play(mixedClip).Time = 0;
    19.     }
    May-27-2020 16-18-20.gif
    (The mixer character is on the left)
     
  28. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    hopeful likes this.
  29. petey

    petey

    Joined:
    May 20, 2009
    Posts:
    1,824
    Cool, that seems to work better! Hey not sure if it helps, but this only seems to happen for me when the inspector for that Animancer component is visible on screen.
     
  30. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    It's a bug in the system that caches the playable time and passes new values to the mixer's children so it's affected by the Inspector which views the same value, but that's not the root cause.
     
    petey likes this.
  31. Thoranar

    Thoranar

    Joined:
    Oct 5, 2013
    Posts:
    22
    On that note, looks like the inspector is causing playback issues as well. I was wondering why certain animations were not performing correctly, in particular turn animations, and I noticed that if I did not have the animancer component inspector up then the animations correctly played. Here is a video showing what I mean
     
  32. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    So you have a 90 degree turn animation which only goes about half as far when it's selected?

    The root cause is likely the same system, though off the top of my head I can't see how it could be doing that.

    If you can isolate the issue with a simple script and email it to animancer@kybernetik.com.au along with any required animations, I'll make a unit test for it so I can ensure that it gets fixed when I rewrite the system for Animancer v5.0.
     
  33. Thoranar

    Thoranar

    Joined:
    Oct 5, 2013
    Posts:
    22
    Actually I'm thinking it's an issue with Unity and Root Motion in general. If I have any editor heavy window open (Node Canvas, animator, etc.) where there are a lot of things happening, then the character moves at different degrees of rotation. The Animancer Component just happens to be the most noticeable because it has the most going on.

    I believe this is one of the downsides of root motion is that it is framerate dependent. I tried changing update mode on the animator to Animate Physics but that did not change anything.

    Thoughts?

    This is how i'm capturing root motion.
    Code (CSharp):
    1.         public void OnAnimatorMove()
    2.         {
    3.             // Compute movement velocity from animation
    4.                 var deltaTime = Time.deltaTime;
    5.                 if (deltaTime <= 0.0f)
    6.                     return;
    7.  
    8.                 // Compute animation velocity
    9.  
    10.                 animVelocity = _animator.deltaPosition / deltaTime;
    11.  
    12.                 // Compute animation angular velocity
    13.  
    14.                 float angleInDegrees;
    15.                 Vector3 rotationAxis;
    16.                 _animator.deltaRotation.ToAngleAxis(out angleInDegrees, out rotationAxis);
    17.  
    18.                 Vector3 angularDisplacement = rotationAxis * angleInDegrees * Mathf.Deg2Rad;
    19.                 animAngularVelocity = angularDisplacement / Time.deltaTime;
    20.            
    21.         }
     
  34. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Those calculations look reasonable to me and it definitely shouldn't be framerate dependant if you use Animate Physics.

    Make sure you set it to Animate Physics before entering Play Mode though, because there's a bug in the Playables API that prevents it from changing to or from that mode at runtime.

    Do you get the same issue if you just apply the delta values directly to the Transform like in the Root Motion example?
     
    hopeful likes this.
  35. Thoranar

    Thoranar

    Joined:
    Oct 5, 2013
    Posts:
    22
    Good point. When I apply the rotation directly to the transform or the rigidbody it works fine. Looks like there may be an issue with how my character controller is translating the information. (Using Easy Character Movement controller)
     
  36. petey

    petey

    Joined:
    May 20, 2009
    Posts:
    1,824
    Hiya!

    I ran into another issue that seems to be related to the last one.
    Code (CSharp):
    1.     [SerializeField] private AnimationClip clip1;
    2.     [SerializeField] private AnimationClip clip2;
    3.     public LinearMixerState clip2MixerState;
    4.  
    5.     private AnimancerState state;
    6.     public void Input_Test_A(InputAction.CallbackContext context)//New input system Button Press <<
    7.     {
    8.         if (context.phase != InputActionPhase.Performed) return;
    9.         animancer.Play(clip1).Events.Add(new AnimancerEvent(0.5f, PlayClip2));//Play clip1 and blend to clip2
    10.     }
    11.  
    12.     void PlayClip2()
    13.     {
    14.         clip2State = animancer.Play(mClip, 1f);
    15.         clip2MixerState = mClip.Transition.State;//Side note, is the the best way to store that state?
    16.         clip2MixerState.Speed = 0;
    17.     }
    18.  
    19.     void Update()//This is updating the time for clip2
    20.     {
    21.         float testVal = (Mathf.Sin(Time.time) * 0.5f) + 0.5f;
    22.         if (clip2MixerState != null) clip2MixerState.NormalizedTime = testVal;//<-- This one doesn't seem to update
    23.         if (clip2MixerState != null) clip2MixerState.SetNormalizedTimeImmediate(testVal);//<-- The modified version works fine
    24.     }
    This is using clip2MixerState.NormalizedTime = testVal
    Blend_1.gif

    And this one is using clip2MixerState.SetNormalizedTimeImmediate(testVal)
    Blend_2.gif

    It looks like the first one is not updating the animation of clip2. The problem is, this also happens on regular animation clips. Does that look like something you could fix?

    Thanks!
    Pete
     
    Last edited: May 28, 2020
  37. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    I'm not really sure what I'm looking at there. It sounds like the same issue, but it shouldn't apply to regular clips outside of mixers. Can you show the code you're using for the regular clip and explain the problem in a bit more detail?

    Transition.State is the most recently played state. So if mClip is a ScriptableObject being shared among multiple objects then yeah, you need to grab it like that. But if it's just a transition used by only one object then you can simply access that property whenever you want (after the first time it is played).
     
  38. petey

    petey

    Joined:
    May 20, 2009
    Posts:
    1,824
    Ahh, sorry no. It regular clips seem to be working fine. I'm just loosing my mind that's all o_O
    Looks like it is just that other old issue.
     
  39. Thoranar

    Thoranar

    Joined:
    Oct 5, 2013
    Posts:
    22
    Having an issue with fading into a mixer state. I have the following. activeMovement is a linearMixerState created onEnable that comes from a LinearMixerTransition:

    Code (CSharp):
    1.     private void TransitionToMovement()
    2.     {
    3.         animancer.Play(activeMovement);
    4.         activeMovement.SetNormalizedTimeImmediate(0);
    5.     }
    6.  
    7.     private void StartMovement()
    8.     {
    9.         if (IsWalking)
    10.             animancer.Play(StartWalk).Events.OnEnd = () => TransitionToMovement();
    11.         else
    12.             animancer.Play(StartRun).Events.OnEnd = () => TransitionToMovement();
    13.     }
    In the inspector, I've adjusted the End Time on the Start Walk/Stop Walk to fire the end event prior to the end of the clip, but the fade does not happen. It instantly jumps to the walk animation.

    Anything i'm missing?
     

    Attached Files:

  40. petey

    petey

    Joined:
    May 20, 2009
    Posts:
    1,824
    Hey @Thoranar i could be wrong but you don’t seem to have a value for the fade.
    Something like -

    Code (CSharp):
    1. animancer.Play(activeMovement,0.5f);
     
  41. Thoranar

    Thoranar

    Joined:
    Oct 5, 2013
    Posts:
    22
    Well this is interesting. I had not included a fade value because I'm using transition assets which I thought allowed me to set the fade duration directly on the asset. But you're right, if I manually put in a value then it works.

    Is there an issue with the asset fade value then?
     

    Attached Files:

  42. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Are StartWalk and StartRun mixers or transitions? Because if they are mixers, they won't have anything to do with the fade duration of the transition that originally created them.

    Also, since TransitionToMovement has no parameters or return value, you can assign it directly to the event without needing a lambda expression:
    ...OnEnd = TransitionToMovement;
    .
     
  43. Thoranar

    Thoranar

    Joined:
    Oct 5, 2013
    Posts:
    22
    They are clip transitions.
     
  44. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Can you post your full script (or email it to animancer@kybernetik.com.au)? It sounds like it might be choosing the wrong Play method overload or something.
     
  45. petey

    petey

    Joined:
    May 20, 2009
    Posts:
    1,824
    Hi there!
    I found another issue with clips not playing as expected. Managed to put together a little repo script.
    The mixerTest only seems to play every second time in this case (I put a little visual cue on screen for when I'm pressing the button to activate the clip).

    Does that look like a bug?
    Thanks,
    Pete

    May-29-2020 10-00-47.gif

    Code (CSharp):
    1.     [SerializeField] private AnimancerComponent animancer;
    2.  
    3.     private LinearMixerState mixerTest;
    4.     public AnimationClip clipTPose;
    5.     public AnimationClip clipTestL;
    6.     public AnimationClip clipTestR;
    7.  
    8.     public void Awake()
    9.     {
    10.         mixerTest = new LinearMixerState();
    11.         mixerTest.Initialise(clipTestL, clipTestR);
    12.         mixerTest.SetThreshold(0, 0);
    13.         mixerTest.SetThreshold(1, 1);
    14.  
    15.         //Play T Pose
    16.         animancer.Play(clipTPose);
    17.     }
    18.  
    19.     public void Input_Test_A(InputAction.CallbackContext context)
    20.     {
    21.         if (context.phase != InputActionPhase.Performed) return;
    22.         ArmsUp();
    23.     }
    24.  
    25.     public void ArmsUp()
    26.     {
    27.         ShowPressMessage();//Visual cue for gif
    28.         animancer.Play(mixerTest).Events.Add(new AnimancerEvent(1f, ReturnToTPose));
    29.         mixerTest.SetNormalizedTimeImmediate(0);
    30.     }
    31.  
    32.     public void ReturnToTPose()
    33.     {
    34.         animancer.Play(clipTPose, 0.25f);
    35.     }
    36.  
     
  46. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    I suspect that's the same time issue again. When ReturnToTPose finishes fading the mixer out it fails to reset the time, then when you play it again you force reset the time but the events still check based on the cached time that frame so it ends again instantly.

    Try going to the AnimancerState class and making the _Time field protected so that you can set it to 0 in SetNormalizedTimeImmediate.

    Incidentally, yesterday I found a new way to approach the problem which would avoid the need for this time caching system. It works great ... except for the part where it randomly crashes Unity. In theory, I should be able to just connect my late update system to a ScriptPlayableOutput so that it gets evaluated after the main playables are all done so it can set their time without breaking things, but since the documentation is completely useless I don't even know if that's the proper use for a ScriptPlayableOutput.
     
  47. petey

    petey

    Joined:
    May 20, 2009
    Posts:
    1,824
    Yikes! The docs sound terrible :( Can you reach out to one of the devs somehow?

    Also so I've changed the _Time field -
    Screen Shot 2020-05-29 at 1.35.46 pm.png
    Now where should I add the other mixerTest.SetNormalizedTimeImmediate(0)?
     
  48. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,570
    Just put
    _Time = value;
    in the
    SetNormalizedTimeImmediate method since that's the cached value the event system will be checking.

    There's nothing I can do about their documentation. There's a link down the bottom to report a problem with the page, but none of the things I've reported have ever actually been fixed. I've also tried reporting bugs in the engine (since Documentation Bug is one of the options) so now I just have a couple more bug reports that never got any response.

    Edit: actually there was one issue they fixed. Back in about Unity 5 I explained that having the documentation for SerializeField say "You will almost never need this" was retarded because it has always been almost universally accepted as a better practice than making fields public. Well that sentence is still there in the 2017.4 documentation, but they finally managed the monumental task of removing those 6 words in 2018.1.
     
    hopeful likes this.
  49. petey

    petey

    Joined:
    May 20, 2009
    Posts:
    1,824
    Ahh sorry to hear, if there's anything we can help with, post it up here.
    Hey that worked by the way, thanks for that :)
     
  50. craigjwhitmore

    craigjwhitmore

    Joined:
    Apr 15, 2018
    Posts:
    135
    I have a start walking state that transitions into a walking locomotion blend tree. When the walking blend tree state is entered, the start time must be advanced by an offset for the two animations to sync.
    Code (CSharp):
    1. private Float2ControllerState.Transition unarmedLocomotionBT;
    2. private Float2ControllerState.Transition boxingLocomotionBT;
    3. private ManualMixerState movementMixer;
    4. ...
    5. void OnEnable()
    6. {
    7.      var state = Creature.Animancer.Play(movementMixer, 0.25f);
    8.     state.Time = 1.6f;
    9.     //state.NormalizedTime = 1.6f     //alternatively
    10. }
    I'm unable to preview the start offset in the inspector or with the visual tool. Adding the start offset would be a great addition to Animancer, and is essential, modifying this can be awkward and slow to do in code.
    This would be best placed below Fade Duration, IMO.
    ie.
    AnimancerStart.png
     
    Last edited: May 31, 2020