Search Unity

Goto Animator Sub-State Machine's entry node and not to a specific state within?

Discussion in 'Animation' started by Silly_Rollo, Mar 18, 2015.

  1. Silly_Rollo

    Silly_Rollo

    Joined:
    Dec 21, 2012
    Posts:
    501
    With the new Entry / Exit node it seems like I could get away from awkward Trigger / etc usage to control flow directly in script just by triggering to go to an Animator sub state machine, but I can't seem to find any scripting support for changing sub state machines. Crossfade or Play doesn't work and gets a "Animator.GotoState: State could not be found" error and checking Animator.HasState shows false. So I guess they aren't "states"?

    Is there a way to do this?

    Note I do not want to have to make transitions in the Animator. Looking for a pure script solution here.
     
  2. Silly_Rollo

    Silly_Rollo

    Joined:
    Dec 21, 2012
    Posts:
    501
    If there really isn't a way to do this via script that feels like a pretty big omission.
     
  3. Silly_Rollo

    Silly_Rollo

    Joined:
    Dec 21, 2012
    Posts:
    501
    Hell is there any API access to sub states? It looks like they are pretty black box. No way to query what the current Sub state is and no way to switch them manually
     
    Last edited: Mar 19, 2015
  4. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
  5. Silly_Rollo

    Silly_Rollo

    Joined:
    Dec 21, 2012
    Posts:
    501
    That isn't what I was asking. I wasn't asking about states but the entire sub-state machine. How do I ask what substate machine I am in? If I hash from AnimatorToHash every single animation state within a sub-state machine I can check the hash vs it but that's pretty awkward.

    I guess I can substring the statename but thats pretty hacky too.
     
    Last edited: Mar 19, 2015
    IgorAherne and jwinn like this.
  6. Runemark

    Runemark

    Joined:
    May 23, 2013
    Posts:
    244
    I'm curious too, an animator.CrossfadeToSubstate method or something like that could give the current mecanim system more power.

    Right now I would like to do this:
    I have multiple weapon sets, that is a simple int parameter in the animator. (ex: sword&board = 0, twohanded = 1 ...etc)
    I created an "Attack" sub-state, and put every attack animation in it.

    Then from script I would like to trigger in a similar way as the Crossfade function does: animator.Crossfade("Attack", 0.1)
    But this only seems to work with substates,

    Update: also, how can I check which sub-state is active in the animator? This doesn't work:
    _animator.GetCurrentAnimatorStateInfo(3).IsName("Attack")
     
  7. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    You can't ask in which sub state machine the controller is currently at runtime.
    The best you can do is use Tag on animator state to mark all your state that are part of your sub state machine.

    So if your sub state machine is called 'attack' then all your state in this state machine should have tag set to 'attack'.
    then at runtime you can do GetCurrentAnimatorState().IsTag("attack");

    http://docs.unity3d.com/ScriptReference/Animator.GetCurrentAnimatorStateInfo.html
    http://docs.unity3d.com/ScriptReference/AnimatorStateInfo.IsTag.html
     
    SvOzMaS and jwinn like this.
  8. CDF

    CDF

    Joined:
    Sep 14, 2013
    Posts:
    1,311
    Are there any plans to query subStateMachines at run time in the future?
     
  9. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    No not in the near future, it the first time we get a request for this.

    You can workaround by using tag to add user information on state.
     
  10. Runemark

    Runemark

    Joined:
    May 23, 2013
    Posts:
    244
    Thanks for the answare. I figure out something similar, but instead of using tags I made a simple state machine behaviour, that updates the ActiveSubstate string array in my main character controller script.

    I didn't know about the tags. Thank you for the information. :)

    However this didn't solve the Crossfade problem. I could make a workaround and put an empty state to every substate, and set a transition from the entry node to this empty. It's not enough pretty for me. :D Any idea how could I use something crossfade like with substates?

    (I put my state machine behaviour here, maybe somebody find it useful.)

    Code (CSharp):
    1. public class UpdateSubStateName : StateMachineBehaviour
    2. {
    3.     public string Name;
    4.     CharacterControl _me;
    5.  
    6.      // OnStateEnter is called before OnStateEnter is called on any state inside this state machine
    7.     override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    8.     {
    9.         if(_me == null)
    10.             _me = animator.GetComponent<CharacterControl>();
    11.  
    12.         Debug.Log(layerIndex);
    13.         _me.ActiveSubstate[layerIndex] = Name;
    14.     }
    15.  
    16.     // OnStateExit is called before OnStateExit is called on any state inside this state machine
    17.     override public void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    18.     {
    19.         if(_me == null)
    20.             _me = animator.GetComponent<CharacterControl>();
    21.  
    22.         _me.ActiveSubstate[layerIndex] = "";
    23.     }
    24. }
     
  11. Bravo_cr

    Bravo_cr

    Joined:
    Jul 19, 2012
    Posts:
    148
    Well, I have hit the same wall as you. Can't enter to a Sub-state machine using code without using animator parameters.
     
  12. KyleStaves

    KyleStaves

    Joined:
    Nov 4, 2009
    Posts:
    821
    This is a very surprising and strange omission. We were very excited when entry/exit nodes were added, it greatly simplifies our animation setup. Not being able to trigger them manually defeats their purpose for anyone who doesn't want a ton of duplicated logic in code and set up manually in the animation controller :(
     
    IgorAherne likes this.
  13. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    This is on the short term road map, we would like to fix this for 5.2
     
  14. KyleStaves

    KyleStaves

    Joined:
    Nov 4, 2009
    Posts:
    821
    Excellent news! Will be too late for our current projects, but it will let us set things up in a more comfortable way for the future. Thanks!
     
    theANMATOR2b likes this.
  15. Hugbot

    Hugbot

    Joined:
    Jul 10, 2013
    Posts:
    10
    @Mecanim.Dev

    I was just trying to trigger an animation sub state's entry node, using the new beta version. However when I perform that action I get:
    "Animator.GotoState: State could not be found"

    I am using the Unity 5.2.0f1 release candidate version under the assumption that this feature was already added.

    The call to the animator is:
    animator.play(<substate machine name>);

    I also tried:
    animator.play(<substate machine name>.Entry);

    Is there something I may be misunderstanding with regards to how to use this new feature?
     
    dreasgrech likes this.
  16. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    The feature didn't make it in 5.2, so this is expected.
    The feature is still planned and active on our roadmap
     
  17. Hugbot

    Hugbot

    Joined:
    Jul 10, 2013
    Posts:
    10
    Last edited: Aug 27, 2015
    IgorAherne and dreasgrech like this.
  18. Pacosmico

    Pacosmico

    Joined:
    Jan 16, 2014
    Posts:
    14
    @Mecanim.Dev @KyleStaves
    ¿This is still not resolved? I'm guessing not, but I think I found a very subtle way to workaround with very low cost to update when it's resolved.
    I'm going to make an empty (basic) state that instantly enters the SubMachine, this way is virtually the same doing Play(SubMachine) as Play(SubMachineEmptyStateDoorThing)

    PS: Made it, and it work, I even have the SubState Machine and the EmptyStateDoorThing with the same name (probably will become a problem someday?) The transition between is with an exit time of .0001 instead of 0 because, having it like that (zero) make it bahave like 1 sec or so
     
    theANMATOR2b likes this.
  19. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    This is still on the road map.

    If an unique name become a constraint you will probably get a warning when you will open your project to fix all your duplicate, So don't worry for now.

    Yes this is a know issue.
     
    Pacosmico likes this.
  20. jwinn

    jwinn

    Joined:
    Sep 1, 2012
    Posts:
    88
    @Mecanim.Dev Thanks for the suggestion of using tags. For me, this was a good workaround for not being able to tell which sub-state machine you're in. Are these items listed somewhere on the public roadmap? I'm not seeing it; maybe it's under another larger item or it's too small of a feature to list.

    Also, are there plans to have an "Any State" node that's local to the sub-state machine? Feedback requests: [1] [2]
     
  21. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    Hi jwinn,

    Not all feature are on the public roadmap.
    And for the Any state node local to a sub statemachine it's not planned right now.
     
  22. dreasgrech

    dreasgrech

    Joined:
    Feb 9, 2013
    Posts:
    205
    Is this feature included in Unity 5.3?

    I really, really need this functionality for my project:
    Code (CSharp):
    1. animator.CrossFade("Layer.StateMachine.Entry", ...);
     
  23. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    nope, but it still on the road map for the next release
     
  24. kilik128

    kilik128

    Joined:
    Jul 15, 2013
    Posts:
    909
    somethink's Strange with substate any state confict with entry condition
     
  25. dreasgrech

    dreasgrech

    Joined:
    Feb 9, 2013
    Posts:
    205
    5.3, maybe? :rolleyes:
     
  26. OnlyFails

    OnlyFails

    Joined:
    Jan 24, 2015
    Posts:
    3
    If Unity hasn't disappointed you by now I'm impressed you made it this far mate
     
  27. deram_scholzara

    deram_scholzara

    Joined:
    Aug 26, 2005
    Posts:
    1,043
    That being 5.4 - is it in the 5.4 beta?
     
  28. dreasgrech

    dreasgrech

    Joined:
    Feb 9, 2013
    Posts:
    205
    What's the status on this one?

    I am still in great need of this functionality:

    Code (CSharp):
    1. animator.CrossFade("Layer.StateMachine.Entry", ...);
     
    Eluem likes this.
  29. wonson

    wonson

    Joined:
    Apr 21, 2016
    Posts:
    5
    Maybe we should name the state like this instead.
     
  30. Silly_Rollo

    Silly_Rollo

    Joined:
    Dec 21, 2012
    Posts:
    501
    Came to check in on this again and not surprised to see this is a still maybe someday thing.
     
  31. Xylph

    Xylph

    Joined:
    Dec 11, 2013
    Posts:
    30
    Bumped to the problem also. I wanted to transition directly to the StateMachine instead of a specific inside that state machine.This made the Entry node useless. I guess I'll just add a filler state (works like an entry node) for now.

    I'm also eager to see Any State transition directly to Exit node. I think it's better if Any State will just refer to the local states within the sub state machine. The sub state machine should be able to handle itself without worrying about other sub state machines. I added an extra boolean parameter for each substate machine just to filter out transitions that will come from other state machines. I set this boolean to true whenever I'm inside the state machine and set it to false after exiting.
     
    IgorAherne likes this.
  32. Matthew-Miller

    Matthew-Miller

    Joined:
    Apr 20, 2015
    Posts:
    2
  33. thelebaron

    thelebaron

    Joined:
    Jun 2, 2013
    Posts:
    857
    @Mecanim.Dev Any updates? Bumping this because it I'd really like this to be a feature too
     
    jwinn likes this.
  34. thelebaron

    thelebaron

    Joined:
    Jun 2, 2013
    Posts:
    857
    Also is this actually on the roadmap as I dont see it listed on the website.
     
  35. dreasgrech

    dreasgrech

    Joined:
    Feb 9, 2013
    Posts:
    205
    Any news on this?

    Are we now able to query if we are in a particular sub-state machine?
     
  36. v01pe_

    v01pe_

    Joined:
    Mar 25, 2015
    Posts:
    71
    I've got a good workaround for the local any state node:
    Add this behavior inside the sub state machine (not on any particular state, click on empty space inside the sub state machine):

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class StateMachineActive : StateMachineBehaviour
    4. {
    5.     public string syncParameter;
    6.  
    7.     public override void OnStateMachineEnter(Animator animator, int stateMachinePathHash)
    8.     {
    9.         animator.SetBool(syncParameter, true);
    10.     }
    11.  
    12.     public override void OnStateMachineExit(Animator animator, int stateMachinePathHash)
    13.     {
    14.         animator.SetBool(syncParameter, false);
    15.     }
    16. }
    17.  
    Then add a bool parameter e.g. called "in sub" and put the same name in the field of the sub state machine behavior. Now the bool parameter will indicate if currently in the sub state machine and can be added as additional condition for the transition from any state. It's a bit hacky, but works for me.
     
  37. jaybennett

    jaybennett

    Joined:
    Jul 10, 2012
    Posts:
    165
    @v01pe_

    does that introduce a frame delay?

    right now my approach is similar but different. i used an integer parameter to define the "stance" or substatemachine and then use the change in that param to go to different substatemachines.

    however, I found that I needed to set this param to 0 to "clear" the state (enter back to the top level state machine) and then wait a frame before setting the desired target substatemachine.
     
  38. v01pe_

    v01pe_

    Joined:
    Mar 25, 2015
    Posts:
    71
    @jaybennett

    I was curious and made some tests with our character state machine.

    Here's a log of states and sub state machines that are travered when our character is idling (default state), walks for a short time and then idles again. We use transition states and the transition time between all states is always 0 (b/c 2d frame by frame animations). The logs are generated by state machine behaviors, that listen to the `OnState(Machine)Enter` and `OnState(Machine)Exit` events, in this format:
    • `Idle` is in the sub state machine `Idle Sub` (hash -1885616935)
    • `Walk` is in the sub state machine `Walk Sub` (hash 195196422)
    • The transition states `Idle To Walk` and `Walk To Idle` are in the parent sub state machine (it's not the root), that contains Idle Sub and Walk Sub
    • `Unused` is a pseudo state (in Walk Sub) that holds no animation, but is a workaround to transition from the `(Sub) Any State` to `Exit` which isn't possible directly.
    A few observations:
    • If the default state (of the whole state machine) is in a sub state machine, we don't get the corresponding `OnStateMachineEnter` callback (!!) in the behavior
    • `OnStateMachineEnter` happens before `OnStateExit` of the parent sub state machine (although transition time is 0)
    • `OnStateMachineEnter` / `OnStateMachineExit` and `OnStateEnter` / `OnStateExit` happen on the same frame
    • We don't get the `OnStateMachineExit` callback (!!) at the transition from `Idle` to `Idle To Walk` (although the latter is in the parent sub state machine), because the transition is directly to the state, rather then to the `Exit` node (and then to the other state) - same goes for `OnStateMachineEnter` if you transition to a state inside a sub state machine directly
    • I found no way of keeping `Unused` to a duration of 0 frames in my setup (not super dramatic if you untick "Write Defaults" in the state)
    • The parent sub state machine never get's Enter/ Exit callbacks, b/c all states and sub state machines are within it.
    Conclusion:
    • There seems to be no frame delay between entering the state and it's sub state machine @jaybennett
    • A real `Any Sub State` would be nice!
    • Always use the `Entry` and `Exit` nodes if you depend on the `OnStateMachineEnter/Exit` callbacks, rather than transitioning directly from states to states in the parent/sub state machine. I'd say it is a bug, that the callbacks aren't triggered otherwise. /cc @Mecanim-Dev
     
    Last edited: Dec 22, 2016
    theANMATOR2b likes this.
  39. v01pe_

    v01pe_

    Joined:
    Mar 25, 2015
    Posts:
    71
    I ran another quick test, with a test state in the sub state machine that has the behavior I posted 2 days ago. When I add a transition from the `Entry` node to this test state if the bool parameter is true (that the behavior sets) it will use that transition and go to the test state rather than to the default state of that sub state machine. So there definitly seems to be no delay happening.
     
  40. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    Exactly, those callback are badly named, it should be OnStateMachineEntryNode/OnStateMachineExitNode, those callback are only triggered when a transition pass by the entry/exit node, It's not a bug, it the expected behaviour.

    Also those callback are not called at the same time than all the other callback, they are called while we do evaluate the statemachine to allow you to choose where you want to transition. Also as soon as you define one callback OnStateMachineEntryNode/OnStateMachineExitNode the whole statemahcine evaluation is not multithreaded anymore as we need to call script code.
     
  41. IgorAherne

    IgorAherne

    Joined:
    May 15, 2013
    Posts:
    393
    @Mecanim-Dev, need this feature badly!
    Rather, give a callback to StateMachine once any state inside of it is entered, or when the state machine is exited
     
    Last edited: Jan 27, 2017
  42. IgorAherne

    IgorAherne

    Joined:
    May 15, 2013
    Posts:
    393
    I will contribute a little. I've made an observation that OnStateUpdate() is called for StateMachineBehaviour that sits on StateMachine, while any state inside of that stateMachine is active.

    As long as you have a Singleton in your scene which is dispatching Updates(), you can use the following code instead of StateMachineBehaviour:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. // Igor Aherne 01/27/2017, MIT license
    6. // igor.aherne.business@gmail.com
    7. // https://www.youtube.com/user/Kapurod
    8. //
    9. // This class allows you to automatically invoke Enter and Exit function as the state (or stateMachine)
    10. // holding this script is entered / exited.
    11. // Inherit from this class instead of StateMachineBehavior, or extend the functionality of my class:
    12. public abstract class AutomaticSMBehavior : StateMachineBehaviour {
    13.  
    14.     #region internal vars
    15.         //true if this object got OnEnter called already, but not yet OnExit().
    16.         [System.NonSerialized]
    17.         protected bool _initialized = false;
    18.  
    19.         [System.NonSerialized]
    20.         protected Animator _animator;
    21.         [System.NonSerialized]
    22.         protected int _layerIndex = -1;
    23.  
    24.         [System.NonSerialized]
    25.         float prevUpdateTime = float.MinValue;
    26.         // '2' if we just ran UpdatedByCore and unity no longer invokes this state's (or stateMachine's) OnStateUpdate():
    27.         // '1' if we just ran UpdatedByCore()
    28.         // '0' if we just ran OnStateUpdate(), invoked by unity, meaning we are still inside of our state.
    29.         [System.NonSerialized]
    30.         int _abandonment_ix = 0;
    31.     #endregion
    32.  
    33.  
    34.  
    35.     //Called by unity, EVEN when our nested state gets played (if we are a stateMachine).
    36.     //Therefore, we cannot rely that the supplied 'stateInfo' is really us and not our sub-state machine.
    37.     //however, we can use layer and animator, - they are definitelly ours.
    38.     public override void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
    39.  
    40.         // notice, will be called multiple times, if OUR state has sub-states which are transitioning
    41.         // into each other, inside of us. Therefore, we will prevent getting multiple update invocation:
    42.         if(Time.time == prevUpdateTime) {
    43.             return;
    44.         }
    45.  
    46.  
    47.         //else, function is ran for the first time in this Frame:
    48.         prevUpdateTime = Time.time;
    49.  
    50.         #region initialization stage:
    51.         if (_initialized == false) {
    52.             //otherwise, initialize:
    53.             _initialized = true;
    54.             Core.instance.registerForUpdate(this.UpdatedByCore);
    55.  
    56.             _animator = animator;
    57.             _layerIndex = layerIndex;
    58.             _abandonment_ix= 0;
    59.  
    60.             OnEnter();
    61.         }
    62.         #endregion
    63.  
    64.  
    65.         //show that OnStateUpdate() was ran in this frame.
    66.         _abandonment_ix = 0;
    67.  
    68.         OnUpdate();
    69.     }
    70.  
    71.  
    72.  
    73.     //hooked up to the singleton called "Core". We hooked up this function to Core.update delegate:
    74.     void UpdatedByCore() {
    75.         _abandonment_ix++;
    76.  
    77.  
    78.         if(_abandonment_ix > 1) {
    79.               //Unity no longer runs OnStateUpdate(), meaning the state (or stateMachine) is no longer current.
    80.             Leave();
    81.         }
    82.     }
    83.  
    84.  
    85.  
    86.     private void Leave() {
    87.         //reset flag variables and references:
    88.         _initialized = false;
    89.         _animator = null;
    90.         _layerIndex = -1;
    91.         _abandonment_ix = 0;
    92.  
    93.         Core.instance.unregisterForUpdate(this.UpdatedByCore);
    94.         OnExit();
    95.     }
    96.  
    97.  
    98.     protected abstract void OnEnter();
    99.     protected abstract void OnUpdate();
    100.     protected abstract void OnExit();
    101. }
    102.  

    As you can see, the code gives you 3 callbacks - one when the state or any subState is executed for the first time,
    one when we get an update from our state or any sub-state, 1 when we no longer get updates (meaning we left the state and its subStates)

    I used my own singleton, called Core to invoke updates.

    Notice, we store a reference to Animator, and have access to our layer, but we don't have information about AnimatorStateInfo.

    That's intended, - unfortunatelly, we cannot get AnimatorStateInfo, since it could belong to our sub-states (we would be getting updates along with them as well)


    How it works:
    The idea is that our Core singleton will be invoking updates no matter what, but Unity willl stop invoking as soon as state is exited. This allows us to "capture" the moment when the two functions stop running in parallel
     
    Last edited: Jan 29, 2017
    theANMATOR2b likes this.
  43. IgorAherne

    IgorAherne

    Joined:
    May 15, 2013
    Posts:
    393
    Actually it will not work, I've just encountered that if we call Animator.CrossFade into some state, unity will continue invoking OnStateUpdate on the previous StateMachine as if we are still inside of it . Goddamn
     
  44. kindingerlek

    kindingerlek

    Joined:
    Jul 7, 2015
    Posts:
    10
    @Mecanim-Dev

    There is some plan to crossfade between substates ? At moment, we can create transitions between them from the editor, but for every state inside of substate needs link to exitNode.
     
  45. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    I'm not sure I understand the question, you can already crossfade between all state in a controller, by substates do you mean statemachine?
     
  46. IgorAherne

    IgorAherne

    Joined:
    May 15, 2013
    Posts:
    393
    @kindingerlek, @Mecanim-Dev we need a possibility to hit the "Enter" and "Exit" nodes directly, from anything that's inside any sub-state machine.

    We understand that it would require considering a larger number potential destination sub-state machines (their Enter nodes) and any of their states, but would be an enormous simplification to the Mecanim. In fact, it would be the True Encapsulation that Mecanim was dreaming about for so long.

    Each sub-state machine has its own exit node that can be hit by any state within that machine, including "Any state--->Exit", including "Enter--->exit".

    And please make it trigger appropriate StateMachineBehavior callbacks, so that we can cross-fade into a sub-state machine (and unity will decide what we really mean by that, will select appropriate destination state from that Enter node), not just a "cross fade into state" - which would bypass any of the callbacks :(

    Just make it happen, damnit!
     
  47. chrismarch

    chrismarch

    Joined:
    Jul 24, 2013
    Posts:
    472
    Yes, this whole thread is asking for the ability to interact with StateMachines via the Animator class as we can with AnimatorController. This seems the most straightforward way to follow the advice of mecanim devs to "avoid Any State transitions in favor of crossfade" for nontrivial projects where StateMachines typically are what you are crossfading to conceptually, even though Animator currently only allows you to crossfade to one of the States in our StateMachines. The fact that Animator can't query the current StateMachine is another example of how the current Animator interface fights the inevitable structuring of nontrivial animations into StateMachines with multiple States.
     
    clownhunter, Alic and Nairda_Draziw like this.