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.

Animancer - Less Animator Controller, More Animator Control

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

  1. silver1911

    silver1911

    Joined:
    Jul 11, 2017
    Posts:
    5
    Hey Kybernetic, i think i need your help.

    I have a parent GameObject (the Wardrobe) wich contains 6 children (the Drawers). The AnimancerComponent is attached to the parent, and each child contains a class wich hold the animation, because they are different for each drawer.
    Now when i call animancer.Play(animationClip) the animation for the specific child works, but when i call the next drawer animation (works also), the first drawer goes back to its origin. So there is only one drawer outside at a time. but i want them all to stay in their outside state. I remember doing exactly this with the Animancer(1.5?) long time ago and to make it work i just had to put all animationClips in the AnimancerController also.

    Can you point me in the direction to get the issue? i mean i obviously overlooked something, but i dont know what it could be.

    Thank you
     
  2. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,233
    That could be done using Layers:
    1. Set the AnimancerPlayable.maxLayerCount = 6 on startup (it starts at 4).
    2. Call animancer.Play(clip, drawerIndex) where drawerIndex is between 0 and 5, so each drawer has its own layer.
    Or since it's a simpl object that presumably won't have any other animations, you could just control each state individually:
    1. var state = animancer.GetOrCreateState(clip);
    2. state.Play() to just play that state without affecting any of the others where animancer.Play(clip) would play the state and stop all others.
    You might also be interested in the Fine Control/Doors example if you want to be able to close the drawers as well.
     
    Last edited: Nov 26, 2019
    hopeful likes this.
  3. ColpColtran

    ColpColtran

    Joined:
    Oct 9, 2014
    Posts:
    27
    Thank you for the script!

    BTW Is there something as frame blending for 2D animations? No worries if not, I dont even know if such feature exists in normal Unity though :) Havent done much research on this yet. But it struck me today as I was working on my 2D game, that it might be nice to have some blending in between keyframe sprites.
     
  4. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,233
    No, sprite animations just swap out the sprite (same as Unity default). There's really no way they could be interpolated that would actually look good. For example, if you interpolated between two frames of a sword swing, you would have the sword at half alpha in both locations at once which would look stupid.
     
  5. silver1911

    silver1911

    Joined:
    Jul 11, 2017
    Posts:
    5
    Thank you very much!

    The first method did the job instantly, that was excatly what i was looking for!

    Thanks again

     
  6. ColpColtran

    ColpColtran

    Joined:
    Oct 9, 2014
    Posts:
    27
    Yes, that makes sense. At least I have less work with animating :)
     
  7. tyrot

    tyrot

    Joined:
    Aug 18, 2013
    Posts:
    34
    hello there - after reading all those reviews i will ask a newbeeish question. Do you think you will add playmaker support or as a concept there is no need for it?

    thanks
     
  8. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,233
    Unfortunately there are a few obstacles preventing me from adding Playmaker support:
    • If I'm going to support Playmaker, I should also support other popular visual scripting plugins like Bolt, NodeCanvas, FlowCanvas, uNode, and the new official Unity visual scripting system they are working on. I've had a few different people ask about support for each of those systems.
    • I don't own any of those plugins.
    • Even if I got them, I would still need to learn to use them so I can figure out what I actually need to make.
    • I don't know what implementing this sort of thing actually entails, but at the very least I would expect to need to make a script for each of the main public and serializable classes in Animancer, which would mean a few dozen new scripts for each visual scripting system.
    • Then I would still need to test and document it all.
    That said, I would certainly be happy to help you work through any problems you encounter if you try to do it yourself. And if you do a substantial amount of work on it, we might be able to negotiate a trade where I give you a free voucher for Animancer Pro (and/or any of my other plugins) in exchange for you allowing me to share the code so I can add Playmaker support to my feature list. Though that might violate the rule that says you can't "sell vouchers or otherwise charge for them".
     
    Last edited: Nov 30, 2019
    RogDolos likes this.
  9. classofthirteenth

    classofthirteenth

    Joined:
    Nov 1, 2019
    Posts:
    16
    Hey kybernetik,
    Lately, I'm doing an experiment. I tried to change the fade duration parameter when called the CrossFadeFromStart(state, fadeDuration) methods. I tried several fadeDuration, 0.25s, 0.50s, and 1.0s

    But unfortunately strange results occurred when I tried to use fadeDuration = 1.0s: the fading process is not yet complete and the animation was continuing to play the next state.

    this is the code :

    Code (CSharp):
    1. AnimancerState previousState = _Animancer.CurrentState;
    2.         AnimancerState state;
    3.  
    4.         //float fadeDuration = 0.25f;
    5.         //float fadeDuration = 0.50f;
    6.         float fadeDuration = 1.0f;
    7.        
    8.         foreach (string word in words) {
    9.             if(toggleTransition.isOn) {
    10.                 state = _Animancer.CrossFadeFromStart(word, fadeDuration);
    11.                
    12.             } else {
    13.            
    14.                 state = _Animancer.Play(word);
    15.                 state.Time = 0;
    16.             }
    17.  
    18.             while(state.Time < state.Length) {
    19.                 yield return null;
    20.             }
    21.  
    22.             previousState = state;
    23.         }
    24.  
    25.         if (toggleTransition.isOn) {
    26.             _Animancer.CrossFade("idle");
    27.         } else {
    28.             _Animancer.Play("idle");
    29.         }
    And then i tried to add a new condition :
    Code (CSharp):
    1.  while(state.Time < state.Length + fadeDuration) {
    2.                 yield return null;
    3.             }
    4.  
    The fading process completed but animations were not skipped and only showing the last frame on the clip.

    Here's some video attachment :
    A - correct animation, fadeSpeed = 0.25s
    B - fadeSpeed = 1s
    C - fadeSpeed = 1s with new condition
    https://drive.google.com/open?id=1uOUWUCJQhCZuWGfsO6Lxwj7NIkfsI3PZ

    Thank you
     
  10. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,233
    That's exactly what I would expect to happen. The animation is playing while it fades in so if the fade takes longer than the animation, it will already be done by the time the fade finishes. That's how it works by design.

    If you want it to fully fade to the new starting pose before doing the motion, you could just add some extra time in that pose at the start of each animation. Or if you check out the Animancer v4.0 Alpha you could use the new Animancer Event system, just start the animation paused as it fades in then use an event at the end of the fade to unpause it. You could do the same thing with Animation Events in an older version, but it would be a lot less convenient.
     
  11. classofthirteenth

    classofthirteenth

    Joined:
    Nov 1, 2019
    Posts:
    16
    Ah I see, I think I have some misconceptions about the design of the cross-fade here. Would you mind to explain a little about :
    • the correlation between the Transformations, FadeDuration, Blend-Weight, and FadeSpeed here when the transition is being created?
    • How the blend-weights updated? Is it incremented by the FadeSpeed?
    • How is the transformation being updated?
    Sorry if I asked too much question here :D
    Thank you
     
  12. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,233
    • When you call CrossFade, you specify the fadeDuration - the number of seconds you want it to take.
    • It calls StartFade on every state individually which sets the TargetWeight (previous animations fade to 0 and the new one fades to 1) and converts that duration into FadeSpeed.
    • Every update, every state that hasn't reached its TargetWeight moves its Weight towards that value according to its FadeSpeed.
    • I'm not sure what you mean by "transformations".
    It might also help if you just select your object and watch what happens to the values in the Inspector when you call CrossFade and try playing around with them manually. Use the TimeScale script in the examples folder to manually slow down the time scale if you need to.
     
  13. classofthirteenth

    classofthirteenth

    Joined:
    Nov 1, 2019
    Posts:
    16
    The transformations here I mean are the new interpolated coordinates during the crossfade (transition) is occurred, I just don’t get it how that values being updated and their correlation between the FadeDuration, Blend-Weight, and FadeSpeed

    Thanks
     
  14. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,233
    Fade duration and speed determine how weight changes and weight determines how much each animation affects the output.

    If you have two animations at 0.5 weight each which both rotate the same bone, that bone will be halfway between the rotations each of them would normally give it. As the fade continues bringing one weight down and the other up (to 0.4 and 0.6 for example), the effect of the old animation decreases and the new one increases.
     
  15. craigjwhitmore

    craigjwhitmore

    Joined:
    Apr 15, 2018
    Posts:
    135
    I have setup a character animation that I'd like to move with animator root motion. Just a simple walk animation when I press the up key.
    A Hybrid Animancer component is being used with states, root motion has been enabled.
    In the locomotion state, I have my movement logic for walking, which sets a animator float for walk speed.

    The animation moves slightly jerky using Update or FixedUpdate, so I want to move the character in OnAnimatorMove, which smooths out the animation on regular mecanim.

    The FixedUpdate works fine in the locomotion state, however OnAnimatorMove isn't being called, which is where I want to move the rigidbody.

    Any advice please.
     
  16. DiscoFever

    DiscoFever

    Joined:
    Nov 16, 2014
    Posts:
    286
    I get a weird behavior; and can't understand why. I have a basic character controller; walking and jumping.
    I have implemented an attack but it never gets played; if i change from CrossFade to Play it works but half a sec.
    End of attack never gets called; why ?

    Code (CSharp):
    1.  
    2.     void Update()
    3.     {
    4.  
    5.         if (Input.GetKeyDown(KeyCode.G) && !isAttacking)
    6.         {
    7.             Debug.Log("Attack started: " + Time.time);
    8.             isAttacking = true;
    9.             PlayAttack();
    10.             _Animancer.CurrentState.Speed = 1f;
    11.         }
    12.  
    13.  
    14.         if (!_controller.IsGrounded() && !isAttacking)
    15.         {
    16.             PlayJump();
    17.  
    18.         }
    19.         else
    20.         {
    21.             Vector3 _velocity = Vector3.ProjectOnPlane(_controller.GetVelocity(), transform.up);
    22.             if (_velocity.magnitude > 0.01f)
    23.             {
    24.                 PlayMove();
    25.                 _Animancer.CurrentState.Speed = 2f;
    26.             }
    27.             else
    28.             {
    29.                 _Animancer.CrossFade(_Idle);
    30.             }
    31.         }
    32.  
    33.     }
    34.  
    35.     void StopAttack()
    36.     {
    37.         isAttacking = false;
    38.         Debug.Log("Attack finished: " + Time.time );
    39.     }
    40.  
    41.     void PlayAttack()
    42.     {
    43.         var state = _Animancer.CrossFade(Attack1);
    44.         state.OnEnd = () => StopAttack();
    45.     }
    46.  
    47.     protected virtual void PlayJump()
    48.     {
    49.         _Animancer.CrossFade(_Jump);
    50.     }
    51.  
    52.     protected virtual void PlayMove()
    53.     {
    54.         _Animancer.CrossFade(_Walk);
    55.     }
    56. }
    57.  
     
  17. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,233
    @craigjwhitmore What does your hierarchy look like? As long as you have OnAnimatorMove in a component on the same GameObject as the Animator, it should receive all the root motion so you can apply it to whatever object you want.

    @DiscoFever If you press G to attack it will continue on to check if not grounded and not attacking, but since you are attacking it will go into the else where it will either play move or idle, so it will never actually get to perform the attack. You could fix that by putting "if (attacking) return;" at the top of the Update method and adding "return;" right after you start the attack and set the speed.

    Also, instead of "state.OnEnd = () => StopAttack();" you could just write "state.OnEnd = StopAttack;" since StopAttack exactly matches the signature of the OnEnd callback (no parameters and returns void).
     
    craigjwhitmore likes this.
  18. craigjwhitmore

    craigjwhitmore

    Joined:
    Apr 15, 2018
    Posts:
    135
    Thank you, that was exactly the issue. I was following the More Brains example and had the locomotion state components in the Brain object.
    Adding the state components to the same class as the animator works.
     
  19. kjhuebner

    kjhuebner

    Joined:
    Oct 2, 2018
    Posts:
    14
    Design strategy - make AnimancerComponent a base class?

    Hi, I found that I can compile and run the principal class "AnimancerComponent" as a base class of my custom animation class. The animation seems to work like the original sample.

    I prefer this design strategy, as it reduces my added components in a gameobject to just one -- rather than two. And it eliminates dragging and dropping the AnimancerComponent instance into the custom class component in Unity's Inspector panel.

    Is there anything wrong with this design strategy? Moreover, why did the Animancer design team NOT implement the Animancer framework in such a manner?

    Here's a snippet of my simple design change to better understand:

    Code (CSharp):
    1.  
    2.     // Use AnimancerComponent as a base class.
    3.  
    4.     public class SpiderBotScript_TEST : AnimancerComponent
    5.     {
    6.         /************************************************************************************************************************/
    7.  
    8.         [SerializeField] private AnimancerComponent _Animancer;
    9.         [SerializeField] private AnimationClip _WakeUp;
    10.         [SerializeField] private AnimationClip _Move;
    11.  
    12.         private Action _PauseGraph;
    13.         private Action _FadeToMovement;
    14.  
    15.         /************************************************************************************************************************/
    16.  
    17.         private void Awake()
    18.         {
    19.           // ADDED: set the instance to _Animancer base.
    20.             _Animancer= this;
    21.  
    22.             // Start paused at the beginning of the animation.
    23.             _Animancer.Play(_WakeUp);
    24.             _Animancer.Playable.Evaluate();
    25.             _Animancer.Playable.PauseGraph();
    26.  
    27.             // Cache the delegates we will use with the OnEnd event so we don't allocate garbage every time.
    28.             _PauseGraph = _Animancer.Playable.PauseGraph;
    29.             _FadeToMovement = () => _Animancer.CrossFade(_Move);
    30.         }
    31.  
    I look forward to hear your technical thoughts.

    Ken
     
    Last edited: Dec 7, 2019
  20. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,233
    I've used that strategy a couple of times in my own games and haven't had any real issues with it. The Component Types page talks a bit about inheriting from AnimancerComponent. But it's not always the most convenient approach since I often have the character's model (with the Animator and AnimancerComponent) as a child of the character's root object (with the Collider, Rigidbody, etc.) to group the visual stuff separately from the other components like I've done in the State Machine examples (3, 4, and 5).

    I didn't inherit from AnimancerComponent in any of the examples for a couple of reasons:
    • So that beginners don't need to understand inheritance to understand the examples.
    • To demonstrate interactions between multiple components for beginners rather than encouraging them to just cram everything into a single component.
    • To always be clear about which fields in the Inspector are part of the example and which are part of Animancer itself.
    • So that the AnimancerComponent inheritance diagram and derived types list would only show core parts of Animancer rather than distracting people with the examples.
     
  21. kjhuebner

    kjhuebner

    Joined:
    Oct 2, 2018
    Posts:
    14
    Hi Kybernetic, thanks for sharing your design decisions. I just bought your package yesterday, so I was unclear if the Unity engine was going to give me issues down the road with my design strategy.

    It sounds like you are the original author of Animancer -- so I'm grateful for your insights. Frankly your thoughts sound quite positive. So I'll continue with my derived class approach, well knowing I can use your original two component design as well in the same project, if need be.

    Hey, thanks for creating such wonderful enhancement for Unity. Much success to you! -Ken
     
    Kybernetik likes this.
  22. matasoy

    matasoy

    Joined:
    Jun 19, 2019
    Posts:
    5
    error.gif Hello, can you help us?
    We have a problem about webgl, i am on try mode, not pro version. But i manage something i need to do with your scripts. Thats a huge things for us.

    When we are Publish Settings->Pc,Mac&Standalone mode, we have no problem. Publishing is ok and our sample is working. But when we change publish mode to WebGL we have errors.

    While trying to build get 2 error:
    [1]
    Plugin 'Animancer.Lite.dll' is used from several locations:
    Assets/Plugins/Animancer/Animancer.Lite.dll would be copied to <PluginPath>/Animancer.Lite.dll
    Assets/Plugins/Animancer/Internal/Animancer.Lite.dll would be copied to <PluginPath>/Animancer.Lite.dll
    Please fix plugin settings and try again.
    UnityEditor.Modules.DefaultPluginImporterExtension:CheckFileCollisions(String)
    UnityEditorInternal.PluginsHelper:CheckFileCollisions(BuildTarget) (at C:/buildslave/unity/build/Editor/Mono/Plugins/PluginsHelper.cs:25)
    UnityEngine.GUIUtility:processEvent(Int32, IntPtr)

    [2]
    Plugins colliding with each other.

    You can watch the gif.
     
  23. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,233
    Go to those two DLLs and check which platforms are enabled in their import settings:
    • Assets/Plugins/Animancer/Animancer.Lite.dll should only be enabled for the Editor.
    • Assets/Plugins/Animancer/Internal/Animancer.Lite.dll should be enabled for all platforms except the Editor.
    It should have been set like that already, but Unity seems to randomly lose track of the settings for platforms that I don't have installed since they don't appear in the list.
     
  24. matasoy

    matasoy

    Joined:
    Jun 19, 2019
    Posts:
    5
    Thank you fast answer. I try bu problem still continues. So I try a clean import.

    2019.2.6f1, While Import the "TRY plugin"
    Identifier uniqueness violation: 'Name:minebot_front_upperleg, Type:Mesh'. Multiple Objects with the same name/type are generated by this Importer. There is no guarantee that subsequent imports of this asset will properly re-link to these targets.

    But Editor is checked as default. And When uncheck the Editor checkbox these errors are thrown;

    [1]
    Assets\Plugins\Animancer\AnimancerComponent.cs(60,36): error CS0539: 'AnimancerComponent.AnimatorFieldName' in explicit interface declaration is not a member of interface
    [2]
    Assets\Plugins\Animancer\Internal\Editor Utilities\AnimancerComponentEditor.cs(15,45): error CS0246: The type or namespace name 'AnimancerPlayableEditor' could not be found (are you missing a using directive or an assembly reference?)
    [3]
    Assets\Plugins\Animancer\Utilities\AnimancerUtilities.cs(55,74): warning CS0618: 'MixerState<Vector2>' is obsolete: 'Animancer Lite only allows you to try out this feature in the Unity Editor. If you wish to use it in a runtime build you will need to purchase Animancer Pro: http://u3d.as/19Xb'
    [4]
    Assets\Plugins\Animancer\Internal\Editor Utilities\NamedAnimancerComponentEditor.cs(25,33): error CS0115: 'NamedAnimancerComponentEditor.DoOverridePropertyGUI(string, SerializedProperty, GUIContent)': no suitable method found to override


    Also I try 2018.2.18f While import TRY Plugin
    These errors happened.
    Unloading broken assembly Assets/Plugins/Animancer/Animancer.Lite.dll, this assembly can cause crashes in the runtime
    Assets/Plugins/Animancer/AnimancerComponent.cs(24,87): error CS7069: Reference to type `UnityEngine.IAnimationClipSource' claims it is defined assembly `UnityEngine, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null', but it could not be found
    Assets/Plugins/Animancer/AnimancerComponent.cs(1265,33): error CS0539: `System.Collections.IEnumerable.GetEnumerator' in explicit interface declaration is not a member of interface

    The DLL settings are shown in the picture.

    Any unity version do you suggest?
    Thank you.
    Have a nice day.
     

    Attached Files:

  25. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,233
    The top one is correct, but the one in the Internal folder should have only "Editor" ticked under Exclude Platforms because you want to use it for all platforms except the Editor.

    You can just ignore the "Identifier uniqueness violation" warning. I've already fixed it in the v4.0 Beta which I'll be releasing later today.

    Any Unity version should work fine (and shouldn't have any impact on this issue) except for 2019.3 which has a bunch of other issues that will be fixed by the v4.0 Beta. I would recommend 2018.4 since that's the latest LTS version. Just make sure that if you change Unity versions you re-download Animancer Lite from the Asset Store because there are a few differences between versions (I suspect that's the cause of the "IAnimationClipSource' claims it is defined assembly ..." error you got).
     
    matasoy likes this.
  26. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,233
    craigjwhitmore likes this.
  27. kjhuebner

    kjhuebner

    Joined:
    Oct 2, 2018
    Posts:
    14
    Hi Kailas, as long as your discussing DLLs...

    1. What is the purpose of the DLLs in Animancer?

    2. Is there any Animancer DLLs required during runtime of a released Unity application (with Animancer), or are the DLLs wholly part of Animancer editor usage during app development?

    3. Do we have access to the DLL source code, or is there some custom native code running that you have not shared with me and other customers?

    Just to note, I'm running the Pro version beta 4, but I see it has the Animancer.Lite.dll in two folders.

    Thanks, Ken

    Animancer DLLs...
    Assets/Plugins/Animancer/Animancer.Lite.dll should only be enabled for the Editor.
    Assets/Plugins/Animancer/Internal/Animancer.Lite.dll should be enabled for all platforms except the Editor.
     
  28. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,233
    Animancer Lite doesn't include the source code, so it's all compiled into those DLLs. The first one is used in the Editor and the one in the Internal folder is used at runtime.

    Animancer Pro includes empty dummies of those DLLs to ensure that you can upgrade from the Lite version by just importing Pro over the top. Those DLLs contain no code and aren't included in builds. All the actual code is in the regular scripts.

    I've added this to the list of things the documentation needs to explain.
     
    Last edited: Dec 9, 2019
  29. kjhuebner

    kjhuebner

    Joined:
    Oct 2, 2018
    Posts:
    14
    Thanks for your thoughts on the DLLs. So going forward and to create more work for you ;)

    Animancer PRODUCT SUGGESTIONS:


    1. Remove the DLLs in Animancer Pro version if they are not required. I'm always apprehensive to put DLLs in an application, unless they are from standard libraries from large, trustworthy companies.

    2. Rearrange your folder structure and get Animancer out of the Unity "Plugin" folder. If DLLs and native code are not required in your framework, Animancer should not be in the Plugin folder, period. I believe the convention for Unity is the "Plugin" folder is reserved for native code, such as a native Java plugin that directly accesses Android OS. (I already maintain Java plugin source in this folder and a manifest.xml, so it's especially critical to me.)

    So I'm using the above rules to get Animancer in shape for including in my app. -Ken
     
  30. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,233
    Thanks for the suggestions, but I disagree on both points.

    As I said, the DLLs allow people to import the Pro version without getting errors after trying the Lite version. Otherwise the Pro version would add scripts with classes that already exist in the DLLs so it would give loads of confusing errors for new users. Anyone who buys Animancer Pro should have tried Animancer Lite first anyway, so it's not really an issue of trust. And honestly, it should be pretty obvious that the Animancer.Lite.dlls are not a real part of Animancer Pro even if the FAQ and Feature Comparison didn't also explain it.

    The Plugins folder is not reserved for native code, it's the recommended location for scripting packages and editor extensions as well. Before the introduction of Assembly Definition files, this was important because that folder is compiled separately from the rest of your code so having 3rd party code assets you aren't likely to modify very often in that folder meant they didn't need to be compiled as often and therefore improved overall compile times. That doesn't matter now that we have Assembly Definition files, but personally I much prefer having the dozen code assets I use grouped in the Plugins folder instead of having a dozen different company and/or asset names in my root Assets folder with varying naming conventions. The Plugins folder is for things that you can "plug-in" to your project and generally won't be modifying or developing as part of that project.
     
    TeagansDad likes this.
  31. kjhuebner

    kjhuebner

    Joined:
    Oct 2, 2018
    Posts:
    14
    Perhaps folder arrangement is a personal preference. I just wanted to follow current guidelines given at the Unity site, which to me implies native code for "Plugin" folder. But of course such things have evolved with lots of legacy baggage to what goes where.

    In fact, perhaps putting all 3rd party libraries in "Plugin" folder or a custom "Library" folder makes a lot of sense. Keeps it separate from our app code.

    Here's what Unity says about Special Folder "Plugin":

    https://docs.unity3d.com/Manual/SpecialFolders.html

    Plug-ins

    You can add plug-ins to your Project to extend Unity’s features. Plug-ins are native DLLs that are typically written in C/C++. They can access third-party code libraries, system calls and other Unity built-in functionality. Always place plug-ins in a folder called Plugins for them to be detected by Unity.

    You can only have one Plugins folder and it must be placed in the root of the Project; directly within the Assets folder.

    See Special folders and script compilation order for more information on how this folder affects script compilation, and Plugin Inspector for more information on managing plug-ins for different target platforms.

    Anyway, back to work for me. -Ken
     
  32. kjhuebner

    kjhuebner

    Joined:
    Oct 2, 2018
    Posts:
    14
    BUG? I noticed in the Examples/Layers scene an annoying animation issue.

    If you run the Layers.scene, and then click on each button in sequence every 1/4 second...
    Toggle Running,
    Perform Action,
    Toggle Running, and
    Perform Action.

    You'll notice on the last Perform Action the character seems to drop out of the sky for 1/2 second.

    I'm certain this should not be occurring as nothing in the high-level scripting defines a large shift in xyz position.

    Is Animancer causing this, Unity, or is there something in the animation data that would create such an effect?
     
  33. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,233
    Yeah, that's a bug in Animancer caused by changing the animation to a different layer which causes it to fade the wrong layer (or not fade out the old layer, I can't remember exactly). The result is that the total weight ends up being greater than 1 so the hip offset from the origin ends up larger than it should be (the rest of the pose is screwed too, but that's the most noticeable). It's on my to do list for v4.0.
     
  34. craigjwhitmore

    craigjwhitmore

    Joined:
    Apr 15, 2018
    Posts:
    135
    Second attempt, the forum won't allow me to edit, as apparently, a second edit is classed as spam.
    I'm having some issues using states in the state machine

    I declare Player class and an arbitrary Brain and some states;

    Code (CSharp):
    1.  
    2. public class Player : MonoBehaviour, IAnimancerClipSource
    3. {
    4. ....
    5.      public StateMachine<PlayerState> StateMachine { get; private set; }
    6.      [SerializeField]  private IdleState _Idle;
    7.      public IdleState Idle { get { return _Idle; } }
    8.      [SerializeField] private PlayerState _Locomotion;
    9.      public PlayerState Locomotion { get { return _Locomotion; } }
    10. }
    11. public abstract class PlayerState : StateBehaviour<PlayerState>, IOwnedState<PlayerState>, IAnimancerClipSource
    12. {
    13.      [SerializeField] private Player _Player;
    14.      public Player Player {get{..}set{...}}
    15. }
    16.      public  class IdleState : PlayerState {....}
    17.  
    18.      public  class LocomotionState : PlayerState {....}
    19.  
    20. public class KeyboardBrain : MonoBehaviour, IAnimancerClipSource
    21. {
    22.      [SerializeField] private Player _Player;
    23.       public Player Player{ get{...} set{...}
    24.      void Update()
    25.      {
    26.           Player.Idle.TryEnterState();  //TryEnterState() Can't be accessed from here
    27.           Player.Locomotion.TryEnterState();  //TryEnterState() Can be accessed
    28.      }
    29. }
    When the state is declared as the base class (ie LocomotionState), the function TryEnterState() can be accessed, but when declaring it as an actual state (ie IdleState), the function is inaccessible.

    Is there an easy fix for this or is casting the answer, ie to cast down to the base class or to the derived class depending on how the state is declared within the Player class?
     
  35. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,233
    ... Editing is spam but posting isn't? WTF?

    Casting would work, but you can also just call Creature.StateMachine.TrySetState(Creature.Idle).

    This is due to an unfortunate limitation in the C# language in combination with the way my FSM is implemented:
    • The base IState<T> interface doesn't have a reference to its state machine so it can't have a TryEnterState method of its own.
    • IOwnedState<T> has a reference to its state machine so it can have TryEnterState as an extension method.
    • But that method has the generic constraint that T must implement IOwnedState<T>:
      • PlayerState does implement IOwnedState<PlayerState> so it can use that method.
      • But LocomotionState does not implement IOwnedState<LocomotionState> so it can't (because polymorphism doesn't work in generic parameters).
    If you go to Animancer/Utilities/State Machine/IState.cs and have a look down the bottom of the file, there's a commented out region which you can copy into your PlayerState class to give it those methods as regular methods, which avoids the problem with generic constraints on the extension methods. I'd love to find a better way to do it, but no luck so far.
     
  36. craigjwhitmore

    craigjwhitmore

    Joined:
    Apr 15, 2018
    Posts:
    135
    That was quick!

    I'll try your suggestion. Thank you for the swift reply.
     
  37. craigjwhitmore

    craigjwhitmore

    Joined:
    Apr 15, 2018
    Posts:
    135
    I was looking at the 3D game example for reference, and used the Input Buffer described in this.
    It seems that KeyboardMouseBrain.Awake() is called before Creature.Awake(), which doesn't allow the _InputBuffer to initialise correctly with a valid StateMachine as this has not been initialised yet in Player.Awake.
    Code (CSharp):
    1. _InputBuffer = new StateMachine<PlayerState>.InputBuffer(Player.StateMachine);
    I'm not sure if it's something else that I've done that causes the brain code for awake to call before Creature.Awake.
    Moving the Awake code to the start method seems to work though. [DefaultExecutionOrder(2)] also works.
     
    Last edited: Dec 12, 2019
  38. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,233
    That shouldn't happen because Creature has a [DefaultExecutionOrder(-5000)] attribute to make it run first unless you set a different time in the Execution Order menu (select any MonoBehaivour and there should be a button for Execution Order so you can check if you somehow have it set to something else).

    Using Start is a valid way of controlling the order, but I much prefer the attribute because it gives more control (Awake and Start only give you 2 steps which isn't always enough).
     
  39. craigjwhitmore

    craigjwhitmore

    Joined:
    Apr 15, 2018
    Posts:
    135
    I omitted adding [DefaultExecutionOrder(-5000)] to Creature. thank you.
     
  40. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,233
    I just posted a minor info update about the Beta in my other thread over in the Work in Progress Forum (and I'll keep posting updates there from now on). So if you want to hear about my progress without getting a notification every time someone asks a question here, you can go there and click the "Watch Thread" button in the top right.
     
  41. DiscoFever

    DiscoFever

    Joined:
    Nov 16, 2014
    Posts:
    286
    @Kybernetik thanks for the reply; it worked (was that stupid !)

    Now i'm having a more akward problem with layers; i've setup a Mask like so :
    upload_2019-12-13_19-36-59.png

    And the code as following

    Code (CSharp):
    1.  
    2.  
    3.     public bool isAttacking;
    4.     private const int BaseLayer = 0;
    5.     private const int ActionLayer = 1;
    6.  
    7.   [SerializeField]
    8.  
    9.     private AvatarMask _ActionMask;
    10.  
    11.     void Update()
    12.     {
    13.         Vector3 _velocity = Vector3.ProjectOnPlane(_controller.GetVelocity(), transform.up);
    14.         if (Input.GetKeyDown(KeyCode.G) && !isAttacking)
    15.         {
    16.             isAttacking = true;
    17.             PlayAttack();
    18.             _Animancer.CurrentState.Speed = AttackSpeed;
    19.         }
    20.  
    21.         if (!isAttacking)
    22.         {
    23.             if (!_controller.IsGrounded())
    24.             {
    25.                 PlayJump();
    26.             }
    27.             else
    28.             {
    29.  
    30.                 if (_velocity.magnitude > 0.01f)
    31.                 {
    32.                     PlayMove();
    33.                     _Animancer.CurrentState.Speed = 2f;
    34.                 }
    35.                 else
    36.                 {
    37.                     _Animancer.CrossFade(_Idle);
    38.                 }
    39.             }
    40.  
    41.         }
    42.        
    43.  
    44.     }
    45.  
    46.     void StopAttack()
    47.     {
    48.         Debug.Log("Attack ended");
    49.         _Animancer.GetLayer(ActionLayer).StartFade(0);
    50.         isAttacking = false;
    51.     }
    52.  
    53.     public void PlayAttack()
    54.     {
    55.         var state = _Animancer.CrossFadeFromStart(Attack1, 0f, ActionLayer);
    56.         state.OnEnd = StopAttack;
    57.     }
    58.  
    Issue is that feets keep moving whilst attacking; which ... should not happen; no ?
    What am I missing here ?
     
  42. craigjwhitmore

    craigjwhitmore

    Joined:
    Apr 15, 2018
    Posts:
    135
    Are you initialising the action mask?

    Code (CSharp):
    1. void Awake()
    2. {
    3.     _Animancer.SetLayerMask(ActionLayer, _ActionMask);
    4. }
     
  43. craigjwhitmore

    craigjwhitmore

    Joined:
    Apr 15, 2018
    Posts:
    135
    To go back to my earlier question regarding the extension methods to add the TrySetState() functions to the inherited Creature class. I'm still having issue with this.
    In your example, the Idle & locomotion states can be used with the TrySetState extension method fine, but when I do a straight cut & paste of either of these methods and only rename it to something else, the TrySetState extension method doesn't show up at all.
    I am adding the namespace Animancer.FSM to my own classes, yet I can't understand why it shows for Idle & Locomotion, but for a cut & pasted function, it doesn't show at all.
    Is there some secret sauce being used and what is this?

    On a slightly different note, I was tinkering around with something like this issue about 10 years ago, having generics with different interfaces depending on the generic class used, the answer was to use a fluent interface, having the interface being passed back from functions.
     
    Last edited: Dec 13, 2019
  44. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,233
    @DiscoFever Try to narrow down the issue. Is the isAttacking flag not being set and reset at the right times? If you put a Debug.Log in PlayMode, does it still get called while you are attacking? How are you actually performing the movement? If you're using a Rigidbody, you might need to set its velocity to zero when you attack (and possibly every frame during an attack depending on how you want it to work).

    @craigjwhitmore the commented out methods for you to copy shouldn't need any renaming or editing and they aren't extension methods, the point is to have them as regular methods in your base class so you don't need to mess with generic constraints or have a using statement in every file. There isn't anything secret going on with the FSM system, it's all in those files in the State Machine folder so you could take just that folder (without the rest of Animancer) into another project if you wanted to. I don't think a fluent interface would help here, but if you can come up with a way for it to work I'd love to hear it because this is one of the few things I don't like about my FSM system.
     
  45. craigjwhitmore

    craigjwhitmore

    Joined:
    Apr 15, 2018
    Posts:
    135
    I'll have a think about the fluent methods. I lost the code about 6 months ago when my backup hard drive bust due to a power spike. irony is, that I put my code onto this drive from my old drive for fear that the old one would break due to age, as it's over 10 years old, but it's still working perfectly while my new hard drive is busted.

    I can't get my head around why the extensions work in one file and not in another nearly identical file, which bugs me more than just accepting it and adding the extra code.
     
  46. zedz

    zedz

    Joined:
    Aug 31, 2013
    Posts:
    191
    Love this assest, very well made & documented

    if you do the following quickly, bad things happen

    animancer.CrossFade(_Walk);
    animancer.CrossFade(_Pain, 0.3f, TorsoLayer )
    animancer.CrossFade(_Idle);
    animancer.CrossFade(_Pain, 0.3f, TorsoLayer )

    Heres the same thing happening from example 07
    http://zedzeek.com/junk/animation.jpg <- happens if you click rapidly between the 2 states.
    How to prevent this?

    ta zed
     
  47. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,233
  48. MaliceA4Thought

    MaliceA4Thought

    Joined:
    Feb 25, 2011
    Posts:
    406
    Hi Kybernetik.. a quick question.. this looks like a fabulous tool, but in my case i am working on a multiplayer game using MIRROR. Is it possible to use Animancer in a multi-player environment, as currently, the anims are passed to the controller by the network.. how would a similar system work with animancer? Regards.
     
  49. Kybernetik

    Kybernetik

    Joined:
    Jan 3, 2013
    Posts:
    2,233
    Unfortunately the way Animancer works means that it can't be easily synchronised in the same way and even if it could it wouldn't be very useful. In this case the restrictiveness of Animator Controllers actually works in their favour:
    • Since the structure of the Animator Controller can't change at runtime, one side can just say the current state is number 15 and trigger parameter 6 has been set to true and the other side will know exactly what that means. But in Animancer the AnimationClips can come from anywhere and change at any time so it would need a way to identify each clip for the other side. That's not an impossible problem to solve, but:
    • Animator Controllers act as a finite state machine where it does whatever it wants internally and your scripts have to constantly check what it's doing when deciding what they want to do. But Animancer doesn't act on its own like that, it only does what your scripts tell it to do so even if it were to synchronise its animations your scripts would not be checking which animation it is playing so the sync might tell it to play an attack animation but your movement script would still be active.
    • The presence of End Events (and the new Animancer Events in v4.0) would also massively complicate the system because they use regular delegates which can't be easily serialized to be send over the network.
    That's not to say networking is impossible with Animancer, it just means you need to synchronise whatever finite state machine or other system you are using to control your characters instead of the animations themselves. If you synchronise the fact that your attack script is now the active state, the client will properly know it is attacking and play the appropriate animation. I want to make an example to demonstrate how to do it, but my To Do list is pretty long so I might not get it done for v4.0 if I want to get it released by the end of January.
     
  50. MaliceA4Thought

    MaliceA4Thought

    Joined:
    Feb 25, 2011
    Posts:
    406

    Thank you for the awesome reply.. I will deffinately get and use animancer for a single player setup I am working on and put some thought into this re multiplayer in regards to passing appropriate info. Thanks again.