4
\$\begingroup\$

I'm dabbling in writing my own somewhat basic game engine from scratch, and I wanted to ask about some common design configurations for connecting the various parts. I'm particularly interested in the relationship between the "engine" and the "game", and the various ways in which information and function calls cross this boundary (the "control flow", for lack of a better term).


Let me start by describing my understanding of the main parts:

  • Engine: The engine contains various subsystems related to rendering graphics and audio, inputs, physics solving, networking, memory management and asset/resource loading/unloading, etc. Subsystems may be encapsulated into individual classes or use interfaces in order to support a variety of platforms or libraries.

  • Game: The game contains a large and complex structure of "entities" which contain all of the data and functions that define the gameplay world and logic. Entities are also associated with visual representations, audio sources, physics bodies, and everything else that determines what the game is and how it is represented.


So, for example, this is my current understanding of 'control flow' when it comes to updating or 'ticking' gameplay logic (pseudocode ahead):

  1. Setup: Main() creates and initializes the Engine, which creates a context and initializes various subsystems. (For example, maybe setting up SDL/OpenGL/OpenAL/etc., allocated a big block of memory within the memory manager, etc.)

  2. Some kind of game loop will call an Engine::Update(), either at a fixed or variable timestep, maybe passing in some kind of 'deltatime'.

  3. The Engine contains a object/pointer/reference to a Game, and (if there is a game) will then forward its own updates to the game's Game::Update() function.

  4. The Game will then call Entity::Update() on each of its own Entities, and they will perform some game-specific, per-frame, actions.

This is pretty simple to understand, as it is a straight-forward chain of Update() calls starting from the main loop, then the engine, then the game, and finally propagating all the way down some structure of entities (and/or components) until everything in the entire system has performed it's per-frame logic. This is a 100% linear relationship, and as such each piece only needs access (pointer/reference) to the next piece in the chain.

Is this a mostly correct understanding of how logic flows between pieces of a game? Are there other common ways of handling this (for example, Main updates Game directly and Engine is used as a service by Game for other things?)


Where things seem to get more complicated and where I get more confused are situations where it seems like everything isn't 'flowing in one direction', as it happens with the logic flow described above. For example, here's my understanding of the "rendering flow".

  1. Setup/Initialization, same as before.

  2. Game loop calls Engine::Render() at some timestep, possibly in sync with the Update() but not necessarily.

  3. At this point, the Engine could call Game::Render() and this call could be passed down to individual Entities that are responsible for rendering themselves. However, then this seems to create a problem where rendering-specific code is now seeping into the "Game Space", and your game's code is getting all mixed up with D3D or OpenGL calls! I don't think that's what we want, Right? As such, my instinct here would be to have the Engine request all of the "renderable" Entities from the Game (for example, Game::GetRenderables() fucntion).

  4. After the Engine has pulled all of the "renderable" Entities from the Game, they are then passed into the the IRenderSystem::Render( IRenderables ).

At least, that's my current understanding of how the control flow works when it comes to things like graphics rendering, audio rendering, physics solving, etc. In these situations it seems desirable to encapsulate these tasks within their respective engine subsystems, so that as much of the related code as possible exists in a single place. Of course the Game world and Entities determine the content of the game, including what needs to be seen, heard, collide, where it is, what it looks like, etc. But it should be the Engine that is responsible for actually taking those things from the Game and rendering/solving/processing them.

Is this a decent high-level understanding of the rendering flow between the Engine and the Game? Is this general design suitable for audio and physics systems as well (Engine collects 'primitives' from Game and delegates processing to subsystems)?


Finally, in my design, where the "Engine has a Game" as a "Console has a Cartridge"; updating the game's logic and collecting of all of its renderable/physics primitives each frame. I've run into a situations where the Game depends on the Engine for certain tasks. For example, the creation and destruction of certain Entites/Components/Assets!

It makes sense to me that the Engine should be responsible for allocation and deallocation of system resources (like memory) as well as loading assets from files. As such, in order for the Game to add a Sprite or Mesh, for example, to an Entity, it must be able to request the creation of a Mesh component (or something similar) from the Engine. The Engine will then create that object using a custom allocator, load the asset from a file, do any other preparation, and then deliver the result to the Game.

But now this seems to complicate the relationship between the Engine and the Game. It used to be that the Engine 'owns the Game', updates it, and retrieves primitives from it, while the Game had no knowledge or dependency on the Engine. But now the Game needs to be able to call functions on the Engine as well. Now there are a few ways to resolve this; making the engine a singleton, creating back-pointers to the engine, etc. But I was thinking about a solution where the Engine passes a reference to some kind of EngineUtilities object into the Game and its Entities that gives them some kind of limited interface for calling Engine functions. Kind of an attempt at dependency injection, I guess.


