Game State management in Entity Component System architecture

Started by
26 comments, last by Juliean 9 years, 5 months ago

My first post here!

How would one handle Game State Management in the context of an Entity Component System architecture?

In all my previous games I've used an FSM-based approach where each Game State is an object with its own Update and Draw methods. Each Game State would mantain a local list of Game Objects to update and draw.

When it comes to ECS I can imagine the following:

1) We still have Game States as individual objects

2) Each Game State has its own list of Entities (probably some kind of "EntityManager")

3) Game States have their own initialization logic: they can create their own entities during init. This logic is executed when the Game State is created or when it becomes active. We have a GameStateManager object to manage Game States life cycle

4) Entity Systems belong to the Game (not the Game States) but they only process those Entities that are contained by the currently active Game State

Please share your thoughts

Advertisement

game states and ECSs are both somewhat poorly defined terms.

for example, some folks think of popup menus etc as game states.


each Game State is an object with its own Update and Draw methods. Each Game State would mantain a local list of Game Objects to update and draw.

this is usually only required when making a hybrid game, such as a hybrid FPS/rts game. when in fps mode, it plays like a shooter, when in rts mode, it plays like an rts. the fps and rts engines each has their own loop for render, update, and input. assets may be shared or not. this is an example of a game that runs in multiple states or modes.

i did an rpg once (Mordorventiure III) which has 4 states (4 games engines in one): wilderness adventure, dungeon adventure, city adventure, and castle construction/combat.

Caveman 1.0 had 4 states: a The Sims style interface for wilderness, and separate fps shooter engines for combat, cavern exploration, and wandering around settlements.

Caveman 3.0 only has two states: fps mode, and The SIms "doing an action" mode. they actually share the same loop, and call different render and input functions, depending on mode. update is the same for both.

Both versions of Caveman share assets between the various play modes.

I don't recall if Mordorventure did or not, probably some at least.

SimTrek/Simspace also had multiple states (5 to be precise): bridge view, transporter room, shuttle bay, shuttle flight sim, and fps away team missions.

in all of these examples, the "state" had its own render, input, and update methods.

if you're building hybrid games as described above, i'd say you're on the right track. if you consider things like init_game() and run_ingame_menu() to be game states, i'd say you're probably over-engineering things, and perhaps using design patterns for other than their intended purposes. One must be very careful about applying non-game design patterns to games. They are not designed for games, and can lead to sub-optimal solutions. "never send a boy to do a man's job, and never send a database (or application) programmer to do a gamedev's job."

i usually handle "state" via some variable:

in simspace it was something like: if (view==bridge_view)

in caveman 1.0 it was something like: if (mode==combat_mode)

in caveman 3.0 its: if (cm[cm0].current_action != DONOTHING)

caveman also uses "states" for drawing the scene. a "location" variable tracks OUTDOORS, INCAVE, INCAVERN, and INSTHELTER. this controls which of 4 rendering methods is used to draw the scene. certain actions are also prevented in caves, caverns, and shelters, so these "states" also technically have unique update methods.

so, do you define a state as "fps/the sims action mode" (2 states)?

-or-

outdoors, incave, incavern, and inshelter states? (4 states)?

-or-

both: one of 4 locaitions and one of two types of activity (fps or the sims type actions), for a total of 8 states?

see what i mean about the fuzzy definition of "game state" ?

note that in Caveman 3.0, the "state" is a function of both the player.location, and player.current_action variable.

i find that things are usually easier when you use multiple specific state variables, such as location and action, vs one state variable to track all possible combos of state variables. The AI in caveman probably uses half a dozen or more state variables (taking fire, half dead, in collision recovery, etc) instead of one state variable that enumerates all the possible combos of these state variables (which is probably over 100 combos).

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

ECS gives me migraines.

At first it sounds like a great idea, but you soon end up in a situation were a tiny change in the game can have a massive effect on performance.

It really depends on your definition of an "Entity".

Consider the full on approach where you have an entity that for everything and game objects are bags of entities. So a character running around the game would have ...

