I’ve now completed some additional engine work and implemented some architectural changes. The “Engine” is now decoupled quite a bit from the other components. So at the moment I have:
- SDLGLHandler which manages the creation and management of the SDL_Window* and the SDL_GLContext* . Essentially this does the buffer swapping, it also applies a spaceengine::PostProcessingEffectChain to the buffer before being swapped to the window.
- PostProcessingEffectChain which is made up of a series of PostProcessingEffect s. This applies things like bloom and the real-time lens flare and bokeh. This is currently handled by external SpaceFormatFiles.
-
SpaceEngine and a
GameObject class which are closely linked. Basically, the
SpaceEngine object holds an
SDLGLHandler and a
GameObject* . When the
SpaceEngine instance is started, it throws the
GameObject s
Start method into a new thread and then drops into a standard SDL message pump.
The only message explicitly handled is SDL_WINDOWEVENT_CLOSE , everything that’s input related is thrown to the InputManager .
After processing the messages in the queue the draw method of the GameObject is called followed by the swapping method of SDLGLHandler .
For it’s part, the GameObject is an abstract class. When using my engine, you need to derive your own game class from GameObject and implement the drawing method and update method. You also need to register your own callbacks via the InputManager . This GameObject features a single std::mutex as all methods need to be thread-safe.
- A static
InputManager class. This class features methods to push events onto a queue and to register callback functors that will be called if a specific
SDL_EventType is processed.
The InputManager finally has a Start method which creates a new thread to process the internal queue, looking at each SDL_Event.type and calling the correlated functors from the registered callbacks.
I’ve gone with a multi-threaded approach for a couple of reasons. Firstly I wanted to learn how to do it. Secondly, I anticipate needing the decoupled processing to handle the various aspects of the simulation.
I’m pretty happy, aside from any exception handling, the main function looks like this:
1 2 3 4 5 6 7 8 |
int main(int argc, char* argv[]) { spaceengine::SpaceEngine spaceEngine(glm::ivec2(SCREENWIDTH,SCREENHEIGHT)); SpecificGame game(&spaceEngine); spaceEngine.HookGame(&game); spaceEngine.Start(new spaceengine::PostProcessingEffectChain(glm::vec2(SCREENWIDTH,SCREENHEIGHT),"../../Assets/postprocesschain.json")); exit(EXIT_SUCCESS); } |
SpecificGame is derived from GameObject . You can see how both the SpaceEngine and the GameObject hold a pointer to each other. This allows the Engine to ask the game to quit as well as allow the game to ask the engine to quit.
My reasoning for this is that the game window could be closed, which I consider an engine responsibility, vs game specific menu options which would also need to close the game. I believe I could manually push the window close event onto the queue from the game if I needed to; but I think this method works well enough as it doesn’t require the game to suddenly need to be cognisant of SDL2.