HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Use of a static class for a game engine

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
enginegameforuseclassstatic

Problem

I've been designing a game engine for the past few months with little issues. However, one main goal with this project is to put it on my portfolio/résumé so naturally I want the code to be as good as possible. With that in mind, I got a little worried today because I designed a fair amount of my engine around using static classes. However, I always read that statics are bad, evil, and break OOP among other negative things.

Is my use of the static classes in my game bad design that should be changed (despite working, and personally finding it easy to understand and intuitive) or is it acceptable use? Keep in mind I want employers to look at this project as an example of my programming ability, which is why I'm asking this about my specific situation, in addition to reading all the related posts as possible.

For an example of a static class, I have my Game class:

```
#ifndef _GAME_H_
#define _GAME_H_

#include "common.h"

class btDiscreteDynamicsWorld;

class Game
{
friend class Drawing;

public:
// Initializes the game by creating the application window, initializing OpenGL, and starting the main game loop.
static void initialize(std::string gameName, HINSTANCE hInstance);

// Get the handle to the Windows based application window.
static HWND getAppWindow(void);

// Returns the dynamic world (physics world) used by the game.
static btDiscreteDynamicsWorld* getDynamicsWorld(void) { return sDynamicsWorld; }

// Returns the time, in seconds, that it took the last frame to fully execute.
static float getTimeElapsed(void);

// Sets the game's active status to either true, or false (not active). The game and its application window will not update.
static void setActive(bool active);

private:
// Creates the game's application window.
static bool createAppWindow(void);

// Creates a temporary fake window for the use of GLEW to load OpenGL's functions
static HWND createFakeWindow(void);

// The main ga

Solution

Yes, I do believe that this is a bad practice. And as a presumptive employer I would be worried and put your application towards the bottom of the pile.

What you are doing is essentially a singleton pattern but you're not treating it as such. The singleton pattern is one of the most basic design patterns (and probably most frequently abused). This tells me you have limited knowledge about design patterns and code design.

You are also just taking procedural code and trying to make it look like it is object oriented while it really is not. Which tells me as an employer that "you just don't get object oriented programming".

The exact same API and features could have been implemented with a namespace like this:

namespace Game{
    void initialize(std::string gameName, HINSTANCE hInstance);
    ...
}


and you would have all the private members from your class as globals in the cpp file. This is the exactly the same thing as what you're doing. You're abusing a class as a namespace. While I do not condone this particular design for the problem at hand, the namespace approach would have placed you a bit higher up in the pile of applicants, because at least you're not abusing object orientation and I can accept the namespace approach as a convenient design decision.

Had you instead properly used the singleton pattern where it is suitable, like this:

class Game{
public:
    void createWindow(const std::string& title);
    ....

    static Game& instance(){
        static Game game;
        return game;
    }
private:
    Game();
    Game(const Game&) = delete;
    Game(Game&&) = delete;
    void operator=(const Game&) = delete;
    void operator=(Game&&) = delete;
};

int main(int, char**){
    Game::instance().createWindow("My Game");
}


You would have soared to the top of the pile of applicants.


Addressing "bad OOP design": I am using C++ which is obviously OOP,

Let me stop you right there buddy! Just because you are using a C++ compiler, doesn't mean your code is object oriented. Lots of C, and procedural code compiles perfectly fine under C++. What the language is and how you use it are two entirely different things. Just because you can use a wrench to hammer a nail, doesn't make it a hammer.


...but why does that mean everything needs to follow perfect Object
Oriented design? Not everything in a program needs to be an object in
my mind.

Not everything needs to be object oriented. But don't use a class if you are not writing object oriented code.


For example, the game itself is an object, but there can only
ever be one of them (And it will only ever be "created" once),

This my friend is what singletons are for.


so what could be the advantage of making it a class that can be instanced?

The singleton pattern as I implemented it has well defined construction and destruction which means that all code paths that exit the program normally will invoke the destructor and perform clean up. You do not need to call deinitialize/shutdown explicitly to clean up.

The standard guarantees that memory for class/struct members will be laid out contiguously in memory which is good for your cache hit/miss ratio. Granted that the compiler may likely place your statics contiguously as well, this is not guaranteed though.

If you at some point decide to allow multiple game instances, say you make a multiplayer server, object oriented code is much easier to adapt.


With multi-threading aside (I don't know how that applies to static
classes, but I think I read that it has issues with it), how could
forcing a class to always exist, and to only ever exist once bad
design?

Again it is not bad design but use the correct pattern for it. And yes, statics tend to complicate things when you start multithreading but in your case it is no less complicated if using the singleton pattern.


To me, it's the same as doing Game* game = new Game(), and
never deleting, or creating another Game, however with my design it
has the advantage of not giving the user the ability to recreate the
game (Which is a feature I want).

The singleton pattern has all those properties with the benefit of automatic destruction when your application terminates by normal means.


So, how can making an "object" be an
object that has to exist, that can't be explicitly deleted or replaced
bad design.

As I said, it is not bad design per say. Although it makes testing in isolation with unit tests difficult. Masquerading procedural code as a class is a big no-no for me.


In a literal interpretation of my Game class, why would I
ever want to create another Game when one is already forced to exist?
I say specifically talk about create because I feel that's the only
advantage of a class that can be instantiated over a static class.

Code Snippets

namespace Game{
    void initialize(std::string gameName, HINSTANCE hInstance);
    ...
}
class Game{
public:
    void createWindow(const std::string& title);
    ....

    static Game& instance(){
        static Game game;
        return game;
    }
private:
    Game();
    Game(const Game&) = delete;
    Game(Game&&) = delete;
    void operator=(const Game&) = delete;
    void operator=(Game&&) = delete;
};


int main(int, char**){
    Game::instance().createWindow("My Game");
}

Context

StackExchange Code Review Q#77513, answer score: 8

Revisions (0)

No revisions yet.