State Machine in ECS

Started by
2 comments, last by Theis_Bane 8 years, 3 months ago

Allllllllright, so. I'm programming a server/client MUD in C# using a form of the Entity/Component design paradigm. I've made a substantial amount of headway, even reaching the point where I can move about the world and interact with simple objects and other players with no problems or glitches. However, I ran into some issues when I started learning about and implementing a state machine for AI purposes. From what I can tell, the State Machine design in Programming Game AI by Example will not work for an Entity/Component system, at least, one like mine. My basic design is as follows:

All of my components are managed separately by their own respective managers. Each manager is instantiated in a Region object representing a specific area of the game. I do this so that I don't have to load large amounts of data into memory in order for players to interact with the world. Only regions with players are active. Others update the database and are disposed of. All components are in a dictionary keyed to the ID of the Entity that owns it.

My entities are basics classes containing their ID, their Region ID, a List of ComponentMap objects that describe the type of component and ID for each component it contains. (this hasn't really been of any use yet, but I'm loathe to remove it until I'm sure).

Most of my 'system' code lies in my Game class. This class handles the different regions, and has methods for logging in and out, character creation, updating client information, and so on. I basically just pass around the currently focused Region and Entity, and do work.

I'm having a hard time wrapping my head around state machines though, at least in this context. Since my entities don't hold any of their own data, a State Machine can't really do much. I've run into a wall. Any advice on State Machines in an ECS?

Advertisement

If you have your heart set on ECS, one option would be to model each state as a separate "component" and attach/detatch the entity from the states dynamically. Suppose for instance your entities could be in idle, walking, and attacking states. When an entity is idle, you attach an "idle" component to the entity. When an entity is walking, you attach a "walking" component to the entity. This not only should fit into your existing architecture with minimal effort, but also help you prevent other entity components from getting bloated since you can keep data that's only needed for a particular state in the component for that state. If your character isn't attacking, do you really need to track what weapon or spell they're using? If your character is idling, do you need to track how far they'll move this frame?

"If you saw that there weren't any apples in stock, would you still haggle over their price?"

Of course, you could make this simpler by simply representing your "states" as simple sets of entities that are in that state and a mapping onto the data, even if you weren't using ECS for the rest of the architecture. In effect, entity state becomes represented by set membership rather than an enum flag or some such, meaning that when you update entities in a particular state, you don't have to loop through all the entities looking for entities that are in that particular state. This is called "existence based processing." This also means that you have a centralized place where you can verify which entities are in which states, without having to track down individual entities and look at their state flags, so debugging certain kinds of bugs becomes easier.

Can't you just make the state machine a generic AI component?

The general idea for the AI FSM is to have a current state which has an 'update' that can run to persist in that state. Then you have transitions between states that handle cleaning up the current state and entering the new one. There are a few different approaches, but the general idea is the same.

For example, if you were making an FPS and you had a guard you could have the states 'patrol', 'chase', and 'fight'. The patrol state moves toward the next node along some preset path, the chase state moves toward the player, and the fight state takes aim and shoots at the player. For each state you can have three functions: enter, update, and leave. Alternatively you could use a class for each state where the constructor and destructor handle the enter and leave parts.

Within the update function for each state you check against some conditions and decide if you want to transition to a new state. For example, in the patrol state, if you see the player you want to transition into the chase state, so you either call a patrolToChaseTransition() function of the FSM, or else just replace the state object with a new 'chase' object. Within the chase object you transition to 'fight' if you're in weapon range, or transition to patrol if you lose sight of the player.

Since the FSM is acting on the entity it will obviously need access to the entity ID or whatever you're using to manipulate the entity.

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.
Just wanted to reply to close out this thread.

The solution I went with is sort of a combination of your answers, with a few tweaks. Since access to each separate world and all my data was contained within the bulk of my GameManager class, I was concerned about having access to everything from inside my individual state objects without storing excess pointers to my GameManager in every state.

Turns out I can just pass a pointer to each Enter, Execute, and Exit state for temporary use...

I built a StateComp that controls an entity's current state and its transitions. My update loop just iterates through each StateComp and calls the current and global state's Execute method. It works awesome. Thank you both for your advice!

This topic is closed to new replies.

Advertisement