Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I am creating a 2d platformer using SDL and I was thinking that my game object could be static, but I wasn't sure if this was a good idea.

The pros (that I can see):

  • Accessing settings options (such as screen size and keyboard bindings) would be easier accessed
  • There should only ever be one main game loop, so this makes sure for me.

The cons:

  • From what I've heard, static classes in C++ are a bear to work with
  • I've read that this may cause problems later in development (things don't work right or can't be used properly
share|improve this question
5  
If you absolutely must have a one-shot object make it a singleton instantiated by main(). Static objects have weird initialization rules, exist before main() and generally make debugging more complex. There are a ton of other good reasons dealing with architecture and design practices that other people will fill you in on, the first you can count on being debated is whether singletons are evil =) –  Patrick Hughes Jul 7 '12 at 5:08

4 Answers 4

up vote 6 down vote accepted

A singleton for your main engine class is perfectly normal.

It's even quite acceptable to have one singleton for each of your game's major systems, like graphics and input. I personally prefer a single Engine singleton with all systems as members of that object, but really there's very little difference.

A singleton can just be a static global object, although its usually more of a special kind of class with a static accessor method. E.g.

class Engine {
private:
  Engine();
  ~Engine();

public:
  static Engine& Instance() {
    static Engine engine;
    return engine;
  }
};

There are of course a million variations on that theme. In particular, some singleton implementations allow or even require explicit calls to construct or destruct the object (useful if you put each system in its own singleton and need to deal with dependencies between them). You can make a reusable templated version. You can make the singleton a separate object from the class being used as a singleton. And so on.

share|improve this answer
    
you can still copy this object, add declarations for a copy constructor and assign operator in the private sections (it's kind of just a detail, but we're talking c++ here specifically) –  dreta Jul 7 '12 at 6:11
    
