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. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    @GoldFireStudios I can't see anything wrong with what it's showing in the Inspector. Try playing just a single Animation Clip on the Base Layer instead of an Animator Controller, there shouldn't be any difference but Animator Controllers are pretty complex. Also try playing each of the animations individually without any layers to make sure they are all correct.

    @protopop The Animancer.Lite.dlls in Animancer Pro are empty dummies to overwrite the ones from the Lite version when you upgrade to Pro. They contain no code and are not included in runtime builds anyway, but it's fine if you delete them.
     
    protopop likes this.
  2. protopop

    protopop

    Joined:
    May 19, 2009
    Posts:
    1,549
    Thank you:) I want to add that your extensive and organized examples are very helpful. I'm looking forward to adding in some Animancer support to my game.
     
  3. Ruchir

    Ruchir

    Joined:
    May 26, 2015
    Posts:
    927
    @Kybernetik could you add support for the new input system for the scripts present in the example scenes?

    It would be really helpful,
    Thanks.
     
  4. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    I'll look into it for the next version. Hopefully there's a way I can implement it without making a mess in every script that uses input.
     
    Ruchir likes this.
  5. Ruchir

    Ruchir

    Joined:
    May 26, 2015
    Posts:
    927
    Also, a tip for making the demos work with SRPs:
    Don't leave any game object with a Default Material from the built-in pipeline, instead create a basic white material and then assign that to the game objects, this will allow the automatic convertor to convert these materials to the SRP specific material :)
     
    hopeful and Kybernetik like this.
  6. MoonBeam91

    MoonBeam91

    Joined:
    Jan 28, 2021
    Posts:
    35
    How I create a looping animation for walk ? I have a walk animation which freeze at the end of the frame. How do I loop it using normalizedtime
     
  7. MoonBeam91

    MoonBeam91

    Joined:
    Jan 28, 2021
    Posts:
    35
    ClipTransitions create many levels of transitions I am not able to understand it

    upload_2021-11-22_22-46-4.png
     
  8. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    To make an animation loop, enable the Loop Time toggle in its import settings.

    And the transition can create duplicate states when you play it if its Start Time toggle is enabled. The Playing and Fading example explains why, but for a walk animation you'll just want to disable that toggle so it can continue playing from its current time if you play it again when it was already playing.
     
  9. mm_hohu

    mm_hohu

    Joined:
    Jun 4, 2021
    Posts:
    38
    @Kybernetik

    The value of AnimancerState.Time and the value of AnimancerState.NormalizedTime are inaccurate for several frames after the animation starts playing.

    I'm working on a fighting game, and missing information in the first 5 or 6 frames of a 1 second animation is fatal.

    Please point out any settings that I may have missed. I would like to know if there is a way to get a more accurate frame count of the animation that is playing.

    Code (CSharp):
    1. using System;
    2. using Animancer;
    3. using Cysharp.Threading.Tasks;
    4. using UnityEngine;
    5.  
    6. public class AnimationTest2 : MonoBehaviour {
    7.     private AnimancerComponent Animancer;
    8.     public AnimationClip clip;
    9.     private AnimancerState State;
    10.     private int ClipFrameCount;
    11.  
    12.     private void Awake() {
    13.         ClipFrameCount = (int) (clip.frameRate * clip.length);
    14.         Animancer = GetComponent<AnimancerComponent>();
    15.     }
    16.     private void Start() => LaunchAnimation().Forget();
    17.  
    18.     private async UniTask LaunchAnimation() {
    19.         await UniTask.Delay(TimeSpan.FromSeconds(10d)); // wait for 10 seconds for stability.
    20.         Debug.Log("--- Animation Start ----");
    21.         Debug.Log("ClipLength: " + clip.length + " seconds.");
    22.         Debug.Log("ClipFrameRate: " + clip.frameRate);
    23.         Debug.Log("ClipFrameCount: " + ClipFrameCount);
    24.         State = Animancer.Play(clip, 0f);
    25.     }
    26.  
    27.     private void FixedUpdate() { // Fixed Timestep = 0.02 (50fps default setting)
    28.         if (State is not null) {
    29.             Debug.Log("Time: " + State.Time); // State.NormalizedTime is also inaccurate.
    30.             Debug.Log("CurrentAnimationFrame: " + (int) (State.NormalizedTime * ClipFrameCount));
    31.         }
    32.     }
    33. }
     

    Attached Files:

  10. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    I have unit tests to verify that those properties accurately report what the animation system gives Animancer and that the time always increases as expected according to the current frame's delta time so I'm reasonably confident there's nothing wrong with them.

    The only way those logs would be possible should be if you have a lag spike where no Updates occur (and therefore no animation updates) but it still has to run FixedUpdate the appropriate number of times, meaning it's running the logs repeatedly without the animation system getting a chance to actually update to advance the animation so the time obviously wouldn't change.

    The math supports that theory:
    • 2 FixedUpdates at T=0.003 then it gets to Update which should be somewhere between 2 to 3 frames worth of the 0.02s timestep, meaning the next value should be between T=0.043 and T=0.063. And the next value is T=0.057, so it's within the expected range.
    • Same for the 5 frames where it goes from T=0.57 to T=157, which is a jump of exactly 0.02 x 5.
    For a fighting game, you should probably set the Animator component to Animate Physics mode so that it runs in the consistent FixedUpdate cycle instead of the inconsistent Update cycle. Note that you need to set it on your character prefab because the Playables API doesn't support changing the update mode at runtime.

    And you probably also want to use the Profiler to find out what's actually causing the lag spike. I doubt it's Animancer, but maybe if this is the first animation you're playing on any object there could be something internal that Unity has to initialize for it.
     
    hopeful and mm_hohu like this.
  11. mm_hohu

    mm_hohu

    Joined:
    Jun 4, 2021
    Posts:
    38
    I was stupid.
    I thought the animator component would be updated with FixedUpdate timing by default.
    When set to Animate Physics mode, the behavior was accurate both in the editor and on the actual Android device.
    I really appreciate it.
     
    hopeful likes this.
  12. MoonBeam91

    MoonBeam91

    Joined:
    Jan 28, 2021
    Posts:
    35
    Can you tell me what's wrong with this code ? I am able to achieve transitions without easily by using AnimationClip instead of ClipTransition

    public class Base : MonoBehaviour
    {
    [SerializeField] private AnimancerComponent _Animancer;
    [SerializeField] private ClipTransition _Idle;
    [SerializeField] private ClipTransition _Action;
    private void Update()
    {
    if (Input.GetKeyDown(KeyCode.W))
    {
    Walk();
    }
    else
    {
    _Action.Events.OnEnd = Idle;
    }
    }
    private void Idle()
    {
    _Animancer.Play(_Idle);
    }
    private void Walk ()
    {
    _Animancer.Play(_Action);
    }
     
  13. MoonBeam91

    MoonBeam91

    Joined:
    Jan 28, 2021
    Posts:
    35
    In the above I intend to use GetKey instead of GetKeyDown
     
  14. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    What are you actually trying to do?
     
  15. MoonBeam91

    MoonBeam91

    Joined:
    Jan 28, 2021
    Posts:
    35
    I am trying to Walk while pressing down on 'W' key. Whenever I try to use ClipTransition the animation get stuck, but if I use AnimationClip is works fine
     
  16. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    Untick the Start Time toggle in the Inspector of any transitions that you don't want to start from a specific time whenever you play them.
     
  17. Doomchecker

    Doomchecker

    Joined:
    Apr 5, 2021
    Posts:
    105
    Hi,
    I'm new to the tool and loving it so far.

    Nevertheless I need a little help here one time.
    I set up my main character with animancer, and it works perfectly fine. But as soon as I add a AI character to the scene, I run into the problem that animations events on the main-character won't fire anymore.

    I can even start with the main-character, play all animations properly and then add AI later and the events are not fired anymore.

    Both use completely different animations with different FBX files.

    Is there a fix for this?

    Thanks for helping out!
     
    Last edited: Nov 26, 2021
  18. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    Animancer Events can cause similar issues if you share them between characters, but it doesn't sound like that's the case here.

    When you play an animation, put in a Debug.Log("Playing Animation", animancer); so that when you click on that log message in the Console it will highlight the object with the AnimancerComponent in the hierarchy so you can verify that your scripts are each controlling the correct objects.
     
    Doomchecker likes this.
  19. Doomchecker

    Doomchecker

    Joined:
    Apr 5, 2021
    Posts:
    105
    My stupidity, I had my character's gameobject "static".
    Sorry for bothering, although it's great to know one is not alone here.
    Keep up the good work and thanks a lot!
     
  20. reinfeldx

    reinfeldx

    Joined:
    Nov 23, 2013
    Posts:
    162
    Hey Kybernetik, thanks for your work on this. Reaching out because I'm bumping into a couple of things while evaluating the lite version.

    I modified your PlayAnimationOnClick class in an attempt to pause and resume playing an idle animation. The two issues I'm having:
    1. I'm not able to pause/resume with the code below (I used your pause approach from here)
    2. When I try to set IsPlaying to true in the OnActionEnd() method I see this warning in the Console:
      Possible Bug Detected: An End Event did not actually end the animation:
      - State: Flip (ClipState)
      - Callback: PlayAnimationOnClick.OnActionEnd
    If it's relevant, the Animator has Apply Root Motion enabled. I'm using Animancer Lite v7.2 with Unity 2021.1.17.

    The code:
    Code (CSharp):
    1. private void Update()
    2. {
    3.     // Every update, check if the user has clicked the left mouse button (mouse button 0).
    4.     if (Input.GetMouseButtonDown(0))
    5.     {
    6.         // This should pause the animation without rewinding it
    7.         _Animancer.States[_Idle].IsPlaying = false;
    8.  
    9.         // Play the action animation.
    10.         var state = _Animancer.Play(_Action);
    11.  
    12.         // In this case, we just want it to call the OnActionEnd method (see below) when the animation ends.
    13.         state.Events.OnEnd = OnActionEnd;
    14.     }
    15. }
    16.  
    17. private void OnActionEnd()
    18. {
    19.     // I think this should resume the animation from the the paused time. Currently throws a warning.
    20.     _Animancer.States[_Idle].IsPlaying = true;
    21.  
    22.     // This plays the animation from the beginning without a warning, but expectedly does not resume the animation from the paused time
    23.     //_Animancer.Play(_Idle);
    24. }
     
    Last edited: Nov 26, 2021
  21. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    Play will Stop all other animations which includes resetting their time so pausing the Idle animation beforehand won't matter.

    And for the end event, if you look in the Inspector you should see that the Idle animation actually is playing again, but it's at 0 Weight (because Play(_Action) stopped it) and the Action animation is still playing because you aren't stopping it. So calling Play(_Idle) is correct there, it's just that the Idle animation was already returned to the start by Play(_Action).

    A simple solution would be to replace Play(_Action) with a method like this:

    Code (CSharp):
    1. private AnimancerState PauseCurrentAndPlay(AnimationClip clip)
    2. {
    3.     var current = _Animancer.States.Current;
    4.     current.IsPlaying = false;// Pause.
    5.     current.Weight = 0;// Stop affecting the character.
    6.     // current.Stop(); would have also set current.Time = 0; which is the bit you don't want.
    7.  
    8.     var state = _Animancer.Layers[0].GetOrCreateState(clip);
    9.     state.Play();// This only affects this state where _Animancer.Play would have stopped everything else on this layer.
    10.     return state;
    11. }
     
  22. MoonBeam91

    MoonBeam91

    Joined:
    Jan 28, 2021
    Posts:
    35
    Should I worry about these errors ?

    upload_2021-11-26_16-50-27.png
     
  23. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    The first one is an exception, meaning it interrupts your code so you definitely need to fix it. It explains how you can avoid it and the Playing and Fading example explains why it happens.

    The warnings are as they say "possible" bugs. They explain why they happen and how you can disable them if you think what you are doing is correct.

    You should never just leave errors or warnings to spam your console though, because it makes it hard to find actual problems and has a significant performance cost at runtime.
     
  24. reinfeldx

    reinfeldx

    Joined:
    Nov 23, 2013
    Posts:
    162
    Working. Thank you!
     
  25. MoonBeam91

    MoonBeam91

    Joined:
    Jan 28, 2021
    Posts:
    35
    How do I set an animation to looping through code ? I can manually toggle looping by going to the animation file. But want to be able to control through script
     
  26. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    The Event Utilities example shows how that can be done at runtime, but unless you have a strong need for it I'd recommend just setting it on the animation file.
     
  27. renman3000

    renman3000

    Joined:
    Nov 7, 2011
    Posts:
    6,681
    Hi there,
    I have successfully used Animancer in the past, I even created my own manager to handle your asset, to act as a middle man, to my own code..... I have done this many times.... however I have stepped away from your asset, for sometime and I just came back to it today and my avatars are not animating, they are just frozen in the T Pose.


    .... some notes:
    - My Animancer Component/States: is showing the proper clip as progressing, from 0 to 1.
    - In my older projects, I created a Transition variable as ClipState.Transition, but in my newer projects, this is an error. What should be the solution that this?


    Other than that I have double checked everything against the older functioning projects and everything is lined up, clips, loops, rigging....


    Thanks!
     
  28. PeachyPixels

    PeachyPixels

    Joined:
    Feb 17, 2018
    Posts:
    678
    Hi @Kybernetik,

    I've hit a small performance issue with Animancer Pro and wondered if you might be able to point me in the right direction please.

    I've a scene where around 50-80 (animated) actors are constantly despawning & spawning. The actors are pooled and spawn every quarter to half second (approximately). The entire game object is disabled when despawned and enabled when spawned.

    Now I've noticed that the frame rate fluctuates wildly when spawning, so ran the scene through a deep profile and it seems Animancer is the main cause.

    Of the time it takes to spawn the actor, game spawn logic accounts for around 30% of the time but Animancer the remainder.

    When the actors spawn, they enter what I define as the initial state with this code...

    Code (CSharp):
    1.     if (StateMachine == null)
    2.     {
    3.       StateMachine = new StateMachine<AnimatedActorState>(InitialState);
    4.       StateMachine.SetAllowNullStates(true);
    5.     }
    6.     else
    7.       StateMachine.TryResetState(InitialState);
    Once up and running performance is fine, Animancer runs brilliantly. It's only when spawning that I am seeing the spikes. They're very short & sharp, so don't affect the game greatly. But it would be good to try and reduce them a little.

    Maybe it's disabling then enabling the actor (when it's despawned\spawned) that's resetting Animancer internally? Is there a way to keep Animancer in a 'warm' state if so (beyond the obvious, do not disable the game object when despawning)?

    Any advice would be greatly appreciated.
     
    Last edited: Dec 2, 2021
  29. hopeful

    hopeful

    Joined:
    Nov 20, 2013
    Posts:
    5,625
    Have you tried just disabling the mesh renderers? When you disable the whole object I think you may be defeating some of the benefits of pooling.
     
    PeachyPixels likes this.
  30. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    @renman3000 If the AnimancerComponent Inspector shows everything playing properly, then the problem most likely relates to the model's import settings, possibly the avatar. Try making a new copy of your model.

    And the Animancer v7.0 Upgrade Guide explains the change in transition class names.

    @PeachyPixels Can you post a screenshot of the profiler so I can see which part of Animancer the cost is actually coming from?
     
    PeachyPixels and renman3000 like this.
  31. PeachyPixels

    PeachyPixels

    Joined:
    Feb 17, 2018
    Posts:
    678
    Thanks for the feedback @hopeful

    Were you suggesting disabling all renderers (etc) but leaving the objects active?

    I'm open to alternative ways of pooling, but marking them as inactive is pretty common from my understanding. Unity themselves advertise this.

    I'm sure there is a cost to waking them up from an inactive state, but would that outweigh the cost of leaving the objects active and the overheads associated with that.

    UPDATE: So it seems the Unity pooling advice was probably out of date. I rolled my own pool years ago based on it and it's worked well for less complex 2D objects, but this is the first time it's been used for more complex 3D objects. On-top of that, I've now come across numerous posts covering the performance impact of using SetActive()

    But I also just came across this...

    https://thegamedev.guru/unity-cpu-performance/object-pooling/

    I wasn't even aware Unity now offers their own pools in 2021 and by the looks of it, they now take the approach of not using SetActive()

    I'm on 2020 and was thinking of moving to 2021 when it hit LTS, but may bring that forward and investigate Unity's new pooling solution.
     
    Last edited: Dec 3, 2021
    hopeful likes this.
  32. PeachyPixels

    PeachyPixels

    Joined:
    Feb 17, 2018
    Posts:
    678
    Thanks @Kybernetik

    I've made some good progress with optimisations in the spawning logic and rendering today. I'm certainly seeing a more solid frame rate in the production build now, but the player fps is still spiking.

    I don't want to waste your time unnecessarily, so let me see what else can be optimised and if the spikes are still there, I'll mail you a screenshot of the deep profile next week.
     
    Last edited: Dec 3, 2021
  33. PeachyPixels

    PeachyPixels

    Joined:
    Feb 17, 2018
    Posts:
    678
    @Kybernetik

    Talking of optimisations...

    1) Given that Skinned Mesh Renderers can't be GPU Instanced, does Animancer Pro support Animation Instancing? I came across this prototype (?) from Unity, but not sure it's production ready...

    https://blog.unity.com/technology/animation-instancing-instancing-for-skinnedmeshrenderer

    2) Does Animancer Pro perform any sort of animation optimisation for frustrum or occlusion culled objects? By that I mean, running animation time (when culled) but pausing animation updates? Or is that not possible?

    Sorry if #2 is an obvious stupid\question to ask. But in trying to optimise the actors (as discussed previously) I tried setting State.isPlaying to false for actors that were frustrum culled (at-least a third are just off-screen at any one time) and it gained me a few fps I'm sure of it. Which was surprising. Unless it was a placebo :)

    UPDATE: What I was trying to describe was animation culling. The thing I completely overlooked on Unity's Animator component. Where is the face palm emoji when you need it :)

    https://docs.unity3d.com/ScriptReference/AnimatorCullingMode.html

    Mine are set to the default of CullUpdateTransforms but the IP animations still run when the actor is frustrum culled. Where-as the documentation implies otherwise...

    Retarget, IK and write of Transforms are disabled when renderers are not visible. The statemachine and root motion will always be evaluated. Thus you will always receive the OnAnimatorMove callbacks. All other animation will be skipped if the character is not visible. Specifically evaluation of bone animation, IK, OnAnimatorIK will be skipped.

    Setting it to CullCompletely fixes that and IP animations are now culled when the actor is frustrum culled, but the actors also don't start moving until they are in view (by moving the camera in scene view). Now that might be my issue and I'll investigate on Monday.

    But does Animancer respect that property? And shouldn't it be culling the IP animations?
     
    Last edited: Dec 3, 2021
  34. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    1) As far as I know, the Playables API doesn't support instancing and that instancing system would essentially replace what the Playables API does (except that it barely has any of the features it would need).

    2) The Playables API respects the Animator culling modes.

    CullUpdateTransforms means the animations still run when off screen so they properly keep track of what time they're up to, but they don't write their values to the character's bone Transforms. You can easily test that by selecting a bone and watching it stop moving after the character goes off screen.

    CullCompletely means that the animations entirely stop updating when off screen.

    I wouldn't expect any notable performance gain from setting state.isPlaying = false. All that would save is incrementing the time each frame, but it would still need to evaluate the animation along with all others that have any weight to determine the character's pose.

    If you want to disable animations to save performance, animancerComponent.Playable.PauseGraph() will prevent the entire graph from updating.
     
    PeachyPixels and hopeful like this.
  35. renman3000

    renman3000

    Joined:
    Nov 7, 2011
    Posts:
    6,681

    ... Hi, just to follow up, regarding an animation not playing, it seems the cause was a "faulty Animation Controller".....


    ----
    So, this leads me to my question, which may or may not really fall under your realm per say, but if you can help, I would love that....

    So, when I imported a new model, with preset controller, and applied my system to get it animating it worked. But previously, when I imported a model, with out a controller, but applied my own or other controller, it did not.

    If a controller has various states set in it: idle, run..... is there any reason why this controller would work on model A, but not on model B?

    Thank you!
     
  36. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    In my experience, it's more up to the animations in the controller as to whether or not they work. Things like trying to play a Generic animation on a Humanoid Rig, trying to play a Generic animation on a differently named/structured Rig, or not having the Rig set up correctly on the model or the animations. There are cases where Animator Controllers can become broken, but for animations outright not playing my first guess would be a problem with the animations themselves.
     
  37. PeachyPixels

    PeachyPixels

    Joined:
    Feb 17, 2018
    Posts:
    678
    Thanks @Kybernetik

    So I switched out the isPlaying code to PauseGraph() & UnpauseGraph() and can see the graph not playing now. Great!

    But the same issue where the actor is pretty much stationary still persists. The actor locomotion can start off or on screen, so I tween the animation start speed to make it more visually appealing. But the tween isn't updating because the graph is paused. I suspect this is due to AnimancerPlayable.DeltaTime not being updated when the graph is paused?

    I'm using IP animations and my forward locomotion code is like so...

    Code (CSharp):
    1. transform.position += Direction * (Time.deltaTime * ForwardAnimation.State.Parameter);
    So it's only ever advancing at an incredibly slow speed (Time.deltaTime) as Parameter is always 0

    Am I doing something fundamentally silly here? It's entirely possible :)

    I guess it could be changed so the tween doesn't occur if it starts off screen, but that seems like a workaround. Maybe I should just use CullUpdateTransforms as that offers a good amount of optimisation without the hassle of pausing the graph?

    UPDATE: I made the change and only tweening the speed of the actor when it starts on-screen (where the graph is playing) fixes the issue. Off-screen the actor immediately starts at their default speed.

    I'm going to try and produce some metrics to see how much benefit it offers. Hopefully others will find it useful.
     
    Last edited: Dec 6, 2021
  38. celechii

    celechii

    Joined:
    Nov 8, 2016
    Posts:
    4
    Hey Kybernetik! I'm trying to figure out how to make markers/signals work for PlayableAssetTransitions and the documents say "Using a Markers track requires a PlayableDirector component to be attached to the same object as the Animator even though the Timeline is being played through Animancer."

    But adding a PlayableDirector on its own to the animator has Timeline's NotificationUtilities throwing a null reference due to the director's PlayableAsset not being assigned

    Assigning the director's PlayableAsset to whichever timeline is being passed into the animancer component doesn't throw any errors, but it also doesn't pass along any bindings or emit the signal. I'm using Animancer Pro 7.2 in timeline 1.6.3. Am I missing something? I haven't seen anyone else trying to do this with without using Animancer Events, so maybe should I manually generating the events on the animancer component's state after calling play?
     
  39. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    @PeachyPixels Yeah, it's definitely worth doing some profiling to make sure you can actually gain a meaningful amount of performance from all that messing around. Also make sure you have Optimize Game Objects enabled for all bones that you don't need to access at runtime.

    @celechii Have you tried assigning the bindings (and PlayableAsset) to both the Animancer Transition and the PlayableDirector? Unfortunately, Timeline wasn't really intended to be used in custom Playable Graphs so things will basically either just work out of the box or there's not much you can do about it. Falling back to Animancer Events is likely the way to go if you can't get signals to work.
     
    PeachyPixels and celechii like this.
  40. onefoxstudio

    onefoxstudio

    Joined:
    Sep 28, 2016
    Posts:
    191
    Sometimes my character controller gets stuck, CPU goes 100% for a while then stops. I can't find where can be the issue. It's a very simple code (taken from the platformer example)

    Any idea of where it could be from ?

    Code (CSharp):
    1. ArgumentOutOfRangeException: Time must be finite
    2. Parameter name: weightedNormalizedTime
    3. Actual value was Infinity.
    4. Animancer.MixerState.ApplySynchronizeChildren (System.Boolean& needsMoreUpdates) (at Assets/Plugins/Animancer/Internal/Mixer States/MixerState.cs:925)
    5. Animancer.MixerState.Update (System.Boolean& needsMoreUpdates) (at Assets/Plugins/Animancer/Internal/Mixer States/MixerState.cs:543)
    6. Animancer.AnimancerNode.Animancer.IUpdatable.Update () (at Assets/Plugins/Animancer/Internal/Core/AnimancerNode.cs:283)
    7. Animancer.AnimancerPlayable.UpdateAll (Animancer.Key+KeyedList`1[T] updatables, System.Single deltaTime) (at Assets/Plugins/Animancer/Internal/Core/AnimancerPlayable.cs:1269)
    8. UnityEngine.Debug:LogException(Exception, Object)
    9. Animancer.AnimancerPlayable:UpdateAll(KeyedList`1, Single) (at Assets/Plugins/Animancer/Internal/Core/AnimancerPlayable.cs:1274)
    10. Animancer.AnimancerPlayable:PrepareFrame(Playable, FrameData) (at Assets/Plugins/Animancer/Internal/Core/AnimancerPlayable.cs:1239)
    The code

    Code (CSharp):
    1.  
    2. public bool CheckMotionState()
    3. {
    4.     if (SantaController.IsOnGround())
    5.     {
    6.         state = SantaController.MovementInput == Vector3.zero ? _Idle : _Locomotion;
    7.     }
    8.     return state != StateMachine.CurrentState && StateMachine.TryResetState(state);
    9. }
    10.  
    11. public void UpdateSpeedControl()
    12. {
    13.     Vector3 movement = Vector3.ClampMagnitude(SantaController.GetVelocity(), 1f);
    14.     DesiredForwardSpeed = movement.magnitude * SantaController.maxWalkSpeed;
    15.     var deltaSpeed = movement != default ? SantaController.GetMaxAcceleration() : SantaController.GetBrakingDeceleration();
    16.     ForwardSpeed = Mathf.MoveTowards(ForwardSpeed, DesiredForwardSpeed, deltaSpeed * Time.deltaTime);
    17. }
    18.  
    19. public sealed class IdleState : CreatureState
    20. {
    21.     [SerializeField] private ClipTransition _MainAnimation;
    22.  
    23.     public override bool CanEnterState => Creature.SantaController.IsOnGround();
    24.  
    25.     private void OnEnable()
    26.     {
    27.         Debug.Log("IdleState");
    28.         Creature.Animancer.Play(_MainAnimation);
    29.     }
    30.  
    31.     private void FixedUpdate()
    32.     {
    33.         if (Creature.CheckMotionState()) return;
    34.     }
    35. }
    36.  
    37. public sealed class LocomotionState : CreatureState
    38. {
    39.  
    40.     [SerializeField] private LinearMixerTransition _LocomotionMixer;
    41.     public override bool CanEnterState => Creature.SantaController.IsOnGround();
    42.  
    43.     private void OnEnable()
    44.     {
    45.         Debug.Log("Locomotion");
    46.         Creature.Animancer.Play(_LocomotionMixer);
    47.     }
    48.  
    49.     private void FixedUpdate()
    50.     {
    51.         if (Creature.CheckMotionState())
    52.             return;
    53.  
    54.         Creature.UpdateSpeedControl();
    55.         _LocomotionMixer.State.Parameter = Mathf.Clamp01(Mathf.Abs(Creature.ForwardSpeed / Creature.DesiredForwardSpeed));
    56.     }
    57. }
    58.  
     
  41. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    Are you using the latest version of Animancer (v7.2)? There have been similar bugs in the synchronization system in earlier versions.

    Otherwise, can you make a minimal reproduction project and send it to animancer@kybernetik.com.au so I can take a look at it?
     
    onefoxstudio likes this.
  42. onefoxstudio

    onefoxstudio

    Joined:
    Sep 28, 2016
    Posts:
    191
    Yes, 7.2 latest on 2021.2.5f1 MacOS. Ok will send you a minimum project. Thank you !
     
  43. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    Your
    Creature.DesiredForwardSpeed
    is 0 so you're doing a divide by 0 and setting the parameter to NaN.

    I can add an error message in the next version for when that happens, but you will still need to fix your code to give it a valid value.
     
    onefoxstudio likes this.
  44. PeachyPixels

    PeachyPixels

    Joined:
    Feb 17, 2018
    Posts:
    678
    Thanks @Kybernetik

    I tried profiling the graph changes, but the frame rates are fluctuating so much it's really difficult to measure improvements (if any). Even averages over three runs give quite different results.

    I refactored my object pool so the actors remain in an active state and it hasn't actually made that much difference. So maybe the SetActive calls (in this situation) did not have as much overhead as initially thought.

    I have pretty much ruled out Animancer though, after going through the deep profile in more detail. I am beginning to suspect it's profiler overhead and\or a player issue. That said the spikes occur in builds as well, so maybe not.

    Screenshots of the profiler are attached. The run of spikes are where 21 actors are spawned and enter their default locomotion state. The varying shades of blue (in the big spike) is my actor & the Animancer code, which collectively is only about 3ms.

    I'm no expert, but it looks like the GPU is taking a hit on spawning and the CPU is mainly idle and waiting for the frame to be rendered. It's not waiting for VSync as it's disabled and is obviously running in the player anyway, where it's disabled.

    I'll run this past someone at Unity and see if they can shed some light on it.

    UPDATE: The actor in question is third party and I've been looking at it today. It's quite a few years old and not well optimised by the looks of it. For example, the models have no mesh optimisation\compression and read\write is enabled. I tried the optimise game object option, but it didn't seem to make any difference. Anyway, I've optimised a little and more improvements have been found, but still seeing the spikes on spawning. Anyway, thanks for the advice so far. Think I need to take this away and spend some more time optimising the models etc.
     

    Attached Files:

    Last edited: Dec 8, 2021
  45. One_Learning_Man

    One_Learning_Man

    Joined:
    Sep 30, 2021
    Posts:
    80
    Hello Kybernetik, I am trying to attempt to combine layers and Animancer's FSM.

    I have two separate states: Locomotion State and Cast State (casting an ability). When using layers to cast an ability while moving, would you recommend.

    A) Somehow enabling both Locomotion + Cast State at the same time (squeezing animation masking/layering functionality into Cast State)?
    B) Creation of a third state "Cast While Moving" State?

    I think option B is the right one but I just wanted to make sure.
     
  46. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    If Casting is the only other state where you can move, you could give the CastState a reference to the LocomotionState so you can call its methods without re-implementing the same thing.

    Or if there might be lots of states like that, you could make a dedicated movement component like I did in the Platformer Game Kit. Then the base state class has a MovementSpeedMultiplier which can be 1 to allow movement or 0 for states that can't move, and the movement script can just use the multiplier of the current state in its calculations.
     
    One_Learning_Man likes this.
  47. One_Learning_Man

    One_Learning_Man

    Joined:
    Sep 30, 2021
    Posts:
    80
    Thanks for the helpful suggestions, your flinch movement section has some good explanations with what you're talking about. I'll be studying the Platformer Kit more tomorrow. That addresses moving (the functionality) with states but in regards to moving (the animation) layering like your example 7 where the lower body is running and the upper body is doing actions...

    upload_2021-12-12_0-55-45.png

    Is stuffing all the things related to animation masking and animation layering into the base CharacterStates.cs class a good choice or is it better in the derived State classes? I was doing the same thing with _Animation until your paragraph caught me off-guard.

    upload_2021-12-12_0-59-33.png
     
  48. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    Putting the animation in your base state so every state has one isn't necessarily wrong, but I've found that it leads to a lot of code duplication and inefficiency. For example:
    • In the Platformer Game Kit the Idle, Walk, Run, and Fall animations all have very similar behaviour, i.e. you have full control over your movements (with the actual speed and acceleration values being managed in the character movement component rather than the states) and can freely perform other actions.
    • They could each be very simple states with the base "play one animation" logic and their own one-line CanEnterState property. Then a Multi State could simply check through them each frame to pick the correct one. That's 5 components which are nice and modular, but that has a (small) performance overhead and also makes debugging a bit annoying because the execution is jumping around through all of them all the time and only running a line or two of code from each one.
    • So instead, I put all those animations in one Locomotion State with a straightforward Animation Selection system that simply checks which animation should be playing every frame. Looking at that script makes it obvious that all of the animations behave with the same state rules such as having no restrictions on when you can enter or exit the state. In theory this approach would be less maintainable because it's less modular, but in practice the state is so simple that it would take a lot of added complexity before it became a real issue.
    Looking through the states in the Platformer Game Kit shows that 8 out of 15 have a single Animation field so I stand by my decision to not put that field in the base state class, but maybe it would be worth making an AnimatedCharacterState which inherits from the base to add that field and play it, then that class can be further inherited from as necessary.

    I've never actually worked with layers in a real project so I don't have a solution that I can confidently recommend for handling them, particularly if you want to allow stopping and starting movement mid action to give the action animation control over the whole character rather than leaving the action on just the upper body and playing Idle on the lower body because the code for Layer Changing Mid Action in the Layers example feels rather hacky even in that super simple situation.

    Putting an AvatarMask in the base state class does seem like a bad idea though because there's also no way to cross fade mask changes. Maybe you could avoid that by giving the character a Dictionary<AvatarMask, AnimancerLayer> so you can set up each mask on its own layer, but how many states are actually going to require a unique mask?
     
    One_Learning_Man likes this.
  49. One_Learning_Man

    One_Learning_Man

    Joined:
    Sep 30, 2021
    Posts:
    80
    Thanks Kybernetik for your write-up, informative as always. I study and follow your 3D Game Kit animancer examples first since I am learning how to construct a 3D RPG but I see that your Platformer Game Kit has techniques definitely worth evaluating and integrating too - that LocomotionState where it bundles everything into a neat package is pretty darn nice.

    Time to sit down and start experimenting and testing on how to handle integrating animation layering with everything we discussed so far!
     
  50. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,485
    I received a negative review from @Khalreon which I can see has been edited in response to my original reply, but since reviews are a terrible place to hold an actual discussion I figured I'd reply here instead.

    TL;DR: parts of the review are factually incorrect, but your reasons for giving a negative review are justified and I'm not asking you to increase the rating.
    That's false.

    PreventDataLoss.png
    I actually did try to make an upgrader system, but only 1 of the 3 people that tested it was able to do so successfully and for the other 2 it corrupted data from other plugins (Playmaker and a dialogue system because they have Transition classes as well). So I had two choices:

    1. Make an upgrader tool:
    • Spend time ironing out those issues and making the tool user-friendly.
    • Spend time maintaining the tool for bugs, changes in future versions of Animancer, and changes in future versions of Unity (primarily the serialization format which is undocumented and therefore subject to change at any time without warning). This would include re-testing the tool for both Animancer Pro and Lite in every supported version of Unity (currently there are 4) every time I want to release an update.
    • Deal with users who lost their transition data because they ignored the fact that it's a major version update and didn't read the Change Log to know they needed to download the upgrader tool.
    • Deal with users who followed the upgrade instructions incorrectly.
    • Deal with users who encountered a bug in the upgrader which prevented it from working.
    • Deal with users who encountered a bug in the upgrader which destroyed or even corrupted data in seemingly unrelated systems.
    2. Don't make an upgrader tool:
    • Spend the time that would have been used on the tool for bug fixes and improvements instead.
    • Deal with users who lost their transition data because they ignored the fact that it's a major version update and didn't read the Change Log to follow the instructions or the recommendation to avoid updating until their next project.
    Neither option is particularly good, but I believed #2 was the lesser of two evils and in hindsight I 100% stand by that decision. One negative review after 5 months is nowhere near as bad as I would have expected if I had gone with #1.

    Your review stings more than it otherwise might because it's the first negative out of over 100 positives, but I fully support your right to give a negative review. I believe I made the best decision possible in the given circumstances, but that decision negatively impacted you so a negative review is appropriate and I'm not asking you to change it (aside from the parts about the Change Log not explaining how to avoid the data loss because that's not true).