SkinnedMeshEntity, WeaponEntity, HealthEntity, BackpackEntity, etc. Each of those would have it's own bag of entities. (TransformEntity, CollisionBoxEntity, ......)

So suddenly this one game object contains hundreds of entities. Nasty but workable. Things get really nasty when you start needing references between entities.

The weapon needs bullets, so it has a reference to a BulletMagazineEntity. A skinned mesh needs an animation entity. A character needs a target to shoot at, which is an entity.... you see were this is going?

It gets even worse if you have multiple entites with a reference to the same entity. Null pointers can really ruin your day.

So be careful at what level you decide an entity exists.

I think the key decision for you is how the game flows. Is your design a bunch of self contained levels with a definite start and end, or an open world type game.

If you are thinking of the latter, I would avoid ECS like the plague. The problems when you start streaming out objects that are refenced by entities can stall development completely. You have an object (bag of entities) that is leaving the game area but objects that are still in the game area have references to them. Next update they try to extract information from the object that doesn't exist anymore with random and probably fatal results.

If you have a more level based design in mind, go for it. At the end of the level you throw away all the entities anyway, no streaming issues at all.

ECS or non-ECS at the individual "game state" level is really up to you.

the primary reason for using ECS is to avoid entity class hierarchy hell:

http://www.gamedev.net/index.php?s=b4371f2221d0bc5585fada17196af82e&app=googlecse#gsc.tab=0&gsc.q=entity%20class%20hierarchy%20hell

it can also be used to allow non-programmers to define new entity types using components created by the programmers.

there's also a thrid type of "ECS" which is about cache optimization, once all other optimizations have been done, and you're still too slow. only seen one case ever of this type. and NO, its not the same thing as the other types! its all about data oriented code, and has little or nothing to do with object hierarchies or letting non-programmers define new entity types from existing component types. those are just side benefits of the way the code gets re-organized - but its all in the quest for speed, nothing more.

some folks go ECS because they're in object hierarchy hell. some do it so they can create new entities from exiting components without writing code. and some implement the usual ECS, thinking it will get them the benefits of the "third kind", which is doesn't.

so there are actually 2 types of ECS: the usual type, and the cache optimization type.

and there are three reasons to use ECS:

1. object hierarchy hell - try conventional ECS. i use procedural code, so this a non-issue for me.

2. define new entity types from exiting components without code. - implement conventional ECS.

3. i've optimized EVERYTHING in the game, and i'm still too slow, AND the bottleneck is the cache misses caused by the layout of my entity data structures. optimize entity data processing to be as cache friendly as possible. this just happens to result in a rather ECS'ish looking system. thus the confustion that "ECS = cache friendly as possible", when in fact its more like: "conventional ECS = probably a bit more cache friendly, but not fully optimized".

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php


When it comes to ECS I can imagine the following:

1) We still have Game States as individual objects

2) Each Game State has its own list of Entities (probably some kind of "EntityManager")

3) Game States have their own initialization logic: they can create their own entities during init. This logic is executed when the Game State is created or when it becomes active. We have a GameStateManager object to manage Game States life cycle

4) Entity Systems belong to the Game (not the Game States) but they only process those Entities that are contained by the currently active Game State

Please share your thoughts

I think this is reasonable. I assume by "game states", you're talking about things like, main menu, lobbies, actual game play, etc... If you have separate lists of entities, you might possibly want separate instances of each system too. It really depends on your needs. For instance, think about how the rendering system(s) will work when transitioning between game states (can you have more than one game state active at a time?).


in all of these examples, the "state" had its own render, input, and update methods.

That seems a bit strange to me. Why would the render methods differ? At any rate, if the OP is using an ECS, I wouldn't expect the game state to have render or input methods - that functionality should be handled by the render and input systems (which are the same for all game states) and the entities that belong to that game state.


Consider the full on approach where you have an entity that for everything and game objects are bags of entities. So a character running around the game would have ...

