Game State management in Entity Component System architecture

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

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.

To avoid this, edge cases should be worked in not as hacks to the existing code base but as full fledged iterative parts of the overall solution. But unfortunately, developers tend to be lazy and take the least path of work due to various reasons and this is one of the largest contributors to the problem you described above. Whether you're doing something insanely simple or something far more complex and involved, the same series of iterative steps should be considered, validated, and executed.

Advertisement

Personally for my project I'm using a screenmanager modified from the Microsoft XNA sample to work with SharpDX, while the ECS is controlled by it's own EntityManager based off the one from Almirante Engine.

Both the Screen Manager and the Entity Manager are placed in a helper class called GameServices, which makes stuff available throughout the entire application, including e.g. the Content Manager and other things.

So instead of every Screen/State having it's own Entity Manager they can all access GameServices.EntityManager to do entity related stuff.

I may note that (for now at least) my game has only one Screen/State that is actual gameplay, while the others are menu related, so I don't know how well this works with different gameplay screens handling different kinds of gameplay.

Both the Screen Manager and the Entity Manager are placed in a helper class called GameServices, which makes stuff available throughout the entire application, including e.g. the Content Manager and other things.

So instead of every Screen/State having it's own Entity Manager they can all access GameServices.EntityManager to do entity related stuff.

I used to do something like this, but it's risky and error prone. If GameServices is global, then you are exposing these systems to your entire program. It becomes easy to use that access point in places where one really shouldn't, tightly coupling disparate systems that really shouldn't know about each other at all. And tracking down a bug can become extremely difficult since function calls aren't localized to a well defined subset of code.

If your passing your GameServices object around, it's somewhat better, but then why not take the even more better approach of passing these Manager classes specifically to the objects and/or functions that need them?

Not that your approach doesn't work. It does and can be okay for small projects, but it allows the introduction of some nasty bugs, and can become a crutch. I couldn't see how to structure my code any other way until I finally scrapped my entire code base and vowed not to go down that path for my next project. And now I'm a better coder and my code has no hidden dependencies, and is easier to understand and maintain.

Be careful about multi-threading that design.

Once you have a global that is so useful, you tend to use it, even when you shouldn't.

And finding a crash in multithreaded code can lead to grey hair and head shaped inprints in various pieces of furniture.

Well, the initial point of making the GameServices class in the first place was to make the XNA (and now SharpDX) ContentManager accessible outside of the main game loop, which is a class derived from SharpDX.Toolkit.Game. Also, GameServices is a static class.

When switching from XNA to SharpDX I intended to use it's GameSystems class instead, but again, it's only available inside the main loop.

With GameServices now containing almost a dozen objects It's indeed getting impractical and cumbersome to use, though it still works.

But so far I have failed to find a better solution, as it contains stuff that needs to accessible from a variety of places, e.g.:

Content Manager

Configuration Manager

Entity Manager

Archetype management

GraphicsDevice and GraphicsDeviceManager

etc..


But so far I have failed to find a better solution, as it contains stuff that needs to accessible from a variety of places, e.g.:

containts stuff that needs to accessible from a variety of places

There is your error/problem already. A class usually doesn't need to be/should not have to be accessed from many variable places. If it still does, then you need to break up the class. There are usually straight-foward ways for doing so. Sometimes, you can just simply make two classes out of the one without much issues. Say if you store both a d3d-device, and the audio-device in this class. Seperate both of those into own classes, graphics and sound have nothing to do with each other. There possibly exists some high-level structure that somehow both configures rendering and audio triggering, but thats not part of eigther GraphcisDevice/SoundManager/... classes.

Just in case someone brings up the "just do whatever works" argument. If you want to get stuff done, it really doesn't matter how you do it, but just be noted that your design really isn't the best - without judging whether this has actual negative effects on your developement-speed. Just let it be said so, if you plan on making a larger-scale projects that lasts 2+ years, such a design as you described is going to bite you in the a** pretty hard. I personally had two projects go down the toilet after a working time of ~9 months each because the code just became a cluttered mess that couldn't be improved without spending a week for each minor feature that needed to come.

OK finally I'm returning to this post...

I'd like to clarify my vision.

In my design the ECS handles pretty everything, from UI widgets to game "actors" (e.g., player, enemies, items). The reason to do so is to maximize code reuse. Position of UI Button on screen can be a component that I can reuse for Position of Player. No difference. A system rendering 2D stuff can be used to render both UI widgets and game specific entities. I think this is the point of this kind of architecture.

When I talk about "Game State" I mean things like: "Main Menu", "Settings Screen", "In-Game" or "Paused". Modelling each Game State as an entity container just makes sense: "Main Menu" will mainly contain buttons and text labels (we already said they are entities too), "In-Game" state will contain more game specific stuff.

I prefer to add Entity Systems to the game and make them always be active. This is easier and, if you think about it, the fact that your collision system is idle when inside the Main Menu is not that huge problem performance-wise.

Your opinion is much appreciated

For Game State Management you can take a look at this: http://xbox.create.msdn.com/en-US/education/catalog/sample/game_state_management

It's a good sample from Microsoft and I'm using it in my own project after porting it from XNA to SharpDX

In it, you have different kinds of Screens like Gameplay, Menu, Pause (which is also a menu in a way) and the system is easily extensible and customizable

For Game State Management you can take a look at this: http://xbox.create.msdn.com/en-US/education/catalog/sample/game_state_management

It's a good sample from Microsoft and I'm using it in my own project after porting it from XNA to SharpDX

In it, you have different kinds of Screens like Gameplay, Menu, Pause (which is also a menu in a way) and the system is easily extensible and customizable

I already know about that sample, I'm discussing about Game State management in the context of an ECS architecture.

I seee basically two options for you here:

1. Have one single ECS system running parallel to the Game State Management and is shared by all States. That would require keeping track of which Entities belong to which Game State and setting them Active/Inactive accordingly.

2. Let each Game State have its own ECS system. The would avoid the tracking issue, but creates a lot of code overhead.

I'd keep the UI seperated from the ECS though. The rendering would be done seperately as well preferably, because then you can reuse the ECS code, the UI code and the render code independently in case you only need one or two of those three

This topic is closed to new replies.

Advertisement