Refactoring: making the game engine more modular and how

My game engine consists of a number of loosely coupled modules that can be loaded and unloaded.

Some examples: the base module, processing window management and response to OS events, entity manager, Lua manager, physical manager.

Currently, these modules are organized as namespaces, and their status is determined by local variables in the corresponding source files. Each of the namespaces has Open (), Close (), and Update () functions.

Now I don’t really like the namespace solution.

  • It's not flexible enough

  • Even if this may not be necessary in reality, the simple possibility of creating multiple instances of the module seems correct

  • It seems that I am not using OOP here - the base class of the module with the virtual member function Update () will sound more reasonable

  • It is more difficult to make sure that when the module is closed and reopened, all variables will also be reset (a class with constructors and destructors will be easier)

  • You cannot manage modules correctly without explicitly calling Open (), Close (), and Update ()

So, my idea would be to use classes for each of the modules derived from the base class of the module. Instances of the module class will be handled by the ModuleManager class, which updates them.

But a solution with OOP raises the issue of how modules should interact. At the moment, the base module told the console module to print something using console::print()

  • How can I get around this problem without using something like g_ModuleManager.GetConsoleModule()->print() ?

  • How can this module manager work in detail?

And my last question:

  • Do you have additional tips on writing a modular game engine in C ++ using OOP?

  • Are there any design patterns that could help me in this situation, or perhaps even specific reading material?

+7
source share
3 answers

Namespaces become very inflexible very quickly.
One way to keep things together is to use messaging through a centralized message manager; instead of saying console::print , you would say messenger->sendMessage(PRINT_MESSAGE, stuffToPrint) .
The console would register as a listener on PRINT_MESSAGE and act as it wants. The sender does not need to worry about someone listening, and there may even be several listeners for each message (useful for debugging).

Regarding reading materials, Jason Gregory's “Game Engine Architecture” is pretty good, discussing the pros and cons of several architectures.

+2
source

At first, as a general reminder, just remember to use inheritance as a replacement, not as reuse code. Roughly this means that everything that inherits from your base module should support (basically) the same operations, and if the user says open , close or update on any module, it will provide the expected results.

Do you really need a module manager? Perhaps it would be better to draw a diagram of the relationships between your various classes / modules and create a connection there, rather than a top-level manager. Then the main application loop will know about one or more high-level modules and immediately call the appropriate methods. If desired, there may even be a high-level module to manage this high level of delegation.

For your console example, assuming you don't want to support multiple consoles, you probably better just keep the namespace: it gives you the ability to directly refer to the only console you are interacting with. Alternatively, you may need to pass a “parent console” to each module you create and use the appropriate console to perform your I / O operations. This gives you more flexibility by making sure you need to support the console in the base module.

+1
source
 g_ModuleManager.GetConsoleModule()->print() 

simple, as stated above, you will most likely have to reorganize a lot of things in order to take advantage of OOP. You want each class to have a distinctive relationship, and not all on an equal playing field. Anyone who says that the ConsoleModule for printing should have its own print function, which tells the ConsoleModule to print. So you just say print()

also think in terms of "has" for composition and "eat" for inheritance. I would read the entire section 8: http://www.learncpp.com/cpp-tutorial/81-welcome-to-object-oriented-programming/

As an example, my current project: I have a board that "has" beatles , walls and apples . Each one of them is an icon

0
source

All Articles