Note that this is not threadsafe. (You never claim that it is, but it's a common pitfall and source for bugs in singletons, see how difficult this is: oaklib.org/docs/oak/singleton.html ) –  Roy T. Jul 7 '12 at 8:12
    
@RoyT. ugh, does that apply only to the object creation with "new", because the way it's done here and the way i do it is just by using a static, that whole "new" thing in this article seems just weird and unnecessary –  dreta Jul 7 '12 at 8:46
2  
This does not work well, if you need a proper shut down sequence of your program, especially if you have more than 1 of this type of classes –  Maik Semder Jul 7 '12 at 9:31
    
@MaikSemder you just use functions for startup and shutdown and call them in main, this really isn't that complicated –  dreta Jul 7 '12 at 10:05

I would recommend against the singleton, as it's become quite the anti-pattern. A better solution, in my eyes, would be to use the service pattern. Here you have a service locator that is available to everyone. (OK so that might have to be a static singleton depending on your code). Other objects query the service locator which then provides one. For example if I'm looking for the GraphicsDeviceService I query the service locator for an IGraphicsDeviceService. It will then return an existing current instance or construct one (Factory pattern) if there is no matching service available.

Now this is of course more complex than just using singletons, but the big benefit is that implementations of the service ,can change on the fly. Here is a sample implementation: http://www.blackwasp.co.uk/ServiceLocator.aspx

share|improve this answer
    
I agree, this is what Xna uses and it works without ugly static classes. –  Kikaimaru Jul 7 '12 at 9:16
    
It is what XNA uses, but I think many people find it cumbersome and end up not using it :P –  David Gouveia Jul 7 '12 at 9:27
1  
implementations of the service can change on the fly <- how often would you want to do that in a game? If you ever need to replace your game engine on the fly, you are probably overengineering your solution. –  Lie Ryan Jul 7 '12 at 10:04
1  
Lie Ryan: this is extremely helpful when debugging or prototyping new features you can see the difference at runtime and that is invaluable in game design. If you have an idea to improve your game but can't prototype it becomes a gamble. –  Roy T. Jul 7 '12 at 11:20

This is a general post about why static classes cause you more trouble than they are worth. And this includes singleton classes which are just a fancy way of doing the same thing that avoids construction order problems. No advice fits all cases, so take it as advice, not as an instruction that it is always better to do it this way.

In general code shouldn't reach out to get the data that it needs to provide it's job, it should be provided with that data.

I'll give an example from what you said -

**Accessing settings options (such as screen size and keyboard bindings) would be easier accessed**

So you write you main rendering loop and in there you call something like Game::getSetting(ScreenWidth);

That all works well and is nice and easy. Then one day you decide "wouldn't it be nice to let the player make screenshots. And it would be good to do them at higher resolution than the screen so they look good!". Now you have a problem. All your code depends on getting the screen size from the static getSetting function. If instead you'd provided your rendering code with the screen size instead you could simply call it with different parameters.

Another problem -

You write an amazingly cool user interface class for your game. It needs to know what font the user has chosen so you call Game::getSetting(UIFont) to get it. Nice and easy. But then you decide to write a separate game editor... Now in order to use your amazingly cool UI class there is a dependency on your static Game class so you need to drag in the whole of the Game class too... Which probably has dependencies on other components.

Sure you can change the code to work differently, but then you have two copies to maintain. If only you'd provided the font as a parameter to the class instead of letting it go get it itsself it would be much easier.

In general having functions go out and get the data they need to do their job introduces coupling between components and makes your code harder to maintain and change, and reuse, and you are better providing from outside the data that is needed either as a function call parameter or when you construct an object or whenever.

And the main advantage of static data or singletons is that they are easily located in random bits of code. If you don't need random bits of code to reach out to the static data then it doesn't need to be static any more...

Note that this doesn't mean you need to pass dozens of separate bits of data. You might create a group of related settings where you generally need all of them for related purposes and pass that. For example make a UserInterfaceSettingsContext structure and store the related settings needed to draw a user interface in there and pass an instance of that to anything that needs it...

share|improve this answer
2  
the "issues" you described are just bad coding practices, not the issue of the singleton itself, any patterns used poorly is going to turn against you –  dreta Jul 7 '12 at 8:57
    
+1 great answer –  Maik Semder Jul 7 '12 at 9:26
    
Indeed and I tried to say at the top that cases vary so you can't just say "this is bad" or "this is good". But that while singletons make things seem easy at first they often can lead to difficulties later on that might not be apparent if you've not used them a lot –  JohnB Jul 7 '12 at 9:27

The main problem with static and global objects in C++ is that they are created before the application's entry point (i.e. main), but their constructors are called in a completely unpredictable order. The same is true for their destructors, at the end of the application. This is clearly a problem because often we will have objects that depend on each other, and require a certain order of initialization and shutdown for the application to work correctly!

While you could use a singleton, which usually delays the construction of the object until it is first accessed, the destruction order will still be undefined, unless you make your singleton class more complex to handle that situation too.

On the book Game Engine Architecture they discuss a few alternatives to this, but it's the simple solution (in the sense that it uses no patterns, or any special control mechanisms) that they recommend the most. The book even states that this is the approach used in Uncharted for the PS3, so if it was enough for a huge AAA game like that, it will probably be enough for you too. Basically:

  • Leave constructors and destructors empty
  • Add explicit Initialize and Release methods
  • Creating global objects for your subsystems (since constructors/destructors are empty the order does not matter anymore)
  • Manually call Initialize and Release for each subsystem in the order you want

The main advantage is that besides being simple, you have complete control over the construction order, and most importantly, you can just look at the code and see in which order things are being created.

share|improve this answer
    
The dynamic initialization phase of static objects of file scope is not unpredictable. It is well-defined to be top-down within a translation unit, and is unspecified between translation units, but they will never be interleaved. Each TU initializes in an atomic fashion. Also note that you are guaranteed that the storage of all such objects is globally zero-initialized before the dynamic initialization phase, so you can reliably test whether a pointer has been initialized or not. –  Lars Viklund Jul 7 '12 at 10:23

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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