State machine logic questions

94 views
Skip to first unread message

Derrek Leute

unread,
Sep 18, 2015, 11:16:55 PM9/18/15
to Ash Framework

I really like the idea of using a state machine to swap the components on an entity to manage state. But I've run into a series of issues below:

In the asteroid's example, the values handed to components created inside a state are essentially simple defaults. In my case, these values would have to be generated. To generate these values, I need access to other components (for example, I need a position created based no the viewable scene). I don't do it now, but I could see a component needing info from another entity (teleport to nearest friendly unit) Should I do this in the state machine? If I don't, where should I do them? If I do it in the state machine isn't it indirectly coupling the component to other components? Isn't this the role of the system?

Another issue is I sometimes have a "short term transition". Meaning I need something to happen once and then be removed or not updated. For example, triggering an animation and then I can remove that component. This can't wait for a "state change" because the overall state of the entity may not change again immediately. Basically, I need to kick something off and then not execute it again. I have come up with a few options:

1. Add a component dedicated to doing this job and use a "needsUpdate" boolean on that component to inform the processing system that it should take action. And then when it does, set this to false so it ignores it in subsequent loops until something changes it to true again.
2. have the system remove the component for this kind of action when it's done doing it's update for that loop. Removes the need for needsUpdate as the component will be executed and removed on the next loop of the associated system. Systems like this are just basic helpers to do one off tasks shared by a lot of different entities. (Like fade out before the entity is removed)

