"Proper" state management for CBE

Started by
8 comments, last by crancran 10 years, 3 months ago

I have asked a question similar to this before but I cannot move forward with my engine without fixing the State Machine. In my old design, the systems hold the needed components and updated them as needed and the states hold the systems they need. Like "Play" holds graphics,physics,input,ai,gui; the "MainMenu" holds gui,input. Also, the states are not hard written to the engine; one can create any type of a system they want with the needed components. The problem was that new systems are created for each state; for example there would be multiple input systems in the lifecycle of the application if more than one state has input system.

As my engine become more complex, I decided to change the design to something similar to Artemis Framework. So, here is the new design:

The systems are created ONCE only and their pointers are passed to the needed states. Each state holds a component map. The first parameter of the component is the pointer of the system. The second parameter is of type ComponentHolder. A component holder is basically an abstract class of different lists of components. For example a GraphicsComponentHolder holds 3 lists {list<GraphicsComponent*>, list<CameraComponent*>, list<LightComponent*>}

Example Snippet:


class GameState {
private:
   std::map<Controller*, ComponentHolder*> mControllers;
   std::map<View*, ComponentHolder*> mViews;

public:
   void update(Real time) {
     for(auto &controller : mController)
        controller.first->update(time, controller.second);
   }

   void render() {
     for(auto &view : mViews)
        view.first->render(view.second);
   }
};

class HordeCameraController : public Controller {
public:
   void updateNode(SceneNode * node) {
     //...
   }

   void update(Real dt, ComponentHolder * holder_) {
      auto holder = static_cast<HordeComponentHolder*>(holder_);
      for(auto graphics : holder->getGraphicsComponents())
         updateNode(graphics);

      for(auto camera : holder->getCameraComponents())
         updateNode(camera);

      for(auto light : holder->getLightComponents())
         updateNode(light);
   }
};

class HordeCameraView : public View {
public:
   void render(ComponentHolder * holder_) {
      auto holder = static_cast<HordeComponentHolder*>(holder_);
      for(auto camera : holder->getCameraComponents())
         h3dRender(camera->getNode());
   }
};

Is there another way I can build this? Or should I do it this way? Should I just update the component holder when state changes or do something different. I am very noob at state management, so I really need help on designing on.

Thanks,

Gasim

Advertisement


The second parameter is of type ComponentHolder. A component holder is basically an abstract class of different lists of components. For example a GraphicsComponentHolder holds 3 lists {list, list, list}
*>*>*>

I don't understand for the need for subclasses of ComponentHolder (e.g. what additional behavior does GraphicsComponentHolder have over ComponentHolder?). Likewise, I don't understand the need for subclasses of View and Controller. Can't Controller just update all of the things inside the ComponentHolder?

Backing away a bit: maybe you should even reconsider the notion of GameStates. What's the real difference between the "Play" state and the "Main Menu" state? Aren't the states simply implied by the currently active entities/components? Do you really need any custom functionality for the states?

I'm not saying that's definitely the way to go, but it's something to think about.

In my current project, I do sort of have the concept of "states", and each state has it's own completely separate entity manager (and completely separate systems). This does have the advantage that I can easily just completely shutdown and destroy all the entities related to the main menu screen, for instance (this would be more difficult if I shared entity manager and systems across states). So that's something you might want to think about too. I don't have any custom subclasses for my states, because they don't have any custom functionality... they're just the sum of their parts (i.e. their functionality/logic is completely defined by the entities that exist in them). And I do have some code that sits above the states that manages transitions from one to another.

I've been wondering if I could refactor this a bit though, and eliminate my separate entity managers altogether.

Hello there!

I too have had my share of problems implementing a finite state machine.

I have found the very last answer in this thread (the post from Henrique Barcelos) to be a very good starting point.

It is easy to understand and the implementation is very generic.

Good luck!

*EDIT: I guess I didn't really answer your question..

First of all I agree with phil_t that you should try to keep your components more generic in general.

If you do follow the example that I posted a link too, I think a good idea would be to put the system parts of the ECS in the machine and component parts in the states.

Something like this psuedo code..


// In machine.cpp

