State machines seem to cause harmful dependencies in component-based architectures.

How, specifically, is communication handled between a state machine and the components that carry out state-related behavior?

Where I'm at:

I'm new to component-based architectures.

I'm making a fighting game, although I don't think that should matter. I envision my state machine being used to toggle states like "crouching", "dashing", "blocking", etc.

I've found this state-management technique to be the most natural system for a component-based architecture, but it conflicts with techniques I've read about:
Dynamic Game Object Component System for Mutable Behavior Characters It suggests that all components activate/deactivate themselves by continually checking a condition for activation.

I've found this useful, but ambiguous: How to implement behavior in a component-based game architecture? It suggests having a separate component that contains nothing but a state machine. But, this necessitates some kind of coupling between the state machine component and nearly all the other components. I don't understand how this coupling should be handled. These are some guesses:

A.Components depend on state machine:
Components receive reference to state machine component's getState(), which returns an enumeration constant. Components update themselves regularly and check this as needed.

B.State machine depends on components:
The state machine component receives references to all the components it's monitoring. It queries their getState() methods to see where they're at.

C.Some abstraction between them
Use an event hub? Command pattern?

D.Separate state objects that reference components
State Pattern is used. Separate state objects are created, which activate/deactivate a set of components. State machine switches between state objects.

I'm looking at components as implementations of aspects. They do everything that's needed internally to make that aspect happen. It seems like components should function on their own, without relying on other components. I know some dependencies are necessary, but state machines seem to want to control all of my components.

The gist of the technique is to combine the action list pattern (explained -- somewhat poorly -- in the slides) with behavioral state machines that act upon a component-based game entity.

It is in essence the same as creating a special composition system just for AI behavior, geared towards the kinds of inter-behavioral integration you need for simpler AI systems.

My favorite part of that particular game was how we could create entirely new types of enemies by simply selecting from a list of pre-written behaviors, put them into the action list for the game object (residing in a BrainComponent) in the order of desired priority, and everything just worked. With an action list that allows for Blocking/NonBlocking actions, this can do some really cool things despite how simple it all is to implement.

Even behaviors like "stun" where really just a StunBehaviorAction pushed onto the top of action list stack; if the stun behavior become activated (after observing that the game object's EarsComponent heard a stunning shockwave attack) then it set its internal state to Stunned, told the AnimationComponent to play the stun animation, and set its action state to Blocking, and its timer to a stun timeout pulled from the game object's EnemyParametersComponent. Since it was Blocking and at the top of the action list, none of the other BehaviorAction's in the action list would get their update method called, so they would essentially be switched off. When the timeout expired, the StunBehaviorAction set its state back to Idle and its action state to NonBlocking.

The other behaviors we implemented were almost all implemented with a single internal state machine. The only two that didn't have state machines, in fact, were the PatrolPathBehaviorAction (it would push a series of PathAction's onto the action list if it was idle, which in turn pushed MoveAction's) and the GuardHomeBehaviorAction (always at the bottom of the action list, and would always just push a PathAction back to the enemy's home location). Every other behavior was a state machine.

@Pup: From a code perspective, as I built it, a Behavior is an Action. From a conceptual point, Actions are usually transitory -- they exist only until "complete" -- while Behaviors are forever and never get removed from the list. I've seen another team build a similar system but with two lists, one for Actions and one for Behaviors, which works well enough. I like having the ability for an Action to block certain behaviors, though, using the bitmasks and grouping (lanes, I believe I called them in the slides). Sorry that middle slide's graphic is so bad. :)
–
Sean MiddleditchApr 10 '12 at 2:58

At a previous company I worked for we had a component-based system with state-based AI. We had an AI component that controlled all behavior for that object/unit. When the AI was active, like wandering, attacking, etc, it would receive and update each frame to do any logic that was needed. When the AI was idling or not moving the component was deactivated and not updated each frame. The component, while deactivated, could still receive event-based messages, so it would receive a message for something like a player entering its aggro radius, and could respond to that by reactivating the AI component so that it could do frame-based updates.

The AI component has sub-components, that it could create and destroy on the fly, based on what time of actions it was performing. For example if it was wandering it could create a wandering sub-component and update that each frame while wandering, and then if aggro'd while wandering it would close that sub-component and open an attack sub-component. The AI component should be independent of all other components on an object. For example, we had an input component, that would simply query for movement values on a unit. The query it made was something that both human and AI objects would respond to. This allowed the AI component to simply set movement values on itself during things like wandering, that the input component could pick up on, just as a human controllable component would set values that the input component could pick up on. Things like this allow the AI component to be independent of any other component on the object.

So, the AI sub-components are actually doing the work? Did they exist as entity components, at the same level as the AI component?
–
PupFeb 27 '12 at 18:23

Sub-components in our engine were part of the base component class. So any Component, which derived from BaseComponent, could have any number of SubComponents on it. The Update() method in BaseComponent would check the list of sub-components, and call Update() on those. Subcomponents were entirely optional so the BaseComponent might not have any. Also, any messages that went to a component were routed to the subcomponents as well.
–
Nic FosterFeb 27 '12 at 20:50

It's a little unclear what you mean by components as your terms are very vague with no concrete examples. Often game entities are built using composition rather than inheritence. Thus you could make them into something that can take damage by adding a health component to the entity or you could make them animate by adding an animate component. One could also put the AI in such a component. There will be decision making logic in your AI component and if you're concerned about coupling that to much of the other code in the system you could collect the information into a blackboard which the AI logic is only allowed access to. There is also the issue of dependencies on the output of AI system. Basically your AI is controlling an entity and that control needs an interface. One useful concept is that of a controller or gamepad. Your AI can fill out a similar structure that a player gamepad would fill out (though it might have some extra "buttons" for specific abilities). Now this structure could then be passed to your animate component which would interpret it and select the appropriate animations to play. Different AI sub-components could even be writing to different fields of the structure or to the same fields with different priorities. Aiming and walking for example.