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 ?
If you look at the comments of this blog post : Designing Bomberman with an Entity System: Which Components? – T-machine.org you will see that some people consider that FSM are not a good fit with ECS. However, they also talk about a possible way to implement it if I remember right. Have a look ![]()
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.
ECS would be a pretty different approach.
This may be of some help:
@dongyiqi what if you implement each state as ECS component so you will have one system for each state that will do the job.
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
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
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.
A bit more detail on the implementation I was talking about:
public struct MovementCommandSM : IComponentData
{
public MovementState State;
public MovementState NextState;
private Dictionary<MovementState, ComponentType[]> StateComponents;
public MovementCommandSM(MovementState initialState,
Dictionary<MovementState, ComponentType[]> stateComponents) : this()
{
State = initialState;
NextState = initialState;
StateComponents = stateComponents;
}
}
public enum MovementState
{
Idle,
MoveToPosition,
TargetEntity
}
var fsm = new MovementCommandSM(
MovementState.Idle,
new Dictionary<MovementState, ComponentType[]>
{
{
MovementState.Idle, new ComponentType[0]
},
{
MovementState.MoveToPosition, new ComponentType[]
{
ComponentType.Create<Destination>()
}
},
{
MovementState.TargetEntity, new ComponentType[]
{
ComponentType.Create<Destination>(),
ComponentType.Create<Target>()
}
}
}
);
Anyone has thoughts about ValueTask ? all the async/await stuff in C# is basically a syntax sugar for a state machine.
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.
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
This seems to be a really good way to implement kind of FSM structure~ thank you~
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.
Can that be an answer?
https://github.com/Unity-Technologies/EntityComponentSystemSamples/tree/master/ECSSamples/Assets/Use%20Case%20Samples/1.%20State%20Machine%20AI
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.
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~
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.
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.
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.