This guide is intended to help those seeking info on how to create a game engine from scratch using C++. The strategies used are from my own experiences creating a core engine as well as things that I picked up from my peers along the way.
There is no "right or wrong" way to write a core engine, but there are defintley development practices that can help make the path a smoother one. By no means is this a start to finish guide on how to write an engine but hopefully it can help those looking for some guidance. This guide is definitely flexible and can be changed in ways to meet different requirements.
Engine / Systems
First lets start of with the question of "what exactly is a core engine and is it necessary?"
Well the simple answer is that the engine is the underlying framework that ties everything in code together and lets a game run in an organized fashion as a single entity. As for the necessary part, absolutely; try making a full blown game with rendering in just a a few files and with little structure, and the limitations will quickly arise.
Let's start from the bottom of the tree and work our way up, since higher up nodes require/contain the nodes data below them. Components are the driving force of the game and define what makes one game object different from another. Lets start with the base component, of which our other components will inherit from.
We will go further into the game object class that is being used above soon but for now just understand that every component should derive from the above class and these components will be what is modified in the game by designers, either through a GUI/editor or possibly through a scripting language, and due to components being the underlying data of the game, there will likely be many of them created during the development process.
Here is an example of a simple transform component that defines the position and scale of an object and stores that data internally.
Next lets get into game objects. Game objects, as you can tell by the name, are the 'games' objects that are in the levels and are essentially defined by the multiple components attached to them. These components as previously mentioned will hold the objects data as well as decide how they will interact with other objects in the level. Lets take a look at a simple game object class:
And that's basically it! It's essentially a very simple container that holds components and initializes them then updates them every frame. There's many ways you can handle actually adding components to the object itself but in this example you would pass in a previously created component pointer and it would be added to the container with the objects other components.
Game States / Levels
Next up we have game states, also known as a level. These levels will basically be holding all the game objects that belong to them. They may also hold some data about the level that should be after and came before it so that the game state manager can use this information. These levels can also have names so that the game state manager can easily reference/find a level by its name. Here's an example of a Game State class:
Additional functionality you may want to add on top of this is storing cameras that belong to the level, and also storing states such as pausing and restart so that the game state manager can know if the level is being paused or is being restarted. The creation of game objects can also be done externally in a game object factory depending on if you want that functionality or not but in this example it is just being done in the game state itself.
Game State Manager
This will be the only major system I go over in this post because it is most relevant to what we have been talking about so far. The game state manager is more or less the main logic of the game loop. It will handle which level we are on and call the levels functions accordingly, most importantly the update and draw calls.
A base system class, not much to it
Every system will inherit from this class
The most important function of the game state manager is the update. This will be called every frame and will the the games "main loop". Here's some pseudo code on basically how it should work:
And finally we arrive to the overall engine. You may be noticing a pattern by this point, the next level up the tree is just a glorified container for the nodes below it. As for the engine its 'children' are the systems of the engine. I am not the biggest fan of globals or singletons but in this case the engine, being how high up and important it is to the program, suits a singleton well since we can guarantee that there will only be one 'engine'; we will most likely need to reference it in many places throughout our code.
If you want more information on singletons here is a good reference : https://www.codeproject.com/Articles/1921/Singleton-Pattern-its-implementation-with-C
Hopefully this guide was useful and while there is much more to add such as serialization and reflection, that will conclude it for this core engine guide. If you have any questions you can either comment or contact me via any of my social media on my contact page. Thank you for reading!