16
\$\begingroup\$

I know singletons are bad, my old game engine used a singleton 'Game' object that handles everything from holding all data to the actual game loop. Now I'm making a new one.

The problem is, to draw something in SFML you use window.draw(sprite) where window is an sf::RenderWindow. There are 2 options I see here:

  1. Make a singleton Game object which every entity in the game retrieves (what I used before)
  2. Make this the constructor for entities: Entity(x, y, window, view, ...etc) (this is just ridiculous and annoying)

What would be the proper way to do this while keeping an Entity's constructor to just x and y?

I could try and keep track of everything I make in the main game loop, and just manually draw their sprite in the game loop, but that too seems messy, and I also want absolute full control over an entire draw function for the entity.

\$\endgroup\$
  • 1
    \$\begingroup\$ You could pass the window as an argument of the 'render' function. \$\endgroup\$ – dari Dec 3 '15 at 22:05
  • 25
    \$\begingroup\$ Singletons are not bad! they can be useful and sometimes necessary( of course it's debatable ). \$\endgroup\$ – ExOfDe Dec 3 '15 at 22:06
  • 3
    \$\begingroup\$ Feel free to replace singletons with plain globals. No point in creating globally required resources "on demand", no point in passing them around. For entities, you can use a "level" class though, to hold certain things that are relevant to all of them. \$\endgroup\$ – snake5 Dec 3 '15 at 22:06
  • \$\begingroup\$ I declare my window and other dependencies in my main, and then I have pointers in my other classes. \$\endgroup\$ – KaareZ Dec 3 '15 at 22:40
  • 1
    \$\begingroup\$ @JAB Easily fixed with manual initialization from main(). Lazy initialization makes it happen at an unknown moment, which is not a good idea for core systems, ever. \$\endgroup\$ – snake5 Dec 4 '15 at 13:59
3
\$\begingroup\$

Only store data needed to render the sprite inside each entity, then retrieve it from the entity and pass it to the window for rendering. No need to store any window or view data inside entities.

You could have a top-level Game or Engine class which holds a Level class (holds all the entities currently being used), and a Renderer class (contains the window, view and anything else for rendering).

So the game update loop in your top-level class could look like:

EntityList entities = mCurrentLevel.getEntities();
for(auto& i : entities){
  // Run game logic...
  i->update(...);
}
// Render all the entities
for(auto& i : entities){
  mRenderer->draw(i->getSprite());
}
\$\endgroup\$
  • 3
    \$\begingroup\$ There's nothing ideal about a singleton. Why make the implementation internals public when you don't have to? Why write Logger::getInstance().Log(...) instead of just Log(...)? Why initialize the class randomly when asked if you can do it manually just once? A global function referencing static globals is just much more simple to create and use. \$\endgroup\$ – snake5 Dec 3 '15 at 22:29
  • \$\begingroup\$ @snake5 Justifying singletons on Stack Exchange is like sympathizing with Hitler. \$\endgroup\$ – Willy Goat Dec 4 '15 at 1:17
30
\$\begingroup\$

The simple approach is to just make the thing that used to be Singleton<T> a global T instead. Globals have problems too, but they don't represent a bunch of extra work and boilerplate code to enforce a trivial constraint. This is basically the only solution that won't involve (potentially) touching the entity constructor.

The more-difficult, but possibly better approach is to pass your dependencies to where you need them. Yes, this might involve passing a Window * to a bunch of objects (like your entity) in a fashion that looks gross. The fact that it looks gross should tell you something: your design might be gross.

The reason this is more difficult (beyond involving more typing) is that this often leads to refactoring your interfaces so that the thing you "need" to pass is needed by fewer leaf-level classes. This makes a lot of the ugliness inherent in passing your renderer to everything go away, and it also improves the general maintainability of your code by reducing the amount of dependencies and coupling, the extent of which you made very obvious by taking the dependencies as parameters. When the dependencies were singletons or globals, it was less obvious how interconnected your systems were.

But it is potentially a major undertaking. Doing it to a system after the fact can be downright painful. It may be far more pragmatic for you to simply leave your system alone, with the singleton, for now (especially if you're trying to actually ship a game that otherwise works just fine; players aren't generally going to care if you have a singleton or four in there).

If you do want to try doing this with your existing design, you may need to post a lot more details about your current implementation as there isn't really a general checklist for making these changes. Or come discuss it in chat.

From what you've posted, I think a big step in the "no singleton" direction would be to avoid the need for your entities to have access to the window or view. It suggests that they draw themselves, and you don't have to have entities draw themselves. You can adopt a methodology where the entities just contain the information that would allow them to be drawn by some external system (which has the window and view references). The entity just exposes its position, and the sprite it should use (or some kind of reference to said sprite, if you want to cache the actual sprites in the renderer itself to avoid having duplicate instances). The renderer is simply told to draw a particular list of entities, which it loops through, reads the data from, and uses its internally-held window object to call draw with the sprite looked up for the entity.

\$\endgroup\$
  • 3
    \$\begingroup\$ I'm not familiar with C++, but aren't there comfortable dependency injection frameworks for this language? \$\endgroup\$ – bgusach Dec 4 '15 at 13:02
  • 1
    \$\begingroup\$ I would not describe any of them as "comfortable," and I don't find them particularly useful in general, but others may have different experience with them so it's a good point to bring them up. \$\endgroup\$ – Josh Dec 4 '15 at 15:06
  • 1
    \$\begingroup\$ The method he describes as making it so entities do not draw them self but hold the information and a single system handles drawing all entities is used a lot in most popular game engines today. \$\endgroup\$ – Patrick W. McMahon Dec 4 '15 at 16:32
  • 1
    \$\begingroup\$ +1 for "The fact that it looks gross should tell you something: your design might be gross." \$\endgroup\$ – Shadow503 Dec 4 '15 at 19:14
  • \$\begingroup\$ +1 for giving both the ideal case and the pragmatic answer. \$\endgroup\$ – user16989 Dec 4 '15 at 22:12
6
\$\begingroup\$

Inherit from sf::RenderWindow

SFML actually encourages you to inherit from its classes.

class GameWindow: public sf::RenderWindow{};

From here, you create member draw functions for drawing entities.

class GameWindow: public sf::RenderWindow{
public:
 void draw(const Entity& entity);
};

Now you can do this:

GameWindow window;
Entity entity;

window.draw(entity);

You can even take this a step farther if your Entities are going to hold their own unique sprites by making Entity inherit from sf::Sprite.

class Entity: public sf::Sprite{};

Now sf::RenderWindow can just draw Entities, and entities now have functions like setTexture() and setColor(). The Entity could even use the sprite's position as its own position, allowing you to use the setPosition() function for both moving the Entity AND its sprite.


In the end, it's pretty nice if you just have:

window.draw(game);

Below are some quick example implementations

class GameWindow: public sf::RenderWindow{
 sf::Sprite entitySprite; //assuming your Entities don't need unique sprites.
public:
 void draw(const Entity& entity){
  entitySprite.setPosition(entity.getPosition());
  sf::RenderWindow::draw(entitySprite);
 }
};

OR

class GameWindow: public sf::RenderWindow{
public:
 void draw(const Entity& entity){
  sf::RenderWindow::draw(entity.getSprite()); //assuming Entities hold their own sprite.
 }
};
\$\endgroup\$
3
\$\begingroup\$

You avoid singletons in game development the same way you avoid them in every other kind of software development: you pass the dependencies.

With that out of the way, you can choose to pass the dependencies directly as bare types (like int, Window*, etc) or you can choose to pass them in one or more custom wrapper types (like EntityInitializationOptions).

The former way can get annoying (as you have found out), while the latter will allow you to pass everything in one object and modify the fields (and even specialize the options type) without going around and changing every entity constructor. I think the latter way is better.

\$\endgroup\$
3
\$\begingroup\$

Singletons are not bad. Instead they are easy to abuse. On the other hand, globals are even easier to abuse and have loads more problems.

The only valid reason to replace a singleton with a global is to pacify the religious singleton haters.

The problem is having a design which includes classes of which only a single global instance ever exists, and which needs to be accessible from everywhere. This breaks apart as soon as you end up having multiple instances of the singleton, for example in a game when you implement split screen, or in a sufficiently large enterprise application when you notice a single logger isn't always such a great idea after all.

Bottom line, if you really have a class where you have a single global instance which you can't reasonably pass around by reference, singleton is often one of the better solutions in a pool of suboptimal solutions.

\$\endgroup\$
  • 1
    \$\begingroup\$ I'm a religious singleton hater and I don't consider a global a solution either. :S \$\endgroup\$ – Dan Pantry Dec 4 '15 at 14:40
1
\$\begingroup\$

Inject dependencies. A benefit of doing this is now you can create various types of of these dependencies via a factory. Unfortunately, ripping singletons out of a class that uses them is like pulling a cat by it's hind legs across a carpet. But if you inject them, you can swap out implementations, perhaps on the fly.

RenderSystem(IWindow* window);

Now you can inject various types of windows. This allows you to write tests against the RenderSystem with various types of windows so you can see how your RenderSystem will break, or perform. This is not possible, or more difficult, if you use singletons directly inside of "RenderSystem".

Now it is more testable, modular, and it's also decoupled from a specific implementation. It's only dependent upon an interface, not a concrete implementation.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.