Open strangezakary opened 4 years ago
I have a few scattered and perhaps opinionated thoughts without much cohesiveness. These are my initial feelings and are subject to change, and I don't mean to suggest this is the right way of doing anything, just that it's my first gut impression:
The engine should be very careful about making assumptions. It's really easy to start coupling the game very tightly with the engine—and by extension constraining the game's design—if the engine starts deciding things like how game entities should be stored and managed; in fact I argue that such an architectural decision would be a mistake.
The engine should, by default, provide implementations and APIs for a set of low level problems that are generally useful for the subset of games we'd like to support. So, for example, it makes sense to globally (for all games) provide things like memory allocators, input, opening a window, allocating the memory block to sub-allocate from, getting an audio output buffer set up, etc.
For all engine problems that belong to a narrower subset of the games we want to support, we can introduce decoupled pieces for those. For example, let's say we want to support 2D games, but also would like the engine to be able to be used for 3D games also. We might decide to write a 2D renderer. And while the 2D renderer can assume the presence of things globally available in the engine (memory allocators, input, a window, etc.), the converse wouldn't be true here. So theoretically it'd be possible to use all of the "global engine stuff" without the 2D renderer. The 2D renderer is just an example here... see next bullet point.
Non-comprehensive list of decoupled pieces that can sit on top of the platform layer/allocators/input/etc. code, without being required when using the engine: Physics, networking, 2D rendering, 3D rendering, scripting language, profiling tools, UI
Overall I'd say that this is a good way to not constrain the design of games. It would still allow for freedom in the game layer, but for the subsets that the engine supports, the game code has a solution for those spaces, and provides APIs for them.
HMH style hot-code loading would be cool but I am not partial either way. Unless we're doing something like that, I vote that we don't split the build in any way. It's just more complexity than it's worth.
So, in terms of the actual hard-line abstraction cuts we make, I think it should be:
Platform layer, hard-line. Abstraction layer by the globally-available engine stuff, provides simple API (just a struct with data + function pointers?) that just passes necessary stuff to the game layer.
Various modular engine APIs, hard-line. Keep engine flexible by only making really big assumptions in modular areas. This would simplify the APIs too--if we don't have to worry about 3D graphics in the 2D renderer, but can swap the 2D renderer out, then the 2D rendering API can be much more specific, and more useful for gameplay code.
All engine code included with game projects? Keep build self-contained + non-breaking forever, until the game person manually integrates new changes?
Again, scatter-brained and just initial instincts, to throw some thoughts in here. :)
How do we wanna handle abstracting the platform layer, engine, and game from each other? Obviously they all need to talk to each other but how do we go about preventing coupling. Ideally maybe they all should even be able stand on their own.