SkinnedMeshEntity, WeaponEntity, HealthEntity, BackpackEntity, etc. Each of those would have it's own bag of entities. (TransformEntity, CollisionBoxEntity, ......)

Most of those would be components, not entities (e.g. HealthComponent, TransformComponent, CollisionComponent).


The weapon needs bullets, so it has a reference to a BulletMagazineEntity. A skinned mesh needs an animation entity. A character needs a target to shoot at, which is an entity.... you see were this is going?

Yes. This is one good reason why code should not exist in components. Components can be related by the fact that they are attached to the same entity, and the systems (with the code) can reason over them.


or an open world type game.

If you are thinking of the latter, I would avoid ECS like the plague. The problems when you start streaming out objects that are refenced by entities can stall development completely. You have an object (bag of entities) that is leaving the game area but objects that are still in the game area have references to them. Next update they try to extract information from the object that doesn't exist anymore with random and probably fatal results.

This problem has nothing whatsoever to do with ECS. You would have to solve exactly the same issues if you were using a more traditional OOP architecture (or whatever). "some game objects are streamed out of the world and other active game objects may have references to them". If anything, using an ECS (or any data-oriented framework) would make this more straightforward because you generally have more knowledge on where your data is. For example, a "TargetingComponent" would ideally be re-usable for any game object that needs to target another - so your code that reasons over what happens when parts of the world stream in/out only needs to look through TargetingComponents, rather than this information being hidden behind some "OOP wall" on an object.


Norman Barrows, on 22 Oct 2014 - 08:49 AM, said:

in all of these examples, the "state" had its own render, input, and update methods.

That seems a bit strange to me. Why would the render methods differ? At any rate, if the OP is using an ECS, I wouldn't expect the game state to have render or input methods - that functionality should be handled by the render and input systems (which are the same for all game states) and the entities that belong to that game state.

what you see is there is the evolution of the hybrid game design.

SIMSpace consisted of 2 flight sims (starship and shuttle) plus a FPS shooter engine for away team missions. transporter room and shuttle bay scenes were basically fancy menus. It started with the starship sim, then the shuttle sim was added, then later the away team engine was added.

Mordorventure started with the dungeon engine, then added the wilderness engine, and the city adventure engine. then i added the entire engine from my game "Siege!" to add castle construction and combat with armies to Mordorventure. i've often found it simpler to just code another simple engine, than to modify one to draw and run two types of scenes.

Caveman 1.0 started with the SIMs style wilderness adventure engine, plus the FPS combat engine. The cavern and settlement fps engines were added later. again, it was easier to simply code a new engine than mod the exiting one to handle a new type of scene.

With caveman 3.0, all "states" are handled with just one engine. it renders scenes in shelters, outdoors, in caves, and in caverns. it processes input depending on location (OUTDOORS, UPATREE, INSHELTER, INCAVERN, or INCAVE). in update, it handles both FPS style play, and SIMs style "doing actions".

think of it this way:

switch (state)

case 1: run_engine_1

case 2: run_engine_2

vs:

while !quitgame:

switch(state)

case 1: render1, input1, update1

case 2: render2, input2, update2

vs:

render:

switch(state)

case 1: render1

case 2: render2

input

switch(state)

case 1: input1

case 2: input2

update:

switch(state)

case 1: update1

case 2: update2

is just a matter of where you do the switch based on state. before the game loop, in the game loop, or in render, input, and update.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php


I wouldn't expect the game state to have render or input methods - that functionality should be handled by the render and input systems (which are the same for all game states) and the entities that belong to that game state.

i think as you do, but i've seen many (naive?) game state designs where things like the in-game popup menu are a separate game state with its own render and input routines. i consider that an example of mis-applying a design pattern or over applying it to the ridiculous extreme.

sometimes it seems to me that folks these days are too hung up on over-engineering things - how many possible (appropriate or otherwise) design patterns can i possibly use in how many possible places? an over-obsession with code-smithing, refactoring, and code design for its own sake, instead of simply as a means to an ends (the release date - gone gold). its understandable though, its only natural for a programmer to want to improve existing code. i could probably spend a year refactoring caveman to within an inch of its life. but in the end, all i'd have was some pretty code that did pretty much the same thing it did before. it might be a little easier to work on, but the game's almost done. the time savings would not the exceed the refactoring time required.

