The Singleton in action - the Application class

The Singleton pattern achieves its ability to be accessible anywhere easily by having a special function that we use to get the Singleton object. When this function is called, we will check whether that object has been created yet. If it has, then we will simple return a reference to the object. If not, we will create it, and then return a reference to the newly created object.

Now, in addition to having this way to access it, we also want to block off our user from being able to create them, so we will need to define our class constructors to be private.

Now that we have an understanding of some implementations of the Singleton, we have one other version, which is what we actually used within the Mach5 engine.

In Mach5, the only Singletons that are included are aspects of the engine code. The engine code is designed to work with any game, meaning there is nothing gameplay-specific about it, which means that it doesn't need to have instances since they're just instructions. Building the engine in this way makes it much easier in the future to bring this to other games, since it's been removed from anything that's game-specific.

In this case, let's open up the M5App.h file which is in the EngineTest project under Core/Singletons/App and take a look at the class itself:

//! Singleton class to Control the Window 
class M5App
{
public:
friend class M5StageManager;

/*Call These in Main*/

/*This must be called first, before the game is started*/
static void Init(const M5InitData& initStruct);
/*Call this after you add your stages to start the game*/
static void Update(void);
/*Call this after Update is finished*/
static void Shutdown(void);

/*Call these to control or get info about the application*/

/*Use this to change to fullscreen and back*/
static void SetFullScreen(bool fullScreen);
/*Use this to show and hide the window*/
static void ShowWindow(bool show);
/*Use this to show and hide the default window cursor*/
static void ShowCursor(bool showCursor);
/*Use this to change the resolution of the game*/
static void SetResolution(int width, int height);
/*Returns the width and height of the window (client area)*/
static M5Vec2 GetResolution(void);

private:
static LRESULT CALLBACK M5WinProc(HWND win, UINT msg, WPARAM wp, LPARAM lp);
static void ProcessMessages(void);

};//end M5APP

Now, the Mach5 engine follows the Singleton pattern. However, it is done in a different way from the others that we've looked at so far. You may notice in the class definition that every single function and variable that was created was made static.

This provides us with some unique benefits, namely that we don't need to worry about the user creating multiple versions of the class, because they'll only be restricted to using static properties and variables that are shared by everything. This means we don't need to worry about all of those fringe cases we mentioned in the previous examples that we've seen. This is possibly due to the fact that the Mach5 engine classes have no need to have child classes; there's no need for us to create a pointer or even call a GetInstance function.

You'll also notice the Init, Update, and Shutdown functions mentioned previously. We mentioned before that it was a disadvantage to manually have to create and destroy our singleton classes, but there are some distinct benefits to having this control. In the previous examples we had, the order in which classes were created was up to the compiler as we couldn't control the order. However, with our game engine it makes sense to create our Application (M5App) before we start up the graphics library (M5Gfx) and the only way we can make sure that happens is by telling our engine to do so, which you can look at if you open up the Main.cpp file and look at the WinMain function, which is what opens first when we create our project. I've gone ahead and bolded the uses of M5App:

int WINAPI WinMain(HINSTANCE instance, 
HINSTANCE /*prev*/,
LPSTR /*commandLine*/,
int /*show*/)
{
/*This should appear at the top of winmain to have windows find memory leaks*/
M5DEBUG_LEAK_CHECKS(-1);

M5InitData initData; /*Declare my InitStruct*/
M5GameData gameData = { 0 }; /*Create my game data initial values*/
M5IniFile iniFile; /*To load my init data from file*/

iniFile.ReadFile("GameData/InitData.ini");
iniFile.SetToSection("InitData");

/*Set up my InitStruct*/
iniFile.GetValue("width", initData.width);
iniFile.GetValue("height", initData.height);
iniFile.GetValue("framesPerSecond", initData.fps);
iniFile.GetValue("fullScreen", initData.fullScreen);

initData.title = "AstroShot";
initData.instance = instance;
/*Information about your specific gamedata */
initData.pGData = &gameData;
initData.gameDataSize = sizeof(M5GameData);

/*Pass InitStruct to Function. This function must be called first!!!*/
M5App::Init(initData);

/*Make sure to add what stage we will start in*/
M5StageManager::SetStartStage(ST_SplashStage);

/*Start running the game*/
M5App::Update();
/*This function must be called after the window has closed!!!*/
M5App::Shutdown();

return 0;
}

Afterwards, we can look at the Init function of M5App and see that it will initialize the other Singletons in our project:

void M5App::Init(const M5InitData& initData) 
{
// ...
// Other init code above...

M5StageManager::Init(initData.pGData, initData.gameDataSize, initData.fps);
M5ObjectManager::Init();
M5Input::Init();

}

By having this control, our users have a much better idea as to the flow and order that things will be created. But, of course, with that great power comes great responsibility.

The Singleton pattern is used only for single-threaded applications. Should you be developing a multithreaded game, you'll want to use the Double-Checked Locking pattern instead, which was created by Doug Schmidt and Tim Harrison. If you're interested in learning more about it, check out https://en.wikipedia.org/wiki/Double-checked_locking.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.97.170