Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I use static classes in an ECS?

Tags:

c++

static

Hey I am currently working on a small entity component system where there are classes like EntityManager, ComponentManager and SystemManager that are only instantiated once and heavily communicate with each other.

I created a class world which owns all the managers and acts as the center for all communication between the managers. Making all the Managers static would make a lot of things much easier, more understandable and I wouldn't even need the world class.

I know though that static classes (I know "static classes" don't exist but I mean classes with only static members) act as if they were global and global variables are Bad®.
So I wonder what is recommended to do in this case

Thanks for your answers

Che

Edit: The World class looks like this:

class World
{
public:
    EntityManager* entityManager;
    ComponentManager<PositionComponent>* componentManager;
    MovementSystem* movementSystem;
    Entity e;

public:
    World(sf::RenderWindow& window);
    void update();
};

To communicate each Manager needs a pointer to the world to access the other managers. Like this world->entityManager->getEntitys()

like image 286
Earl of Lemongrab Avatar asked Nov 07 '22 13:11

Earl of Lemongrab


1 Answers

(I suggest that the project is a game or something close to it)

I don't suggest you to make all members static. The main problem with it that you're loosing control on object's lifetime. You can't destroy and create new object at runtime easily because there is no object. For example, if you want to change a manager at runtime you'll have to implement cleanup code manually. In case of C++ objects C++ helps you with errors/warnings, default values and class members by value.

There are few popular ways to implement and use managers in gamedev:

  • Use Singleton pattern. In this case class have one static method that returns link to non-static object.
  • Pass dependencies that method requires during the method call manually.

Singleton pattern, I think, is the best way in terms of price-quality ratio for you. Despite on all criticism of this pattern it does its job well (most of game projects I saw used Singleton approach to implement manager classes).

There is an important thing I want to suggest you about this pattern. Don't use default implementation of Singleton pattern. Create methods for creating and destroying object instead of hiding it inside of getter. Here's simple example of glue code for a manager:

class Manager {
private:
    static Manager* ms_manager;
public:
    static void CreateManager() { ms_manager = new Manager(); }
    static void DestroyManager() { delete ms_manager; }
    static Manager* GetInstance() { return ms_manager; }
};

Usage is:

Manager::GetInstance()->SomeMethod();

Passing dependencies approach has its own advantages. It may sounds too difficult to pass everything in every Update method but it's not. You can create context class, set all dependencies there and pass it to every method that needs it. It's almost like your World class but it must be structure with minimum of code and no dependencies. Don't store there any game objects by value (only primitives, geometry vectors and stuff like this). It may be something like this:

struct Context {
    EntityManager* entityManager;
    ComponentManager<PositionComponent>* componentManager;
    MovementSystem* movementSystem;
    Entity* rootEntity;
};

Usage is:

GameObject::Update(Context& context) { context.entityManager->SomeMethod(); }

The point of this approach that you can tune context for some objects at runtime. For example, if you have LODs you can save in context current LOD level and change it at runtime for some objects depends on distance to the camera.

like image 101
Kostya Regent Avatar answered Nov 15 '22 05:11

Kostya Regent