Skip to main content
GameDev.net gamedev.net
🔒 Locked

Modular System

Started by cozman Feb 17, 2005 at 2:40 PM 5 replies 1.5k views
Original Post
cozman
cozman
I'm working on a project, and I'm seriously questioning my design. This is going to be used by people other than myself, so I'd really like input on what most people seem to like. I want to somewhat abstract away any underlying APIs I use so that it isn't difficult for me to switch them, or if someone needs a custom implementation of a particular portion, they can write it themselves. I don't plan on using DLL/shared objects because I am keeping this cross platform, and it really isn't needed, a recompilation is fine. Strategy 1 was to have a variety of "Cores" which is similar to a system I used before. For example I'd have a VideoCore/AudioCore/InputCore/TimerCore/etc. The issue here is that some of these systems rely on the same APIs, for example, at the moment I am leaning towards GLFW, which can handle the window creation, timer, and input. If I were to switch to SDL, it too would handle all of those. I then considered the following Strategy 2: An Application class, which defines the interface for an application (window creation/input/timer would exist within the application class). A switch from SDL to GLFW would then be possible by writing two versions of the Application class. Applications using my system would define a class :

class MyGame : public Application 
{
public:
    int main(ArgList args);
}
and everything could work. This works nicely because the Application constructor/destructor can handle init/shutdown code. The issue here is that certain classes are going to depend upon some of the core subsystems. For example, the Timer class would obviously depend on the TimerCore. For this reason something resembling a singleton seems to be needed. Does anyone know of a pattern that is more suited to my problem than a singleton, I prefer to avoid them, and it just doesn't seem optimal.
LostSoul78
LostSoul78
Here is my solution:

For every Core there is an abstract base class which defines the API. For instance: AudioCore defines the interface (play, stop, setvolume etc). These functions are implemented in derived classes like OpenALAudioCore or DirectSoundAudioCore.

The application class is a singleton. It does, however, have Factory Methods for each Core, like CreateAudioCore() or CreateVideoCore(). The application class creates all cores, initializes them and allows access to them.

You derive your Game application from the application class and overwrite some or all of the factory methods. That way you can implement custom Cores and the application base class can still use them.
cozman
cozman
This is very similar to what I was initially thinking, however I have encountered a few problems.

For instance, how does one optimally deal with things which need access to the Core's but do not know about the Application class? (ex. Timer needs access to a 'TimerCore')


AudioCore is fairly independent of the other cores, but in choosing APIs generally one API will cover Window Creation/Timing/Input, be it system native, SDL, GLFW or something else.

I am considering the following:
AppCore (takes care of the above things, window creation,timing,input)
VideoCore (takes care of setting perspective, etc)
SoundCore (takes care of setting listener position, etc)

Application contains instances of all 3, and all 3 are singletons.

Also, making abstract classes for the Core's isn't actually that useful, because at any given time only one will be implemented in the library. Instead I am opting to simply define the API in the header file, and an implementation in the source file (AppCore_glfw.cpp, AudioCore_oal.cpp)
kaysik
kaysik
Quote:
Original post by cozman
For instance, how does one optimally deal with things which need access to the Core's but do not know about the Application class? (ex. Timer needs access to a 'TimerCore')


Why do you have a time class if you have a timer core? Whats the point of the core if you've got timer classes as well - one of them seems redundant. If you just make the time core and abstract base class, then write your specific cores as sub classes everything should work out fine. Your timecore has something like startTimer(int ID, int delay), stopTime(int id), getTimerTime(int ID) and resetTime(int ID). You have no need for a timer class and its easy to create a new subclass for a different API/platform.
Penance
Penance
I'm working on a similar design for my game engine.

Basically what I came up with was a centralized Core API that all other modules would have to know about. Within the Core I define a Msg class abd handle important things like timing.

The Core is only responsibile for discovering, loading, and managing modules and interpreting/executing messages for itself and/or dispatching them to other modules.

Basically, if two modules want to access Core functionality, they know how, because they'll have included the Core API via a DLL. If modules want to interact with each other, then it's up to the developers to agree on an API for those modules.

So say I have Scripting, Networking, and Core. Scripting and Networking know about Core. But say Networking wants to load and execute a script. Networking sends a Msg to Core with a destination of "Networking", Core dispatches the message to Networking and Networking interprets the contents of the Msg.
kaysik
kaysik
Quote:
Original post by Penance
Networking sends a Msg to Core with a destination of "Networking", Core dispatches the message to Networking and Networking interprets the contents of the Msg.


"Scripting sends a Msg to Core with a destination of "Networking", Core dispatches the message to Networking and Networking interprets the contents of the Msg." perhaps?
LostSoul78
LostSoul78
There are two possible ways to access the Cores without knowing the application class:

1. If you are sure that there is never going to be more than one instance of each core at a time (which sounds reasonable to me) then make them singletons.

There are several ways to implement singletons, though. One way is to create the instance in the GetInstance() method if it does not exist yet; this has, however, the severe disadvantage that you cannot be sure in what order the Cores will be created and you do not get the chance to initialize them in an orderly fashion.

Another way would be to have a public constructor which assignes itself (this-pointer) to the instance variable and asserts() that it is 0 at the time. That way the application could create the Cores and they would still be accessible as singletons.

2. If there can be more than one Core at a time then you need to pass pointers to classes that need the Cores, i.e. the constructor of your Timer class would require a pointer to the Timer Core. That way you would need to know the application in order to create an Timer but the Timer itself would know nothing about it.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.