the longer i make games, the more i come to appreciate the KISS (keep it stupid simple) principal.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

In all my previous games I've used an FSM-based approach where each Game State is an object with its own Update and Draw methods. Each Game State would mantain a local list of Game Objects to update and draw.

Like the system I described here:
General Game/Engine Structure


1) We still have Game States as individual objects

As you should.

2) Each Game State has its own list of Entities (probably some kind of "EntityManager")

So far no problem whether you are using an entity-component-system or any other scheme.
Directly under the state object you should have 1 or more instances of a SceneManager. The SceneManager would have an EntityManager if you go that route, or if you don’t it could have an ObjectManager etc. In either case you would want a SceneManager to orchestrate all the high-level rules of the scene (knowing when to run systems, etc., or how to tell object managers when to perfrom certain types of updates, etc.)

3) Game States have their own initialization logic: they can create their own entities during init. This logic is executed when the Game State is created or when it becomes active. We have a GameStateManager object to manage Game States life cycle

Now you are making the engine too restrictive. Your goal is to make the engine easy to use and have the updating of entities less of a copy-paste operation for each state but rather an automatic operation, but you end up trapping yourself into being unable to do anything at all without it going through the entity-component-system.
As I said above, updating the entity-component-system should be done via a SceneManager, which can be evoked by the state object directly (at any time the state object pleases). Not only can you have multiple scenes in a single state, you will certainly find it handy in the future to be able to perform some state-specific custom logic and rendering via the state’s Update() and Draw().
Update() and Draw() should always be there, even if you move to entity-component-systems.
If you want to make a point that most of the time every state will have the same Update() and Draw():
bool MyScene::Update() {
    m_SceneManager.Update();
}
bool MyScene::Draw() {
    m_SceneManager.Draw();
}

…then you can simplify without losing the freedom of Update()/Draw().

Add 2 virtual functions to the base State class:
virtual SceneManager * State::GetSceneManager() { return &m_DefaultSceneManager; } // Doesn’t need to be overridden often. All scene objects can use m_DefaultSceneManager.
virtual bool State::AutomaticUpdate() const { return true; } // Override to return false to activate the use of Draw()/Update().
The Game class will check AutomaticUpdate(), and if true it will call m_CurState->GetSceneManager()->Update() (etc.) automatically.
If false, it will call m_CurState->Update() (etc.) instead, allowing the states the freedom to manually perform updates and renders.

4) Entity Systems belong to the Game (not the Game States) but they only process those Entities that are contained by the currently active Game State

I don’t have any strong advice on where the systems should live. They likely won’t change throughout a game so they could be part of the Game object, but you could make them context-sensitive (to quote Conker’s Bad Fur Day) by making them part of the states. You could avoid rewriting a bunch of them for each state by making a common set of systems and allowing the states to pick from those (or add their own), with a function available to pick the “default” set. You can also make a hybrid system, taking systems from the Game object usually but with another State virtual method they could come from the state object instead.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

ECS gives me migraines.

At first it sounds like a great idea, but you soon end up in a situation were a tiny change in the game can have a massive effect on performance.

Well, the performance hit can happen in any other architecture, too.

It really depends on your definition of an "Entity".


Consider the full on approach where you have an entity that for everything and game objects are bags of entities. So a character running around the game would have ...

SkinnedMeshEntity, WeaponEntity, HealthEntity, BackpackEntity, etc. Each of those would have it's own bag of entities. (TransformEntity, CollisionBoxEntity, ......)

ECS stands for Entity Component System, although IMHO "component based entity system" would be better. A "game object" is often used as a synonym for entity.

With this in mind ...

* SkinnedMesh is a component,

* Weapon is probably an entity by its own rights (because it should probably be exchangeable),

* Health is a component, or perhaps better a variable of a Constitution component,