Anyway, I realize how big this question is, but the gist of it is this: What are some common designs for connecting the various parts of a game engine and what are the pros and cons of different approaches? How does the flow of logic updates differ from the rendering flow, physics solving, object creation, etc.? Which approaches are used by popular game engines (Unreal, Unity, IdTech, CryEngine, Source, Godot, etc.), and have popular approaches changed over time?

\$\endgroup\$
2
  • \$\begingroup\$ What makes you think that the game should not be dependent of an engine? How many engines are you using with one game? Always a single one. A singleton is therefore acceptable. \$\endgroup\$ Commented Oct 9, 2017 at 20:48
  • \$\begingroup\$ Just to clarify a bit, It's not that I'm necessarily opposed to the game being dependent on an engine, it's more that having a cyclic dependency between Engine <-> Game seems like a sloppy design. Of course, if it works it works, so maybe I'm overthinking it. A 'global' engine singleton is definitely a possible path, because as you said, there really is only ever one engine at a time. My only concern about that is exposing the Engine's entire interface to the game, in the even that the Engine has a tick() that calls a tick() within the game, for example - could ignore it, but it feels sloppy. \$\endgroup\$ Commented Oct 14, 2017 at 23:53

1 Answer 1

1
\$\begingroup\$

I give it a try.

What are some common designs for connecting the various parts of a game engine

The most commonn that come to my mind are

  • BLOB (Bloated Object): one GameObject hold references to every systems and has in its interface methods called by every systems (DoPhysics, DoLogic, DoRendering,...) and can't change
  • Component Systems: GameObjects are split into "nearly" independent components where each component (in a perfect world) would only relate to on subsystem. GameObjects use collections/arrays so you can add components and systems as you wish, even at runtime
  • The third on in the middle: Aggregation (the game object holds only the references needed) but can't change during runtime
what are the pros and cons of different approaches?

Well, this answer fills books, theses, papers, blogs,.... and depending on the person you talk to this can be somewhat religous.

They discuss factors like

  • performance vs flexibility
  • flexibility brings overhead
  • reusability
  • modularity/granularity
  • more modular approaches require more coding time putting things together
How does the flow of logic updates differ from the rendering flow, physics solving, object creation, etc.?

You mentioned you split up everything into subsystems (one for each of audio, physics, rendering,....) so I guess they share a common interface. This leads to the answer that they do not differ basically. Though all have different needs to handle their tasks. So they all share that they are called (in a speciefid order, and this is important) in the same way (Update = DoRender, DoPhysics, DoSound) and then do their tasks.

Which approaches are used by popular game engines (Unreal, Unity, IdTech, CryEngine, Source, Godot, etc.),

