I have made a game engine in C++ before, but it was bad. The game logic was tied to frame rate, everything was. It could only run at one resolution, I had a central "Game" singleton, and used a lot of hacks to get different "game states" because I didn't know how to make a "room" sort of system seen in game scripting IDEs like GameMaker.

So I'm making another one, the difference is this time I want to actually see how it's normally done in professional (2D) game engines. I'm not looking for a game engine coding tutorial, I'm already capable of that, I'm more looking for various things to read about the structure and theory of making a 2D game engine, how all the pieces normally would fit together, how to make it fully object oriented etc. so could anyone please give me some things to read online about that? Thanks.

Though it's more for 3D engines, the book "Game Engine Architecture" by Jason Gregory has a lot of useful tidbits on designing the pieces and how they work together. Usually when it comes to these sort of problems however I tend to just hack away at it and make iterations that are better than their previous. Profiling the code and looking for the slowdowns in the frame rate will help you design around those problems. Though I admit that a DIY method would be a more time consuming route. Good luck on your adventures!

Try watching the Handmade Hero series. Casey's approach is performance oriented, from the OS API's upward, and you can skip over a lot of the programming details because of that, but the general thrust of it is rooted in a lot of shipping engine code, and it will give you the flexibility you're looking for.

Also, read more code. There are plenty of open source codebases. The ones derived from commercial games tend to look like a hurricane blew through them; the ones made entirely by hobbyists tend to be impractically rooted in some coding ideology. Pick the best parts of both.

Edit: And you don't have to necessarily add every feature from those engines. Once you hit a certain threshold of technology you can always stop and just start building the game. But since you're in this to make a better engine...

If you want you can also browse through the source code of a simple C++ engine I've been building and tinkering with. I'm not a professional programmer or anything but maybe you'll find something idk. SimpleGameEngine

My engine is not really professional (or finished) by any stretch of the imagination but it is a basic example of an Entity-Component system like the one used in the Unity game engine.

You could also just make a few games in the simplest 2d engines you can find (preferably open source) and takes notes about what was frustrating. When you're done, try and figure out (from the code) what caused your problem.

What I do is developing my games alongside my engine. I started with really small games and added new features along as I needed them, which made everything kinda messy. I wrote down everything that bothered me about my current engine architecture while developing the game. After finishing the game, I did some heavy refactoring, before I repeated the process again for another small game.

You should definitely checkout other game engines and frame works as well. It doesn't even have to be the source code. Using them for a test project and asking yourself why the devs have chosen certain approaches for certain problems is immensely helpful. Most of the design decisions come down to personal taste without a clear "Right" or "Wrong". Also, I would recommend you to also build games with your engine. Otherwise, the engine will be a single endless life long project, without any goal. Just keep growing your engine, while you are also growing as a developer. Learned a new design pattern? New language version? Can you apply them to your engine? There is always room for improvement.

I find equal fun in developing games, and developing game engines. I'm trying to create my own engine because each time of the many games I make, I keep having to do the same thing over, and over, and over, and over again, so I'm trying to make it easier to make games without making an engine every time.

I would recommend looking up the book Game Programming Patterns. Another thing that was that has helped me is how to create an finite-state-machine (FSM), with this you can manage game states and AI-states etc. Also I have heard good things about ECS (Entity-Component-System) but have yet to try it myself. Currently I am reading about how to use Lua for modding capabilites. Don't know if this was helpful or not.

This isn't direct advice about game engine architecture, but that statement suggests that you've heard that object oriented design is very important and must be considered carefully so that your code fully embraces object oriented principles otherwise everything will be terrible.

Pro tip: it's mostly bullshit. :)

Just write code that lets you do things flexibly and tends to prevent you from making errors and still runs fast. If some object-oriented design patterns help you achieve that, then cool. If not, then don't sweat it. Fretting over that stuff is a waste of time.

What's useful is ensuring that your various data structures don't have more dependencies (both implicit and explicit) on each other than necessary.

Also, if you say you are at the point of being able to do game programming, then go and read the source of some game frameworks and engines. Look at the source for FNA, Cocos-2DX, LibGDX, and SFML. Watch some Handmade Hero, and try to emulate structures from game engines you like (e.g. rooms from game maker).

GameMaker rooms are probably like scenes or states in other libs. A room is an asset file describing the objects that are created when that room is loaded. The objects are associated with that room (array, plus some lookup-optimization structures like hashtables or quadtrees) at runtime, and when a new room is loaded, all those associated objects get destroyed before the new ones are loaded in. It's really that basic, but considering you already can program games you know that the devil will be in the details of your particular implementation.