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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

How to implement FSM in ECS architecture

Discussion in 'Entity Component System' started by dongyiqi, Apr 13, 2018.

  1. dongyiqi

    dongyiqi

    Joined:
    Nov 7, 2017
    Posts:
    11
    I want to convert my game code from OOP(GameObject) to DOP(ECS). But I have no idea how to convert the Finite State Machine in the new system.
    One way I know is change the composition the each component which represent a state. eg: if a entity is idle, idleComponet is attached to that entity, then the entity is running,then remove the idleCompoent and add runningComponent.
    If there will be a best practice or not ?
     
    Gibbonfiend likes this.
  2. floboc

    floboc

    Joined:
    Oct 31, 2017
    Posts:
    91
  3. dadude123

    dadude123

    Joined:
    Feb 26, 2014
    Posts:
    789
    ECS is awesome, but it's not something you can use for everything. It has its advantages and its disadvantages just like every other system.

    Entities (as in the ecs) are about data, its layout in memory, access patterns, ...
    A state machine is about code structure, how you execute different things based on context.

    The two concepts themselves don't really seem compatible.

    "to hammer everything looks like nail" think about it...

    I'd be surprised if you'd actually get more maintainable AND faster code by somehow forcefully pushing your FMS-shaped block into a ECS-shaped hole.
     
  4. Arakon

    Arakon

    Joined:
    Mar 8, 2014
    Posts:
    23
    Nyanpas likes this.
  5. Krajca

    Krajca

    Joined:
    May 6, 2014
    Posts:
    347
    @dongyiqi what if you implement each state as ECS component so you will have one system for each state that will do the job.
     
    PhilSA and optimise like this.
  6. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    My general, untested, initial idea would be:

    StateMachineComponent

    • has an int/enum representing the "FromState"
    • has an int/enum representing the "ToState"
    • has a float representing the time at which the last transition was started
    • has a float representing the time that the last transition should last
    StateMachineSystem
    • Advances the state simulation and updates the information in the StateMachineComponent
    As for how state behaviours would actually be defined; they would all be implemented directly into their own system class that executes after the StateMachineSystem. So we'd have a "MyCharacterAnimationSystem" for example that operates on StateMachineComponents and MyCharacterAnimationComponents. Based on the info contained in StateMachineComponent, MyCharacterAnimationSystem would do some kind of switch case on the From/ToState ids of the StateMachineComponent and determine what animation to actually use with that.

    If you want to trigger a transition, you simply get you MyCharacterAnimationSystem to modify the FromState, ToState, and transitionStartTime. The StateMachineSystem will automatically handle updating the transition on its next update
     
    Last edited: Apr 13, 2018
  7. bcondran

    bcondran

    Joined:
    Jul 2, 2012
    Posts:
    2
    The state of an entity would be described by the components (most likely of the tag-like variety) it has attached.
    The different systems would be responsible for changing the state of the entity.

    Naive example:

    EnemyEntiy with EnemyIdle, EnemyAttack and EnemySearch components

    EnemyIdleSystem handles logic for spotting Player. Removes Idle component and adds Attack component when player is in enemy's sights.

    EnemyAttackSystem handles logic for attacking Player. Removes Attack component and adds Search component if player leaves enemy's sights.

    EnemySearchSystem handles logic for enemy searching for player. Removes Search component and adds Attack component if player re-enters enemy sights, or if the player is not found after a specified period of time, adds the Idle component.

    Alternatively, as others have suggested, you could just have an EnemyStateComponent and EnemyStateSystem, which would probably be a better solution unless the system would end up being too complex
     
  8. UCh

    UCh

    Joined:
    Nov 18, 2012
    Posts:
    29
    I’m working on an implementation of this in a similar concept that Ash framework (and ECS framework from the Actionscript). I didn’t figure out the implementation detail but is this line:

    StateMachineComponent
    • State: actual state (enum,int,string...)
    • NextState: If different than State indicate pending transition
    • StateComponentsMap: A dictionary of stateId-array of component types
    StateExitSystem
    • Runs first
    • If NextState is different of State, remove all component from State from the entity
    Then you need to implement a system for each possible state that adds the components for that state and change the actual one.

    Example:

    State Idle (no components associated)
    State TargetEntity (need Target component)
    TransitionToTargetEntitySystem: Adds the target component with values selected by the UI stored somewhere, and sets actual state in the State machine component.

    That way your targeting system doesn’t mix with the FSM system.
     
  9. UCh

    UCh

    Joined:
    Nov 18, 2012
    Posts:
    29
    A bit more detail on the implementation I was talking about:

    Code (CSharp):
    1.  
    2. public struct MovementCommandSM : IComponentData
    3.     {
    4.         public MovementState State;
    5.         public MovementState NextState;
    6.         private Dictionary<MovementState, ComponentType[]> StateComponents;
    7.  
    8.         public MovementCommandSM(MovementState initialState,
    9.             Dictionary<MovementState, ComponentType[]> stateComponents) : this()
    10.         {
    11.             State = initialState;
    12.             NextState = initialState;
    13.             StateComponents = stateComponents;
    14.         }
    15.     }
    16.  
    17.     public enum MovementState
    18.     {
    19.         Idle,
    20.         MoveToPosition,
    21.         TargetEntity
    22.     }
    23.  
    24. var fsm = new MovementCommandSM(
    25.                 MovementState.Idle,
    26.                 new Dictionary<MovementState, ComponentType[]>
    27.                 {
    28.                     {
    29.                         MovementState.Idle, new ComponentType[0]
    30.                     },
    31.                     {
    32.                         MovementState.MoveToPosition, new ComponentType[]
    33.                         {
    34.                             ComponentType.Create<Destination>()
    35.                         }
    36.                     },
    37.                     {
    38.                         MovementState.TargetEntity, new ComponentType[]
    39.                         {
    40.                             ComponentType.Create<Destination>(),
    41.                             ComponentType.Create<Target>()
    42.                         }
    43.                     }
    44.                 }
    45.             );
    46.  
     
    ben-rasooli likes this.
  10. capyvara

    capyvara

    Joined:
    Mar 11, 2010
    Posts:
    80
  11. ldhongen1990

    ldhongen1990

    Joined:
    Dec 18, 2015
    Posts:
    61
    I briefly read the material referred to by Arakon (it's a book by a veteran game programmer), it's all theory and not much reference code but I think he is trying to say FSM done right in ECS is by being stateless in a way that we don't need to have variables holding the current state. Instead, we utilise ECS implicit "state" by adding and removing entity from system (what he meant by table, if I am not wrong). This way each system operating on different types of state just need to operate directly on their specific data, instead of always checking "if I am in this state blah blah blah, if I am in that state blah blah blah".
    Still learning though.
     
    recursive and starikcetin like this.
  12. ldhongen1990

    ldhongen1990

    Joined:
    Dec 18, 2015
    Posts:
    61
    To add on, as part of my investigation and research, I have further discovered that we can actually utilise the existing Reactive system to provide for an OnEnter and OnExit for individual state system. Perfect

     
  13. dongyiqi

    dongyiqi

    Joined:
    Nov 7, 2017
    Posts:
    11
    This seems to be a really good way to implement kind of FSM structure~ thank you~
     
  14. Reborn1214

    Reborn1214

    Joined:
    Jan 19, 2019
    Posts:
    15
    Hello,Do you have any examples?I have a piece of code for the video.But,I'm not sure how to switch status .I had to use switch to check status in ChangeJob.
     
  15. ScriptsEngineer

    ScriptsEngineer

    Joined:
    Jun 8, 2018
    Posts:
    37
  16. davenirline

    davenirline

    Joined:
    Jul 7, 2010
    Posts:
    945
    The problem with using tag components to differentiate between states is that in introduces the usage of sync points. Components that can be enabled/disabled will solve this but for now, sync points are needed.
     
  17. dongyiqi

    dongyiqi

    Joined:
    Nov 7, 2017
    Posts:
    11
    After two years~
    Finally comes the official explation of FSM in DOTS. Cheers~
    But the "more things" is funny:

    What if we wanted thousands of guards all looking for the player?
    • If there is a large area, do all the guards need to check if they can see the player? What if they are nowhere near the player? What kind of acceleration structure could you add to make this more efficient?
    It is the problem I'm working on.
    The implementation to change state by ComponentData flag in under the condition that just a few entities and change state not so often, what if there are thousands of them? Who knows~
     
    ScriptsEngineer likes this.
  18. WAYNGames

    WAYNGames

    Joined:
    Mar 16, 2019
    Posts:
    939
    The issue I have with the tag state component are :
    - tagging/detaging has a cost although it will be reduced by the enable/disable feature when it will come
    - single state check : in a FSM, you are in one state at a time but you could have several tag state component at once on the entity, especially if anyone can add them.

    That's why I would prefer an implementation with a single component per FSM and an enum for the states.
     
    ScriptsEngineer likes this.
  19. Lieene-Guo

    Lieene-Guo

    Joined:
    Aug 20, 2013
    Posts:
    547
    Agree with the structural change cost issue.
    Not able to have parallel states is a defect of FSM. That's where behaviors tree jumps out and beat FSM.
    What's so bad to have parallel states.
    It makes FSM work better imo.
     
    ScriptsEngineer likes this.
  20. WAYNGames

    WAYNGames

    Joined:
    Mar 16, 2019
    Posts:
    939
    FSM is a tool with it's set of rules and it's uses.
    Behavior tree is another.

    Still if you don't have too many state you could still use a single component to treat states in a bit mask manner.
     
    ScriptsEngineer likes this.
  21. SinisterMephisto

    SinisterMephisto

    Joined:
    Feb 18, 2011
    Posts:
    178
  22. WAYNGames

    WAYNGames

    Joined:
    Mar 16, 2019
    Posts:
    939
    Look into the branches of the repo you might find it
     
  23. Vacummus

    Vacummus

    Joined:
    Dec 18, 2013
    Posts:
    191
    SinisterMephisto likes this.
  24. WAYNGames

    WAYNGames

    Joined:
    Mar 16, 2019
    Posts:
    939
    SinisterMephisto and bb8_1 like this.
  25. SinisterMephisto

    SinisterMephisto

    Joined:
    Feb 18, 2011
    Posts:
    178