Engine Start Up + Shut Down + Design

Started by
4 comments, last by thaler.jonathan 13 years, 1 month ago
Hi, I've been reading Game Engine Architecture (very good btw) and was thinking of writing my own stripped down and very simple game engine. I'm now considering how to structure the "base" of the engine as it were.

I imagine I will have classes running various subsystems which will need to be created/initiated in a certain order. Typically these would be singletons but then you get all the associated difficulty with start up and shut down orders so I was thinking that a "runtime singleton" solution would be best - using an assert in the ctor to prevent more than one instance being created. This seems to be the technique employed by OGRE and it seems to work quite well. Obviously runtime checks are worse than in compile time but in this case it seems a reasonable solution as programmers would rarely try and new an instance of a large subsystem and they would quickly realise their mistake (hopefully!).

These subsystems could be members of a Root class (again like OGRE) which could potentially be static. Then we'd have a controlled start up and shut down order (with all subsystems initialised in the ctor and destroyed in the dtor of Root) but with no dynamic allocation. In the event that some subsystem(s) failed we might have to have an init and shutdown class in Root but that should not be a problem.

Does anyone see any flaws in this design and are there better ways of doing this? Should I perhaps not allow global access to the subsystems and have some as private members of Root and others as public? Thanks in advance!
Advertisement
It depends a bit on what you understand being a sub-system. I'm using sub-systems to abstract OS dependent stuff: Video, Audio, Graphics, Sounds, Storage, Input, ... and these sub-systems does know nothing from each other (if possible). E.g. On MacOS X I may have Core Graphics and X11 for Video, OpenGL for Graphics, Core Audio and OpenAL for Audio and Sounds, and so on. Each sub-system is represented by an object from which at most 1 exists in a registry, where each sub-system kind has its own registry. E.g., if compiled with support for X11, both CGVideo and X11Video can be found in the registry for Video instances. (A registry itself is a pure singleton.) The overall system makes suggestions which sub-system instance should be used by default for each kind of sub-systems, e.g. CGVideo and OpenGL for graphics rendering on MacOS X, but the OS unaware program only sees a Video and a Graphics instance for them. However, such sub-systems are then grant access to devices, e.g. to Video::Display, Input::Joystick (although abstracted using HID like stuff), Storage::FileSystem, and so on.

Another use for the term "sub-system" is in the context of a CES, where the components of a specific sub-type are collected and concentrated in "sub-system". They can be singleton like for the case that they must exist only one, of course. In my engine, however, I can have more than a single scene at a time, and hence cannot use them like singletons. Instead they are anchored in a Scene object, and I named them "services" (e.g. spatial services) to distinct them form the aforementioned sub-systems.

At least, there is also a way of not enforcing a particular initialization order although objects depend on others (as long as no real circular dependency exists). I'm using it for the sake of plug-ins. When a new plug-in gets registered, the plug-in registry invokes PlugIn::install() and is then able to request how many installments are not successful. E.g. a particular plug-in was able to install 2 out of 3 things. The next plug-in may provide stuff that was missed by the former one. So after each install, older plug-ins may now be able to complete, and hence each plug-in that still answers "I'm not completely installed" is invoked once again to install now. If none plug-in installed something new in a round, then the iteration terminates.

It depends a bit on what you understand being a sub-system. I'm using sub-systems to abstract OS dependent stuff: Video, Audio, Graphics, Sounds, Storage, Input, ... and these sub-systems does know nothing from each other (if possible). E.g. On MacOS X I may have Core Graphics and X11 for Video, OpenGL for Graphics, Core Audio and OpenAL for Audio and Sounds, and so on. Each sub-system is represented by an object from which at most 1 exists in a registry, where each sub-system kind has its own registry. E.g., if compiled with support for X11, both CGVideo and X11Video can be found in the registry for Video instances. (A registry itself is a pure singleton.) The overall system makes suggestions which sub-system instance should be used by default for each kind of sub-systems, e.g. CGVideo and OpenGL for graphics rendering on MacOS X, but the OS unaware program only sees a Video and a Graphics instance for them. However, such sub-systems are then grant access to devices, e.g. to Video::Display, Input::Joystick (although abstracted using HID like stuff), Storage::FileSystem, and so on.

Another use for the term "sub-system" is in the context of a CES, where the components of a specific sub-type are collected and concentrated in "sub-system". They can be singleton like for the case that they must exist only one, of course. In my engine, however, I can have more than a single scene at a time, and hence cannot use them like singletons. Instead they are anchored in a Scene object, and I named them "services" (e.g. spatial services) to distinct them form the aforementioned sub-systems.

At least, there is also a way of not enforcing a particular initialization order although objects depend on others (as long as no real circular dependency exists). I'm using it for the sake of plug-ins. When a new plug-in gets registered, the plug-in registry invokes PlugIn::install() and is then able to request how many installments are not successful. E.g. a particular plug-in was able to install 2 out of 3 things. The next plug-in may provide stuff that was missed by the former one. So after each install, older plug-ins may now be able to complete, and hence each plug-in that still answers "I'm not completely installed" is invoked once again to install now. If none plug-in installed something new in a round, then the iteration terminates.


Thanks for the reply. :) For subsystems, yes I was meaning things like audio, input, events, graphics... etc.

I'm not sure what CES is but the "services" you mention are not really what I'm thinking about when I say "subsystem" or "manager".

For the plugins example - I think I'll try and avoid having anything apart from linear dependencies (i.e. subsystem A starts, then B, then C etc) between my subsystems as well as otherwise it will just be confusing (and slower at start up). I mean, I will know in advance what the start up order should be so it seems to make sense to enforce that someway...


Thanks for the reply. :) For subsystems, yes I was meaning things like audio, input, events, graphics... etc.

I'm not sure what CES is but the "services" you mention are not really what I'm thinking about when I say "subsystem" or "manager".

For the plugins example - I think I'll try and avoid having anything apart from linear dependencies (i.e. subsystem A starts, then B, then C etc) between my subsystems as well as otherwise it will just be confusing (and slower at start up). I mean, I will know in advance what the start up order should be so it seems to make sense to enforce that someway...


CES = Component Entity System

For what it's worth, I have graphics, audio, networking, and physics each tucked away in their own static lib, and another lib dedicated to math, which the graphics, sound, and physics are dependent on. The 'engine' core, is built on top of(is dependent on) these modules, as another static lib.
Aha! I did know what a component entity system was I just didn't know/remember the name of it.

Ok after reading some other threads, I'm thinking of using an abstract factory pattern to create my subsystems to allow for future platform independence. Yes I know this is over engineering but I find it quite interesting and it will be fun anyway. :)
I don't think the abstract factory pattern is over engineering in this case, especially for creating subsystems. I do create some componeonts in my engine with this approach and it perfectly fits.
Another approach for subsystem-creation would be to load them from dynamic libraries during runtime and using an XML-Configuration which defines the order, the path and the id of the subsystems. Of course then you'll have to implement a loading/instantiation mechanism for components in dynamic libs but that should be no problem. With this approach you have to design very carefully your interfaces because each component must not have dependencies upon others.
The convenient thing with this approach is that subsystem loading is no more hardcoded in c++ but rather a declarative thing.

This topic is closed to new replies.

Advertisement