Years ago (haven't checked since a time):

  • Unreal: BLOB
  • CryEngine: BLOB (I guess)
  • Amazon Lumberyard: (once I read they bought a CryEngine license to port it to Component Based)
  • IdTech: if they kept the style since Quake they have everything optimized for performance to the nightmare of every software architect. I remember everything was tied to everything, no coupling at all.
  • Source: most of the older engines use BLOB so I guess this too
  • Unity3D: the star amoung the component based

You can learn a lot about that stuff taking a look into open source game engines on sourceforge, github.

and have popular approaches changed over time?

Yes.

At the beginning there was the need for massive performance tuning (ID Doom, Quake). Everything was tied together with "no clear border" between game and engine. For a new game one had to touch "the whole engine".

Then object orientation brought the BLOBs. Adding more functionality to the engine meant that GameOjbect interfaces and member lists were growing (DoRender, DoAudio, DoPhysics) and every game object holds a reference to every system.

Later on a popular way to get all the reference handling easier Dependency Injection came up.


Is this a mostly correct understanding of how logic flows between pieces of a game? Are there other common ways of handling this (for example, Main updates Game directly and Engine is used as a service by Game for other things?)

Yes. One can either hardcode the gameloop and the call order or make a list/array of subsystems held by the gameloop and it will call "some kind of" update() every frame in the given order. No. Since the game loop knows the subsystems and the order it is not needed that every subsystem knows the next element in the queue.

Two interesting points I want to mention is the importance of the order in which they are called (which depends on your needs) and more important that there are two types of subsystems. In a need for a better naming I seperate them into active (do their stuff only when update is called) and passive (do their stuff immediatly).

The example I use to describe the need is as follows: Imagine a networked game with a server and a client. The server sends a message "Create GameObject X". On the client side some network logic receives this message. This message comes only once. So on reciving the logic calls something like GameObjectManager.CreateGameObject(X). The logic knows it has to do more with the game object than just creating it. E.g assign a player to it. So the logic calls something like GameObject x = GameObjectManager.GetGameObject(X). If the gameobject manager creates its objects only in update (since it is the only safe point in time where no other systems interfere with the game object list) this will not work because the GameObjectManager.CreateGameObject(X) haven't created the game object X yet and therefore GameObjectManager.GetGameObject(X) will return null in the network logic.

Hope you understand what I mean.


Is this a decent high-level understanding of the rendering flow between the Engine and the Game? Is this general design suitable for audio and physics systems as well?

Yes, it is a decent high-level understanding of one approach. Nothing against doing it that way but there are other ways may are closer to what you desire.

To your game code getting mixed up with D3D or OpenGL calls: It shouldn't be. Define what stuff you have to render: Models, Animations, Textures, Texts/Fonts,... and provide in the render engine according methods like RenderModel(...), RenderTexture(...), RenderText(...) Then you can either call those methods from the GameObject.Render() or the game object holds it's Renderables which are returned in GetRenderables(). So your game object doesn't even need to know if e.g. RenderModel(...) uses D3D or OpenGL. The code is cleaner and shorter.

This is the way you can design all of your systems. Either the gameobjects holds a reference to the (render) engine as well as all required data and does its stuff like void DoRender() { renderEngine.RenderModel(_myModel); } or the gameobject provide IRenderables.

(Engine collects 'primitives' from Game and delegates processing to subsystems)

When it comes to stuff like render order I assume personally I would tend to an approach where the render engine holds a collection of IRenderables and the game object injects them during creation or logic state. So the render engine already knows all IRenderables when Engine.Render() is called and can sort it dependend on the it's needs. But this is only my personal preferance and I guess this is the most common approach in component based systems (I tend to use). The drawback is that each gameobject with muliptle components is in multiple growing lists. And it has to be added (on creation) and removed (on destruction) from all of the lists which can become a performance peak.


Ah, this was the block with the cartridge.

The first thing I am not sure if I understand is your seperation between engine and game in the sense of the engine using the game like a cartridge and who owns whom.

My personal approach of understanding the relation between those two is that the game is the master and it utilizes an/the engine. The engine is a framework, a collection of libraries, etc... or in short a tool helping your game. The game can't live without the engine but the engine can life without the game. The game controlls the engine, knows what to do and uses the engine to do it.

In your understanding of the "cartridge" you are looking for a data-driven engine design. If gameobjects aren't defined in code but in Json, XML or some self defined format the engine could load it from a predefined location (as well as the other assets like models, sounds, textures) and knows what to do. But this is only true if you also have some external scripts (Javascript, LUA) so you can controll the engine from it and implement your actual game logic. Therefore the scripting engine has to have access to your well defined engine API.... and so on


If you can (at least) read Java you could take a look into the Java Game Components. Some stuff I have written one.

\$\endgroup\$
5
  • \$\begingroup\$ Not to discredit Monty, but I think this answer could be improved. First, it explains too broadly the main frameworks. I myself am having the same problem as the questioner and the broad strokes outlined by Monty aren't enough to solve the problem. This could be solved with some links to resources on the architectures if available. Second, the question about how the flow of the main engine is different from the graphics pipeline seems like the crux of the question and too little time is spent explaining the way they differ and how the control flow of an engine is supposed to go. \$\endgroup\$
    – Althis
    Commented Nov 6, 2017 at 19:43
  • \$\begingroup\$ @Althis I absolutly agree with your opinion. This is a very broad answer. Because it is a very broad question :-) But I offer answers (as far as I can give answers) to every question you want to ask if we find some more suitable communication format than StackOverflow :-) And giving links reference is hard due to the amount of them. And the "game engine bible" is a 900 page book which may or may not suite your needs. \$\endgroup\$
    – monty
    Commented Nov 7, 2017 at 13:21
  • \$\begingroup\$ Just for curiosity: you both are using c++ or do you use any other language for your engine? I am just thinking if your questions would be a great starting point for a game engine (1.01) blog. \$\endgroup\$
    – monty
    Commented Nov 8, 2017 at 6:43
  • \$\begingroup\$ in my particular case, performance isn't my target this time, so I am writing an engine in Python. I would love to help elucidate my problem, and would greatly enjoy reading your blog, but I agree that Stack isn't the place.(These days it rarely is) As such I've made a gamedev reddit post that should be a better place for such a question. \$\endgroup\$
    – Althis
    Commented Nov 8, 2017 at 20:38
  • \$\begingroup\$ @Althis well, i am not using reddit and not (yet, I plan to learn it) using Phython. But you can drop me a mail at (5m0_4yu4jd6phfqq [at] byom [dot] de) and we can start talking via mail :-) (I am sure you know how to replace [at] and [dot] for a valid email. Don't mind the strange e-mail adress, its scrapper protection. \$\endgroup\$
    – monty
    Commented Nov 9, 2017 at 14:07

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .