Search Unity

  1. Looking for a job or to hire someone for a project? Check out the re-opened job forums.
    Dismiss Notice
  2. Unity 2020 LTS & Unity 2021.1 have been released.
    Dismiss Notice
  3. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice

Official State Machine

Discussion in 'Open Projects' started by Dustin_00, Sep 29, 2020.

  1. Eyap

    Eyap

    Joined:
    Nov 17, 2017
    Posts:
    28
    Profiling:
    Doing GetInitialState() 100'000 times in the nested StateMachine took me 1605ms. Reduced to 1ms with the caching (only assignation).
    Using the unity profiler and focusing on the StateMachine, the performance were similar with/without the nested StateMachine, so I suppose it's good like this.

    Sure ! Well, actually it really is the same as before. The only difference is the new StateMachineActionSO with the same UI as the StateMachine monobehaviour (table reference + debug).

    Speaking of the debug, do you mind if it's not internal anymore ? I can't find a way to keep it without making it public...
     
  2. deivsky

    deivsky

    Joined:
    Nov 3, 2019
    Posts:
    86
    Actually, why not simply passing in the same SM you're getting from Awake? That'd avoid the whole interface.

    Nice.

    You're adding the changes to the same assembly, you should be able to reference it? It's just in a different namespace and you have to use #if UNITY_EDITOR.
     
  3. cirocontinisio

    cirocontinisio

    Unity Technologies

    Joined:
    Jun 20, 2016
    Posts:
    742
    I hope it's the same, I read this sentence many many times and I didn't get it.
    Are you saying that an Action can be a StateMachine of its own now?
     
  4. Eyap

    Eyap

    Joined:
    Nov 17, 2017
    Posts:
    28
    The interface is kind of mandatory. The TransitionTableSO and the ActionSO were referencing the StateMachine. As the idea was to be able to use actions and tables indiscriminately for the main or the nested FSM, I made the switch to the interface.
    For the internal : A public interface can't contain an internal property (C# limitation), that's why I was asking.
     
  5. Eyap

    Eyap

    Joined:
    Nov 17, 2017
    Posts:
    28
    Oh, no ! I know I should have posted a screenshot in the first place, sorry :
    SMActionSO.png
    What I mean is that the StateMachineActionSO is acting like the StateMachine monobehaviour component (see below for reference). It references a field for the table and that's it.

    Screenshot from 2021-02-01 15-51-33.png
     
  6. deivsky

    deivsky

    Joined:
    Nov 3, 2019
    Posts:
    86
    What I mean is, can't you simply do this inside the StateMachineAction?
    Code (CSharp):
    1. Awake(StateMachine stateMachine)
    2. {
    3.     _initialState = _transitionTable.GetInitialState(stateMachine);
    4. }
     
  7. Eyap

    Eyap

    Joined:
    Nov 17, 2017
    Posts:
    28
    Oh sorry, so you mean keeping the interface elsewhere but giving the object in the Awake ? Well yeah, I suppose we can also do this. I have to admit that I'd prefer keeping the interface tough (no particular reasons, except that it may be more generic for possible future features).
     
  8. deivsky

    deivsky

    Joined:
    Nov 3, 2019
    Posts:
    86
    I mean not having the interface at all, didn't you make it so you had something to pass into GetInitialState ? But that something can be the original StateMachine, or am I missing something?
     
  9. cirocontinisio

    cirocontinisio

    Unity Technologies

    Joined:
    Jun 20, 2016
    Posts:
    742
    Hmmm.
    Main fear: the transition. How do you transition "inside" this inner state machine, and what happens to the outmost?

    Also, what do you imagine we could use this for, in our simple game?
     
    deivsky likes this.
  10. deivsky

    deivsky

    Joined:
    Nov 3, 2019
    Posts:
    86
    The inner SM wouldn't really be my main concern, it's basically doing in OnUpdate the same thing that the regular SM does in Update, it's gonna perform all the Actions of the current State, and then check for Transitions.

    My fear would actually be the outer SM, which could in theory have more Actions (including more StateMachineActions) running alongside the inner SM. And on top of that, my biggest fear is that the Transitions to leave the State that's holding the StateMachineAction would need to somehow be aware of the current State of the inner SM, otherwise, it might misbehave.

    It's a cool concept, you could have some super deep State Machin-ception sort of thing going on. But then, if something breaks down there you'll probably get stuck in limbo for years trying to fix it :D
     
  11. deivsky

    deivsky

    Joined:
    Nov 3, 2019
    Posts:
    86
    Sorry, this is a mouthful.
     
  12. cirocontinisio

    cirocontinisio

    Unity Technologies

    Joined:
    Jun 20, 2016
    Posts:
    742
    Yes that's what I meant. I'm sure you guys will figure out the tech easily. I'm more worried about it from the UX point of view. It might be configured wrong, and then... you debug it!! :D
     
    Eyap likes this.
  13. Eyap

    Eyap

    Joined:
    Nov 17, 2017
    Posts:
    28
    Sorry for the late reply, I double-checked to be sure. Yes, you are right !
    I still have some concerns with passing the StateMachine though : If I have a twice-nested SM, I will pass the parent (main) StateMachine component to the grandchild, thus making the first child "shunted". It doesn't do anything and works now, but I fear that it's gonna make problems later... Well, maybe it's not worth the hassle now.
    I have to admit that I liked the cleanliness of the interface, but I may be biased ;).

    I think it's gonna improve the UX a lot, and make things easier for designers.

    I got the idea when playing around with the slime critter : I wanted to add some action when it was roaming. But what I call "Roaming" is in fact two states in the current SM : The idle state and the roaming state (which is more like a "MoveToAPointState").
    So I would need to add my custom action on both those states. It's kind of redundant and error-prone.
    It's the same thing for conditions : currently both of theses states have the same transition : If the player enter the alert zone, it enter the fight state. Redundant and make the table bigger and bigger.
    If I want to add a "Sleep" state (just an example) triggered at some time, I would need to add two transitions : From the current IdleState and from the current RoamingState. Same thing, may be error-prone.

    Making a nested SM with both the Idle and current Roaming states would make the overall schema more clear.
     
  14. deivsky

    deivsky

    Joined:
    Nov 3, 2019
    Posts:
    86
    Unless the implementation changes, it shouldn't really cause any issue. The only reason to pass in the SM Component was to provide access to GetComponent. I could've simply passed in the GameObject, but I assumed most actions and conditions would want to reference the same few scripts, that's why I implemented the GetComponent with caching. So in terms of something breaking because of sending the main SM to the great-great-grandchild, I really don't see it possible. Unless, again, something in the implementation changes. Or for some reason you want the child to control the parent in some way.

    It was anything but clean to me, mimicking all the methods of a regular component... that's why I insisted on removing it!

    Ahh, knowing what you were trying to accomplish does make it make a lot more sense. I was having the same issue with the main character SM. Idle and Walking are exactly the same in terms of Transitions so I really wanted to merge them into one State, but that would've required a lot of rework in their specific Actions so I didn't move forward with it.

    Having a nested SM would definitely be of use in those situations.
     
    Eyap likes this.
  15. Eyap

    Eyap

    Joined:
    Nov 17, 2017
    Posts:
    28
    Yeah, I understand what you mean. I made a PR with the new StateMachineActionSO (without the interface ;) ). I tried to describe a little bit further what the changes were, and included some screenshots.
    And yes, I only took a quick glance at the Protagonist SM, but I'm sure it can have some merges as well (for Idle+Working, or for Sliding+MidSliding).

    Speaking of that, I don't think that caching is necessary. If we look at the current implementations of the StateSO, they are all also caching theirs references. That means every component is cached twice, or am I missing something ?
     
  16. deivsky

    deivsky

    Joined:
    Nov 3, 2019
    Posts:
    86
    It only helps during Awake where, for instance, most Actions and Conditions in the main character get the Protagonist script, so thanks to the caching in the SM we only need to fetch it from the backend the first time GetComponent<Protagonist> is called, subsequent calls then just perform a small lookup to get the reference.

    But yeah I'm not too sure about it either, I've thought about removing it as well but just never did, and considering the small amount of SMs we have running at the same time, we probably don't need it
     
    Eyap likes this.
  17. Nsuidara

    Nsuidara

    Joined:
    Apr 22, 2016
    Posts:
    16
    meybe easy manage Transitions with graph ?

    upload_2021-2-12_20-14-28.png
     
    Amel-Unity, Sarai, Eyap and 1 other person like this.
  18. Harsh-099

    Harsh-099

    Joined:
    May 1, 2020
    Posts:
    185
    How do made a graph thing in unity? I don't know. (Any good tutorial).
    But it's a good thing. Graph is best for transitions.
     
  19. Nsuidara

    Nsuidara

    Joined:
    Apr 22, 2016
    Posts:
    16
    You can use UnityEditor.Experimental.GraphView;
    like Shader Graph using and other new tools Unity.

    For my last screen, i made state machine similar, about State / Action / Transition idea.
    but i don't like concept example ConditionSO... abstract ScriptableObject.

    bc. you must create IsMovingConditionSO from ConditionSO, IsMovingCondition and next create object from IsMovingConditionSO in editor.

    I create one ConditonSO and next IsMovingCondition, IsHoldingJumpCondition... and next create SO from ConditonSO
    and just select script. (I just use Type & Assembly)

    Example.
    upload_2021-2-13_18-47-49.png
    Create object from ActionSO and select "AerialMovementAction", problem is ScriptableObject can't serialize Type or object... so here always string....
    when OnEnable in SO then i convert string to Type... when i State -> State create instance type etc.

    Like about paramters - i have generic ActionSO, so can't define all paramters for all Scripts
    then i made array Parameters struct like Name/Value and next Action in play mode, have ManagerParamters and when Awake (once time) then i have example method " GetFloat(string name, float defaultValue)"

    Code (CSharp):
    1.         public override void Awake()
    2.         {
    3.             movement = Core.GetComponent<Movement>();
    4.             _speed = Parameters.GetFloat("Speed", 7f);
    5.         }
     
    Sarai likes this.
  20. cirocontinisio

    cirocontinisio

    Unity Technologies

    Joined:
    Jun 20, 2016
    Posts:
    742
    That looks pretty cool, @Nsuidara. I'll merge this thread with the StateMachine thread, so the people that are interested in that will get notified.
     
  21. nunodelgado

    nunodelgado

    Joined:
    Nov 26, 2019
    Posts:
    1
    Hello everyone!

    After reading the two first pages of this thread I quickly noticed I did not have the skills to understand (straight away) how the state machine was implemented. For this reason I decided to do reverse engineering and learn how it works. I ended up creating a flow chart in Milanote of this system with notes about how it works. Since this system seems quite settled, I would like to share the flow chart here so that anyone that feels the same as I did can easly have an overview of the system:

    https://app.milanote.com/1LpxWN1DSCAI73?p=60mTNFnUNqS

    You can also make comments in the flow chart about how to improve the flow chart or point out errors that it may have. Maybe this flow chart can also help other people making decisions in this project, who knows.

    I am still a little bit overwhelmed and not confident enough to use it in one of my own projects because i am not 100% aware of its limitations. But I really enjoyed to learn all this amazing stuff and read through all the 7 pages in this thread :D

    So thank you everyone for taking your time to invest in this thread. I hope i was able to give back with this flow chart :)
     
    Eyap, cirocontinisio, Elbarzo and 2 others like this.
unityunity