void Machine::on_render()
{
  _state->on_render(*this);
}
 
// In some state.. lets say play-state.cpp
void PlayState::on_render(Machine& machine)
{
  machine._renderSystem.on_run(this->_world);
}

..where _renderSystem is a local RenderSystem in machine and _world is a local container of components (enteties)


I don't understand for the need for subclasses of ComponentHolder (e.g. what additional behavior does GraphicsComponentHolder have over ComponentHolder?). Likewise, I don't understand the need for subclasses of View and Controller. Can't Controller just update all of the things inside the ComponentHolder?

The controller needs to know how its going to update the holder: bullet physics updates the holders in its own way, the graphics controller updates it in its own way; same with the Views. As for the subclassing of holders, I only do that for having less amounts of castings to the needed type. Instead of casting every component to the right type, I just cast the holder to the right type and access its elements. How would you do it? I don't really understand how its possible to have generic holder, view, and controller classes while still preserving flexibility of the state machine.


Backing away a bit: maybe you should even reconsider the notion of GameStates. What's the real difference between the "Play" state and the "Main Menu" state? Aren't the states simply implied by the currently active entities/components? Do you really need any custom functionality for the states?

For me there is not meaning behind States as there is no meaning behind Entities. They are both just containers with ID. The way I think about both of them is like a Storage Unit - by themselves, they are pointless; but once you put the things in it, they get valued :P The State Manager I want to implement is very different than most I have seen in the internet; the ones I have seen always inherit from a base State class to a PlayState or MainMenuState. For my state management, there is a generic State class; that is handled by any sort of state machine - whether its FSM, HFSM, or BT. Then, there is a special case "GameState" where its used for changing menus or whatever. This container itself consists of "components" = managers, controllers, views. The systems are identified by "name". The main difference between controllers/views and managers is that, each state has its own created manager, while each state stores only the reference to the particular controllers/views. The controllers "don't care" about the existence of a state, they run in separate threads and update/render whatever is given to them. I have had Managers, Controllers, and Views working altogether but I want to change that and separate the controllers/views.


In my current project, I do sort of have the concept of "states", and each state has it's own completely separate entity manager (and completely separate systems). This does have the advantage that I can easily just completely shutdown and destroy all the entities related to the main menu screen, for instance (this would be more difficult if I shared entity manager and systems across states). So that's something you might want to think about too. I don't have any custom subclasses for my states, because they don't have any custom functionality... they're just the sum of their parts (i.e. their functionality/logic is completely defined by the entities that exist in them). And I do have some code that sits above the states that manages transitions from one to another.

I've been wondering if I could refactor this a bit though, and eliminate my separate entity managers altogether.

I just realized that I want to build something similar to what you have :) The moment I have a working version of what I want to achieve, I am thinking of getting rid of the entity class all together because it has no specific function than be a "middle man" between the components, which I think components should do directly. For me it makes sense to have a State because of transitions in the State Machine. But for Entity, I would rather change it to a sorted double linked list of components (or maybe a set. i'll figure it out when its time); where one can find the other components by comparing IDs. One question, about Entity Managers? what are they? Are they basically a World or are they the creator of the entities? Or are they both?


I too have had my share of problems implementing a finite state machine.
I have found the very last answer in this thread (the post from Henrique Barcelos) to be a very good starting point.
It is easy to understand and the implementation is very generic.

My main problem is not the implementation of the state machine but the design of a flexible and generic GameState (MainMenu or Play) that can be created into anything.


If you do follow the example that I posted a link too, I think a good idea would be to put the system parts of the ECS in the machine and component parts in the states.

Something like this psuedo code..

I really like the idea of the world. Can you please give me an idea of what a world is? What can it do? What does it store? How is it compared to my ComponentHolder? Is it just a container of component holders?

I think this topic isn't about state management but more about "Designing a world object"


The controller needs to know how its going to update the holder: bullet physics updates the holders in its own way, the graphics controller updates it in its own way; same with the Views. As for the subclassing of holders, I only do that for having less amounts of castings to the needed type. Instead of casting every component to the right type, I just cast the holder to the right type and access its elements. How would you do it? I don't really understand how its possible to have generic holder, view, and controller classes while still preserving flexibility of the state machine.

I guess I'm still trying to wrap my head around your architecture. You may be using different terms for the same concepts (there isn't really a common way to implement entity-component frameworks).