* Backpack is perhaps an entity with an Inventory component (if used as an item by its own), or perhaps only an Inventory component.

Other things like e.g. terrain are (should be, at least ;)) never an entity, so "the full approach" is wrong from the start. However, what's wrong with a weapon, a clip of ammo, or a health pack (not the Health constitution!) being own entities? What's wrong with each of them having its own placement, its own mesh, and other components? Nothing, because your game needs this! You want the weapon to lie around on the ground, ready to be picked up. So it is placed in the world. The weapon has its own mesh clearly.

So suddenly this one game object contains hundreds of entities. Nasty but workable. Things get really nasty when you start needing references between entities.

Well, I think dozens of components would already be more than sufficient. However, the aspect you are indicating here, if I understand you correctly, is that parts that were usually embedded as simple variables, now become their own software objects. The typical example is the placement, since close to everything is placed somewhere in the game world and hence needs a placement. Point is that "close to everything" is not "everything", and with a reasonable amount of static geometry it may be even wrong at all as an argument. However, even if I accept that a placement is needed by close to everything, it is just a single isolated example in front of a much greater couple of counter examples. And of course you are not hindered in the end to embed some "very important components". E.g. Unity3D does so with the placement, too, although I do not agree with this approach ;)

The weapon needs bullets, so it has a reference to a BulletMagazineEntity. A skinned mesh needs an animation entity. A character needs a target to shoot at, which is an entity.... you see were this is going?

Again, use the solution that is suitable.

* A firearm weapon needs ammo, it does not need a BulletMagazine per se. After picking up a ammo clip, the ammo counter could simply be increased and the clip entity could be wasted.

* There is no technical / architectural reason why a skinned mesh (a component) needs an animation (a component). It is a decision of the designer. S/he could create a static skinned mesh entity. Also, it's the entity that needs both the skinned mesh and the animation (as said, by design).

* A character needs a target to shoot at, which is an entity ... well, what should it be otherwise? I go even further: I say that shooting is an action that checks against a BoundingVolume component of an entity, and if the hit test passes, it causes a defined decrement of a variable called "Health", to be found in the Constitution component of the targeted entity. There is no Constitution / Health? Then shooting to death is not possible. There is no CollisionVolume? Then aiming at the entity is not meaningful, perhaps not even possible. (One can see from this example, that components can be used also to tag entities for game related purposes. You can even mark a BoundingVolume to be used especially for shooting, for example.)

It gets even worse if you have multiple entites with a reference to the same entity. Null pointers can really ruin your day.
This happens regardless of the architecture, because it is a much more lower level problem. Even an unresolvable relation in a game item database has this effect.

...

Well, the thing is that the representation of problems comes from the game design. The software architecture should be powerful enough to solve the problems, and this not only acutely but also in the long term

As with everything, the programmer can make mistakes, regardless of whether ECS or not is in use. As with everything, also ECS is not the silver bullet. But for a common problem in game development, it is a better approach than others are. As said, this doesn't mean that every occurring problem should be mangled into the ECS; there are parts that do not fit.

Just my 2 Cents. Perhaps also a bit biased ;)

EDIT: Oh, forgotten to say: It is true that properly implementing an ECS is its own can of worms. There are many aspects to consider, many routines to go, and going the wrong route will have negative impacts like performance lost. It implies a great hurdle. But that doesn't mean that the architecture is bad in itself. Instead it means that there are good and bad approaches.

I was illustrating what a pile of shit you can end up with if you take a system to the extreme.

Which is why I asked you to consider a case. I never suggested that was the way to go.

Programmers tend to start with a clean design that works well, which then gets buggered about with to handle edge cases. Then a few more edges cases come around, the system stops being clean and becomes a mass of inefficient code.

Then you have a few months of hell making the whole system work for release, and vow to change it all.

And you start with a new clean design, until you find an edge case.........

Being aware of things like inter-object dependencies from the start and designing for it can save you several ulcers in later life.

This topic is closed to new replies.

Advertisement