The above is really about "transitions" between states. Example, during game play the user might collect a shield power up for their ship. When this is added to the entity as a component, an animation "raising the shield" should be triggered. This is a set it and forget it type operation. Should I just create a "RaiseShield" component (Probably added by the ShieldComponent state machine in it's initial "Activating" state) that is added to the entity and processed like above? This might come with a one-off sound and also trigger adding a visual node to the entity to represent the shield (again, a one-off fade-in animation type of thing). After this is done, the shield should be switched to activated.

In other state machines it has an update method and would probably track time. I could do that here by storing elapsed time in the component, but this also seems like the wrong strategy.

These solutions seem a bit outside of scope for systems to me. Am I way off track? Or are these things logical and won't hurt me down the road?

I'm a competent OO programmer but this is my first try at ECS/FSM. I don't even have a game background. So I'm really new to this type of development.

Thanks!

--Derrek

Damion Murray

unread,
Sep 20, 2015, 11:03:58 AM9/20/15
to Ash Framework
Figured I'd take a crack at answering some of your questions:


In the asteroid's example, the values handed to components created inside a state are essentially simple defaults. In my case, these values would have to be generated. To generate these values, I need access to other components (for example, I need a position created based no the viewable scene). I don't do it now, but I could see a component needing info from another entity (teleport to nearest friendly unit) Should I do this in the state machine? If I don't, where should I do them? If I do it in the state machine isn't it indirectly coupling the component to other components? Isn't this the role of the system?

I could be wrong but from what I understand, Finite State Machines (FSM) require the content of their states to be fixed (hence the term finite state). In terms of Ash's FSM framework, I take that to mean the properties/values inside a state's components are not meant to change once created and assigned (to said state). The examples you've given (position based on a viewable scene and teleporting to nearest friendly unit) seem to describe situations where the properties of a state's components will have to change. That seems to make them poor candidates for a FSM. 
 
Another issue is I sometimes have a "short term transition". Meaning I need something to happen once and then be removed or not updated. For example, triggering an animation and then I can remove that component. This can't wait for a "state change" because the overall state of the entity may not change again immediately. Basically, I need to kick something off and then not execute it again. I have come up with a few options:

1. Add a component dedicated to doing this job and use a "needsUpdate" boolean on that component to inform the processing system that it should take action. And then when it does, set this to false so it ignores it in subsequent loops until something changes it to true again.
2. have the system remove the component for this kind of action when it's done doing it's update for that loop. Removes the need for needsUpdate as the component will be executed and removed on the next loop of the associated system. Systems like this are just basic helpers to do one off tasks shared by a lot of different entities. (Like fade out before the entity is removed)

The above is really about "transitions" between states. Example, during game play the user might collect a shield power up for their ship. When this is added to the entity as a component, an animation "raising the shield" should be triggered. This is a set it and forget it type operation. Should I just create a "RaiseShield" component (Probably added by the ShieldComponent state machine in it's initial "Activating" state) that is added to the entity and processed like above? This might come with a one-off sound and also trigger adding a visual node to the entity to represent the shield (again, a one-off fade-in animation type of thing). After this is done, the shield should be switched to activated.

Transitions between states can be difficult to pull off but there are a couple ways to tackle it. 

One approach using Finite State Machines is to model what happens before entering a state (transition in) and modeling what happens after leaving/exiting said state (transition out). In a project I did a while back that meant tweening component properties to achieve various effects. The solution I came up with isn't exactly trivial but it worked for my use case. Ash has this notion of Nodes which are distinct groupings of components matched to a specific system type. You can track when a node is added or removed from a System via "nodeAdded" and "nodeRemoved" signals(analogous to events) triggered by the Engine. While I doubt it was the architect's intention, these work well enough for one-off operations. I set up "enter" or "exit" transitions by adding and removing "Transition" components that houses tween information. Then a TransitionSystem triggers FSM state changes upon a tween's completion. It may or may not be of use to you but feel free to check out my implementation here).

You could also try to reframing some aspects of the problem. Take the shield example, instead of making it a component of the ship entity consider making it an entity onto itself. When the ship and a shield power-up collide, the system responsible for handling collisions, would spawn a shield entity with its own series of FSM states independent of the ship (shieldActivateState, shieldTakeDamageState, shieldDeactivateState etc.). Upon creation, the shield entity could be composed of component instances from the ship entity (essentially having the ship and shield entities share state; position for example) but redefine others (shield has a hitbox that is distinct from the ship so it would need unique components to define it).The ship entity's FSM would still have a "shielded" state, but the work of managing that state is offloaded to the shield entity.  

Derrek Leute

unread,
Sep 20, 2015, 12:13:00 PM9/20/15
to Ash Framework

Replies are inline.


On Sunday, September 20, 2015 at 11:03:58 AM UTC-4, Damion Murray wrote:
Figured I'd take a crack at answering some of your questions:

In the asteroid's example, the values handed to components created inside a state are essentially simple defaults. In my case, these values would have to be generated. To generate these values, I need access to other components (for example, I need a position created based no the viewable scene). I don't do it now, but I could see a component needing info from another entity (teleport to nearest friendly unit) Should I do this in the state machine? If I don't, where should I do them? If I do it in the state machine isn't it indirectly coupling the component to other components? Isn't this the role of the system?

I could be wrong but from what I understand, Finite State Machines (FSM) require the content of their states to be fixed (hence the term finite state). In terms of Ash's FSM framework, I take that to mean the properties/values inside a state's components are not meant to change once created and assigned (to said state). The examples you've given (position based on a viewable scene and teleporting to nearest friendly unit) seem to describe situations where the properties of a state's components will have to change. That seems to make them poor candidates for a FSM.

That is an interesting theory. What you are saying is that finite state machines not only have the components be equal but the data in every component should be equal. But, let me create a very simple case. Let's say the asteroid ship didn't want to re-spawn at exactly the same location. This is a change on the data in a component, not the component combination. This seems to be the point of data driven design to me. So the playing state, when you switch to it, might adjust the position of the new ship to one of several re-spawn locations. Or, you might even want to re-spawn where you died, not at the original location. Saying all data in an FSM system must be identical means you shouldn't adjust the position of the ship either. Meaning once you are in the "playing" state and the player moves the ship, technically, it's no longer in the beginning "playing" state. So, really, it should transition to a less restrictive state of "playerControlled". That is obviously "extreme". And I don't think that's what you intend with this response.

The way I view FSM is there are a certain collection of things that it does control, but not *everything*. This is something I haven't gotten onboard with Ash's framework yet (I'm attempting to transition, but it may not work for me). An FSM in Ash seems to control the entire state on an entity. In composition, the entity could have other components that have nothing to do with this particular FSM. This means an entity can have one and only one StateMachine to control it (in ash). This is unfortunate if you have a series of complex logic most easily represented by multiple state machines. (I may be interpreting Ash's architecture wrong). You suggest a solution to this as well below which I will get to.

But, I still don't understand how I would control the re-spawn location. I mean, I can do it from the state machine. I just am not convinced that I *should* do that. This may be as simple as creating a new entity with a new spawn position. This would be handled in a Death/Life system rather than the FSM. Meaning, the state machine for this wouldn't touch position at all. It would just use the position indirectly from the component responsible for that information. I'm just not convinced that would *always* be an option. This is essentially why you can use an existing instance in state machines. Since you want component to pass through multiple states. Just sometimes, I want the component to continue to exist with different data.
 
 
Another issue is I sometimes have a "short term transition". Meaning I need something to happen once and then be removed or not updated. For example, triggering an animation and then I can remove that component. This can't wait for a "state change" because the overall state of the entity may not change again immediately. Basically, I need to kick something off and then not execute it again. I have come up with a few options:

1. Add a component dedicated to doing this job and use a "needsUpdate" boolean on that component to inform the processing system that it should take action. And then when it does, set this to false so it ignores it in subsequent loops until something changes it to true again.
2. have the system remove the component for this kind of action when it's done doing it's update for that loop. Removes the need for needsUpdate as the component will be executed and removed on the next loop of the associated system. Systems like this are just basic helpers to do one off tasks shared by a lot of different entities. (Like fade out before the entity is removed)

The above is really about "transitions" between states. Example, during game play the user might collect a shield power up for their ship. When this is added to the entity as a component, an animation "raising the shield" should be triggered. This is a set it and forget it type operation. Should I just create a "RaiseShield" component (Probably added by the ShieldComponent state machine in it's initial "Activating" state) that is added to the entity and processed like above? This might come with a one-off sound and also trigger adding a visual node to the entity to represent the shield (again, a one-off fade-in animation type of thing). After this is done, the shield should be switched to activated.

Transitions between states can be difficult to pull off but there are a couple ways to tackle it. 

One approach using Finite State Machines is to model what happens before entering a state (transition in) and modeling what happens after leaving/exiting said state (transition out). In a project I did a while back that meant tweening component properties to achieve various effects. The solution I came up with isn't exactly trivial but it worked for my use case. Ash has this notion of Nodes which are distinct groupings of components matched to a specific system type. You can track when a node is added or removed from a System via "nodeAdded" and "nodeRemoved" signals(analogous to events) triggered by the Engine. While I doubt it was the architect's intention, these work well enough for one-off operations. I set up "enter" or "exit" transitions by adding and removing "Transition" components that houses tween information. Then a TransitionSystem triggers FSM state changes upon a tween's completion. It may or may not be of use to you but feel free to check out my implementation here).

I think what I was building in my head was a less generic version of what you have created here (I only briefly skimmed the code to get a very basic understanding). For me, it's just trying to encapsulate simple display interactions that are generic and shared among many entities and triggered by many systems (or state machines). My only concern is they are so common that two distinct systems could want to do the same thing on the same entity. Two different systems might ask to fade out an entity simultaneously. Not sure how these would deal with each other. (Again, going back to your point about "Finite" in state machines)

You could also try to reframing some aspects of the problem. Take the shield example, instead of making it a component of the ship entity consider making it an entity onto itself. When the ship and a shield power-up collide, the system responsible for handling collisions, would spawn a shield entity with its own series of FSM states independent of the ship (shieldActivateState, shieldTakeDamageState, shieldDeactivateState etc.). Upon creation, the shield entity could be composed of component instances from the ship entity (essentially having the ship and shield entities share state; position for example) but redefine others (shield has a hitbox that is distinct from the ship so it would need unique components to define it).The ship entity's FSM would still have a "shielded" state, but the work of managing that state is offloaded to the shield entity.  

This is a pattern I'm attempting to work with already. My "example" is something like an auto-targeting & firing weapon power up. This might be a gun that is mounted on your ship and can circle it pointing in any direction. It will then fire and target on the nearest enemy. Etc. I didn't consider a "shield" worthy of being an additional entity. As I stated above, this may solve the problem of complex component composition on the ship. I already planned a "parentConstraint" component that would essentially link the sub entity to the parent entity. (In fact, visually, this will always be the case since all visual components exist in a hierarchy. Any entities visual representation will always have a parent visual representation)

The above is certainly something I will be doing. But this also hurts the idea of composition. In an ideal world, I should be able to attach these components to any entity. Having a sub entity as an architectural requirement is basically saying "No, we can't use entity composition to do this". I may *want* to have certain things be sub-entities, especially if the visual representation needs to have independent behavior. Example, what if I could "throw my shield" around a friend's ship or other object to protect it for a period of time. Or even use it as a "blunt weapon" to move physics objects out of the way... etc.

To me, I should have 2 ways to implement that, I should be able to attach a shield component (and other related components with possibly a second FSM to control the shield) to an existing entity OR, I should be able to do it as another entity tied to the ship. If I give that capability up, it seems like I'm losing some of the benefit of composition in the architecture. This may be necessary/worthwhile to achieve a specific goal, but I would rather not throw that out the door this early in my process.

These are excellent responses! And I am actually using them to build a customized "Ash like" ECS. Having a full understanding of the results of my architecture decisions is critical to get this right.

Thanks so much for helping me think through these issues!

--Derrek
 

Damion Murray

unread,
Sep 20, 2015, 4:23:59 PM9/20/15
to Ash Framework
More food for thought.


That is an interesting theory. What you are saying is that finite state machines not only have the components be equal but the data in every component should be equal. But, let me create a very simple case. Let's say the asteroid ship didn't want to re-spawn at exactly the same location. This is a change on the data in a component, not the component combination. This seems to be the point of data driven design to me. So the playing state, when you switch to it, might adjust the position of the new ship to one of several re-spawn locations. Or, you might even want to re-spawn where you died, not at the original location. Saying all data in an FSM system must be identical means you shouldn't adjust the position of the ship either. Meaning once you are in the "playing" state and the player moves the ship, technically, it's no longer in the beginning "playing" state. So, really, it should transition to a less restrictive state of "playerControlled". That is obviously "extreme". And I don't think that's what you intend with this response.

To clarify, my claim is that the data in a component be fixed/constant per FSM state. So two FSM states can have the same type of component but have different instances with fixed but unique values.

The point I was trying to make is that some components, given this feature of Ash's FSMs, are ill-suited to be used as members of an FSM state (depending on context). Case in point, the position component. There is nothing to stop you from adding a number of FSM states with position components, but in the context of an asteroids game where the spaceship can have any number of locations, its impractical. If you wanted the ship to re-spawn in a different location, instead of messing with a FSM, it would be simpler to create a position component where the x and y coordinates are randomized (taking into consideration the dimensions of the playing field) whenever you create a new space ship entity (I took a few liberties with the asteroids code to illustrate, see line 85 in snippet below).

In your example you site the position component as a member of the 'playing' state. If you look at the code in the asteroid snippet below you'll notice that the position component is never added to the FSM of the space ship entity. 

63 public function createSpaceship():Entity {
64     var spaceship : Entity = new Entity();
65     var fsm : EntityStateMachine = new EntityStateMachine( spaceship );
66      
67
68     fsm.createState( "playing" )
69         .add( Motion ).withInstance( new Motion( 0, 0, 0, 15 ) )
70         .add( MotionControls ).withInstance( new MotionControls( Keyboard.LEFT, Keyboard.RIGHT, Keyboard.UP, 100, 3 ) )
71         .add( Gun ).withInstance( new Gun( 8, 0, 0.3, 2 ) )
72         .add( GunControls ).withInstance( new GunControls( Keyboard.SPACE ) )
73         .add( Collision ).withInstance( new Collision( 9 ) )
74         .add( Display ).withInstance( new Display( new SpaceshipView() ) );
75
76
77     var deathView : SpaceshipDeathView = new SpaceshipDeathView();
78        
79     fsm.createState( "destroyed" )
80         .add( DeathThroes ).withInstance( new DeathThroes( 5 ) )
81         .add( Display ).withInstance( new Display( deathView ) )
82         .add( Animation ).withInstance( new Animation( deathView ) );
83
84
85     spaceship.add( new Spaceship( fsm ) ).add( new Position( 10 + Math.random(620), 10 + Math.random(460), 0 ) );
86    
87     fsm.changeState( "playing" );
88    
89     engine.addEntity(spaceship);
90
91
92     return spaceship;
93 }

I know you're aware of this as you acknowledge that "in composition [an] entity could have other components that have nothing to do with this particular FSM". And that, I think, is the point. The FSM is not meant to be the meat and gravy of Ash's architecture. I think it lead you to the assumption that FSMs are somehow integral to data-driven design. They’re not. The FSM is a pattern that's been used a lot in game development (pre-dating the whole ECS craze) and the author of Ash saw some utility in adding it to his framework. Be careful not to make it crutch.

Derrek Leute

unread,
Sep 20, 2015, 7:59:29 PM9/20/15
to Ash Framework

I think I haven't conveyed my entire issue sufficiently yet. In this example with the spaceship, the way to create a spaceship does work. (Actually, I'm not sure it would work in ash? I thought I read that FSM's automatically remove components that are no longer in that state from the entity. I may have misinterpreted that. That seemed rather strange to me).

I wouldn't use an FSM to represent arbitrary spaces. But the FSM may need information based on things in other components. Example: How fast a weapon recharges might be relative to how close you are to the nearest "base". If I had a "charging" state, I would want to use this information from the outside world to determine how fast to charge. "Charging" would stop your motion. And then fill a power meter at the rate specified by outside info.

My question is, how do I get that outside info? I imagine I put a charging component on the ship with no real information. And then do per-frame processing of that in a "Charging" system. In that way, I don't actually need any info when switching to a charging state on the ship?

Anyway, sorry if my problems aren't clear. I'm still struggling with basic architecture.

--Derrek

Richard

unread,
Sep 21, 2015, 3:56:26 AM9/21/15
to Ash Framework
Hi Derrek

> The way I view FSM is there are a certain collection of things that it does control, but not *everything*. This is something I haven't gotten onboard with Ash's framework yet 
> (I'm attempting to transition, but it may not work for me). An FSM in Ash seems to control the entire state on an entity. In composition, the entity could have other components 
> that have nothing to do with this particular FSM. This means an entity can have one and only one StateMachine to control it (in ash). This is unfortunate if you have a series 
> of complex logic most easily represented by multiple state machines. (I may be interpreting Ash's architecture wrong).

Yes, you misinterpreted that. In Ash, a state is a set of components that should be added when that state is entered and removed when that state is exited unless required by the next state. The state only cares about these components. It will not touch components that are not part of its state. So a state machine handles a subset of the components on an entity. When you exit a state it removes all the components that are part of that state's definition in the state machine, and then adds all the components that are part of the next state (with the caveat that if a specific component is in both states it will not be removed and added again but will simply remain). You can have any number of state machines operating in parallel on one entity. Each will handle the components they care about and will ignore all the others.

Were I creating Ash again I would not include the state machines in the core library. There are three reasons for this -
1. They are heavily engineered compared to the rest of the engine
2. They are not core functionality
3. They do not cover all needs for state management

The reason they are in Ash is because in the past many developers asked me how to do a state machine in Ash. These developers were under the impression that state machines are not possible in ECS architectures like Ash, when the reality is the whole engine, and every entity, and every component is a state machine at different levels of detail.
- You change the state of an engine by changing the entities and systems in it
- You change the state of an entity by changing the components in it
- You change the state of a component by changing the data in its properties

Whether the code I wrote is the best way to handle states is debatable. In my own games I sometimes use the built-in FSM classes to handle states and sometimes I write custom code for it. Every time you add or remove a component you are changing the state of an entity.

When using the built-in FSM, sometimes you need to do additional work when transitioning to a new state. You can so this in various ways including
- Write some additional code that you run immediately after changing the state (i.e. the next line of code after the call to changeState)
- Create transition states that track perform the transition requirements and then change to the final state when the transition is complete
- Use the nodeAdded signal in a NodeList or the componentAdded signal in an Entity to reset the data in a component
- In a system, detect the initial state of a component and treat it differently, performing "first frame" logic on the entity

Also note that Damion is wrong about the term "finite". In a "finite state machine" the term "finite" refers to the fact that there are a finite number of states. There is no suggestion that the data within a state is immutable.

On a couple of other matters you raise

> In other state machines it has an update method and would probably track time. I could do that here by storing elapsed time in the component, but this also seems like the wrong strategy.

If you need to track elapsed time, storing it in a property of a component would be exactly the right strategy. The only question should be which component.

>> Take the shield example, instead of making it a component of the ship entity consider making it an entity onto itself.
> The above is certainly something I will be doing. But this also hurts the idea of composition. In an ideal world, I should be able to attach these components to any entity.

You can attach any component to any entity. But there is no guarantee that that will make sense within the context of your game and how you have architected its needs. At any time, to get the effect you want you will have to add one or more specific components to specific entities. If you design your components such that, to get the effect you want, a shield component should be added to an entity separate to the ship that is simply how that component is designed to be used.

I hope this clarifies some issues and doesn't just confuse things further.

Richard

Derrek Leute

unread,
Sep 21, 2015, 10:51:54 AM9/21/15
to Ash Framework

This is very helpful. I have had a pretty solid grasp on the theory. I have just had a rough time applying it in code. I think you helped outline the ways to do that. So my responses are inline below.


On Monday, September 21, 2015 at 3:56:26 AM UTC-4, Richard wrote:
Yes, you misinterpreted that. In Ash, a state is a set of components that should be added when that state is entered and removed when that state is exited unless required by the next state. The state only cares about these components. It will not touch components that are not part of its state. So a state machine handles a subset of the components on an entity. When you exit a state it removes all the components that are part of that state's definition in the state machine, and then adds all the components that are part of the next state (with the caveat that if a specific component is in both states it will not be removed and added again but will simply remain). You can have any number of state machines operating in parallel on one entity. Each will handle the components they care about and will ignore all the others.

Were I creating Ash again I would not include the state machines in the core library. There are three reasons for this -
1. They are heavily engineered compared to the rest of the engine
2. They are not core functionality
3. They do not cover all needs for state management

This makes sense. However, they are over engineered because they do so much for you "auto-magically". This was not immediately understood. I submit that an FSM should not automatically add/remove components if you didn't deal with them in a given state. Just because one state has an interest in a component, doesn't mean all other states of that FSM will. Example: Playing state. I just want a default of shields up. That doesn't mean I want to remove the Shield Component in any other state. However, in Ash, it seems like I would at least have to reference it as it moves through the state machine whether or not that state even cares if the shield component exists. You might say that the shields up is a transition that should be acted on immediately after the enter state of playing. This would be another way to do it. But to me, both strategies should be valid. 

FSM is an organizational strategy. This is particularly true in ECS where the entire system *is* state. They exist to help you isolate complex pieces of code and keep the overall system easier to understand. So when you say they are over engineered, I entirely agree. All we really needed was the concept of how to use them and much simpler base classes to do so. You have done a spectacular job showing their value in an ECS. But to fix the over-engineering I would suggest 2 things:

1. Don't automatically remove unused components in a state machine (or touch them in any way not specified by a state)
2. Don't automatically add new components to systems on an entity

These 2 things are a large difference from other ECS/FSM systems. And I believe they are likely both adding to the over engineered nature of your FSM. To illustrate this I have been struggling with temporary ways to eliminate a component behavior:

1. I could create a disabled property that is checked on each frame by the system.
2. I could remove the component from the entity and re-create it later
3. I could remove the component from the system (leave it on the entity)
4. I could create a disabled components on the entity and have the system check that or also remove from the system

Example 3 above doesn't seem to be doable with Ash at least while using an FSM. Real example: Player is stunned and input is temporarily ignored. I can build that into the component logic or I could just remove the component temporarily. Note, I don't want to lose the current component state. Just pull it from the system processing for a bit.

I would suggest that doing those things could be convenience calls on the FSM. Meaning it's tightly integrated into the underlying systems making it easy to interact with systems to do what you are accomplishing automatically. I think the way I view FSM in an ECS is a Finite State Change Machine. It's sole job is to manipulate and create components representing state. That's it. 

The reason they are in Ash is because in the past many developers asked me how to do a state machine in Ash. These developers were under the impression that state machines are not possible in ECS architectures like Ash, when the reality is the whole engine, and every entity, and every component is a state machine at different levels of detail.
- You change the state of an engine by changing the entities and systems in it
- You change the state of an entity by changing the components in it
- You change the state of a component by changing the data in its properties

And this right here is the genius behind it. All I'm trying to do for myself is make sure I don't create unnecessary dependencies between components via their state machines. I still find it odd that a state machine which interacts with so many components (by creating them and removing them) is stored as a property of one of those components. This seems wrong given that systems work with components, why is an object inside a component being given this much power? More on this below.
 
When using the built-in FSM, sometimes you need to do additional work when transitioning to a new state. You can so this in various ways including
- Write some additional code that you run immediately after changing the state (i.e. the next line of code after the call to changeState)
- Create transition states that track perform the transition requirements and then change to the final state when the transition is complete
- Use the nodeAdded signal in a NodeList or the componentAdded signal in an Entity to reset the data in a component
- In a system, detect the initial state of a component and treat it differently, performing "first frame" logic on the entity

Also transition components. Which you use depends on if it's something that needs to be synchronously or can be done asynchronously (fire and forget).

You can attach any component to any entity. But there is no guarantee that that will make sense within the context of your game and how you have architected its needs. At any time, to get the effect you want you will have to add one or more specific components to specific entities. If you design your components such that, to get the effect you want, a shield component should be added to an entity separate to the ship that is simply how that component is designed to be used.

Yeah, I see that as a point. It just makes building an editor a little harder. Knowing when a component can be adding to any existing entity or requires a new entity dedicated to only that component (meaning what can I now add to the shield entity along with the shield?). But I see your point.

On FSM logic. As I thought through these things I realized I'm not sure the component is the place to store the state machine. It is not itself state. In fact, in an ECS, you can make the argument that nothing in it will be related to storing state only manipulating it. So a fundamental change is what if state machines are stored by the System? The key difference is you would hand in the entity and the intended state:

Example:

system.shipStateMachine.enterState(entity,"playing")

Since I've argued that states should not track component changes "auto-magically" they are literally just code organization structures. The difference is subtle, but now it no longer feels like an object owned by a component is controlling other components. It becomes an extension of the System, which in my head seems like a much better architectural fit. This is one response to the argument that the system update method should be doing everything. It can still be done there, just organized into state machines.

What was bothering me is a System will normally act on a small subset of components. And that should be the case. Ideally, the smaller the subset the better to maintain modular nature of components. (Many component systems only allow you to register one component as the main component it manages. Everything else you do is up to the update code). But it is reasonable that systems should be able to communicate with each other. So when systems do need to add a specific component, it is reasonable to off-load that to the system that should control the component. Meaning, in the playing FSM state, all you care is that the appropriate weapons exist and are ready to use. You don't actually *care* what they are.
 
Since this is logically moved into "System" territory. It makes some sense to do something like this in the Ship System:

systemManager.getSystem("Weapons").addWeaponsToEntity(entity)

In addition, if the state is ready to have these weapons processed by the weapons system it would do:

systemManager.getSystem("Weapons").addComponentByEntity(entity)

Now the weapons system can draw from other areas as it needs. It might need to check some player properties to determine what weapons are appropriate to add to the ship. Etc. Or, if the entity is an enemy ship it would check other components defining it's level and strength. Then they won't be enabled for processing by the system until the state says so.

And since the state machine is now firmly in System territory, it seems like an appropriate place to do this logic. It is simply assisting with the complexities of game play states in a system rather than becoming a part of state itself.

What do you think of this? Is this going down some ignorant first timer path? Or is this a reasonable way to move forward? It seems from an architectural programming point of view that logic has been appropriately encapsulated but de-coupled.

I hope this clarifies some issues and doesn't just confuse things further.

It helped me have a greater understanding of ECS architecture. In fact, so much so that I'm merging several ECS examples into one for my game.

Thanks!

--Derrek

Derrek Leute

unread,
Sep 21, 2015, 2:43:07 PM9/21/15
to Ash Framework

One more thing on FSM: One feature I like of some state machines is the ability to track whether or not a state can be entered from another state. For example, while falling you can't enter a firing state (this could be in the same state machine or another one). To support this, the convenience of storing and checking the "current state" becomes apparent.

So it may still be necessary to store an FSM's current state as a simple value in some component. But I would support that by handing the component to the enterState function in addition to the desired state and the entity. First, because some state machines may not need this. A simple tracking of the current state to enable consolidated restrictions on stat logic isn't necessary. But when it is it could be supported optionally.

That enables functions like: "isValidNextState:" "didEnterWithPreviousState:" "willExitWithNextState:". These can help with transition issues as well. Storing the current state in a component somewhere (instead of the entire state machine) seems like a reasonable compromise. And, it will still be controlled by the system.

That's not to say you couldn't do this by querying components from the system. You certainly could. And in some cases that may be advisable rather than building it into states (for example, can't fire while the shields are up). But it gives you another tool in your toolbox. And that tool is all about organization for your own code base rather than an architecturally forced convention.

Ok. Neat. Said enough. Now I need to build it.

--Derrek
Reply all
Reply to author
Forward
0 new messages