In my framework, you just create a particular System and add it to a list in the EntityManager. Each system is updated in exactly the same way (why wouldn't it?), there are no "views" or "controllers". If I want to make a new system, the only code changes it requires is in my initialization code when I add it to this master system list.

The EntityManager stores all the components (one array per component type). It exposes a "component map" for each component type that lets you get a particular component given an entity id. The systems (which have their own list of entity ids that is kept up-to-date by the EntityManager) use these to fetch the components by entity id.

However, if a system only depends on one component type, it can just enumerate through the "component map" directly (it doesn't care which entity it belongs to). Otherwise (in the common-use case where a system depends on multiple component types) it uses its list of entity ids to request the necessary components for each entity id.

You mentioned the Artemis framework. My original design was based very closely on this, so it should be familiar.

I describe some of it here:

http://mtnphil.wordpress.com/2013/03/27/entity-component-system-architecture-c/


In my framework, you just create a particular System and add it to a list in the EntityManager. Each system is updated in exactly the same way (why wouldn't it?), there are no "views" or "controllers". If I want to make a new system, the only code changes it requires is in my initialization code when I add it to this master system list.

Do you register observers to the systems to do some system specific codes. Thats why I have a different update function per system. Like BulletSystem update:


//Bullet.cpp
void BulletController::update(World * world) {
   btDynamicsWorld * dynWorld = world->getHolder<BulletHolder>()->getBulletWorld();
   dynWorld->stepSimulation(1/60.0, 0);
}

//HordeController.cpp
void HordeGraphicsController::update(World * world) {
   for(auto component : world->getHolder<HordeNodeHolder>()->getNodes())
      updateComponent(component);
}

//HordeCameraView.cpp
void HordeCameraView::render(World * world) {
   for(auto component : world->getHolder<HordeCameraHolder()->getCameras());
      h3dRender(component->getHordeNode());
}

For me doing view vs controller is just a conventional thing. I just split the system into Controller and Views, where controllers do update while views do render.

Ah, ok, it was just a term misunderstanding. No, I have custom System implementations, just like you have custom Controller and Views.

I've been wondering if I could refactor this a bit though, and eliminate my separate entity managers altogether.

We decided to apply the concept of a Scene to entities.

Each entity references their owning Scene which is managed through the engine's SceneManager. In the background, every scene instance uses the same entity manager and component subsystems. We initially had them separated as you do, but decided to that each system could just as easily create buckets per scene and maintain various lists of components, allowing us to update all scenes in one loop or update certain scenes more frequently than others with a simplified main loop.

Plus when we want to eliminate that login or character selection screen of entities or perhaps a transition map's set of entities, we can do so using a timeout-based task that will call the Scene's destroy method if the scene remains untouched by the engine for a defined period of time. This was a big plus where players entered a dungeon, later died and were teleported to the graveyard and then had to run back to the instance and rez. The load time on the instance was drastically reduced.

Thats how I do it now, I have a "World" class that holds component holders holders and the systems act upon those component holders. The World doesn't care about the existence of any manager or system. I am still thinking about making design more cleaner as I am deciding whether a system should know about the state dealing with resources or does the system only act upon the world where the world is coming from, either from a state or from somewhere else.

I don't believe that systems should care how the World came into existence, whether by something which the current application state creates or whether it is something that was created due to an event triggering specific actions in a subsystem.

In our engine's design, the World emits events when specific actions take place with an entity and the systems are simply subscribers to various events of interest. When the events fire, enough context data is provided so that the systems can obtain the world reference, query entity state, or even manipulate other systems through various exposed interfaces from the engine's core framework.

These systems are self contained behavior that alter state based on a finite set of inputs. Their dependencies are generally kept quite lean primarily to advocate decoupling and the ability to plug and play variations of system behavior without disrupting the core game mechanics too harshly.

This topic is closed to new replies.

Advertisement