Separating my engine and game code, should the game or the engine contain main()?

Started by
10 comments, last by schen2000 7 years ago

So I'm writing my own engine for fun, and I want to have several games using this engine but I'm not sure where the program initialization code should live. Should it be present in each game project, then the game passes system events down to the engine? That seems incorrect to me, but if I have main in the engine, then how does the project know which game I'm trying to run?

I want to link my engine to my game statically, and I want my boilerplate application initialization code to be in my engine. But I want to be able to compile my games as separate executable that link against my engine. I'm unsure how to achieve this decoupling with all of these properties. Does anyone have any advice?

Advertisement
But I want to be able to compile my games as separate executable that link against my engine

Fine. In this case you have three stages of initialization:

  • The game-specific code in the "separate executable" does ad-hoc, game specific things and decides what the engine will do
  • The shared engine initializes itself with the requested configurations and variant behaviours
  • Game-independent code in the engine loads game-specific data files and possibly game-specific executable plugins and uses them.

The main choice is between putting game-specific content in the first stage or in the third stage, corresponding to the two architectural extremes of a bunch of libraries whose game-specific client code makes all decisions and a game allowing data-only mods without custom code.

A reasonable engine should evolve from a single game (in which the distinction doesn't matter) when it is refactored to share code with a second game.

Omae Wa Mou Shindeiru

it would seem that the location of the main game loop more or less determines whether its an "engine" or a "library". engines tend to have the main game loop in the engine - and use callbacks so you can add code at various "hooks" in the code.. games built from libraries have the main game loop in the game specific code and seldom / never require callbacks. Either way, the usual order of initialization is:

1. init engine/library. usually the first thing in main()

2. load game assets that use standard formats (meshes, textures, wavs, etc). IE stuff the engine deals with. This may be data driven and occur automatically as part of engine iniit. or it may be done on a per level or per terrain chunk basis.

3. load game specific assets that are custom to the game, perhaps a level map of some sort for example.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

One way you can do it:

- Shared engine code into a static library

- Dynamic link library containing the entire game code without any platform specific code, importing the shared engine library

- Main executable contains just the platform specific code + handling the loading of the game code dll and give the dll access to platform specific functions, like file loading and creation, memory allocations, etc.

This will support hot-load of code - meaning you can change the code, compile it and while the game is running you see the changes immediatly - given that you have good handling of game memory/state.

Another way is to use the exact opposite:

- Put the game code into a executable with all the game specific code in it and include the engine static library like you have suggested

- Full engine code in a static library and abstract away the platform parts, like entry point, window creation, event handling, rendering etc.

Oh and there is a really good book about game engine stuff -> Game Engine Architecture by Jason Gregory (Naughty Dog). There may be others as well, just search for it.

I'll just pop in to mention that the first approach Finalspace mentioned is the approach that Handmade Hero is doing, and the series is video documenting every step of the way.

https://handmadehero.org/

https://hero.handmade.network/episodes

devstropo.blogspot.com - Random stuff about my gamedev hobby

No, do not include your own main. What if someone wants to compile your engine as a lib and import it? For example to run the game engine inside of a level editor or UI. Maybe someone wants to run your game inside of wxWidgets. In this game wxWidgets needs to call a function and pass a function pointer, something like:


DoGame( user_data_ptr ); 

The best practice is to expose your engine through C functions, this way it is easy to compile the engine as a static library, or a dynamic library, or by copy/pasting source code directly into a project.

I put utilities in the engine that make it super simple to set up a main function, and then let the game do it.

As above it's also common to put the gameplay code into a DLL, so the exe containing main would basically just be a simple launcher.

FWIW, on almost all of the console games that I've worked on with proprietary engines, the game/engine exist in a single code repo, and the workflow for creating a new game involves making a copy of the most recent game that you've completed and then deleting all the systems you don't need. If the studio is working on more than one game, they perform periodic merging of the repos to propagate new systems and bugfixes :lol:

What Hodgman said is basically how ID tech worked (I don't know if the recent version continue on this road).
What you can do is to have one Game.exe compiled, and do all with loading of scenes and then you only have scene which launch script, all script based.
So basically you have a way to say the start scene and then all other scenes are loaded from this one using script and gameplay using script as well.

I have had a lot of research on this topic just before setting up my engine because I wanted the perfect repo structure, the perfect code clearness and perfect speed. Now I now that there isnt something perfect just that what you are willing to work with or not work with.

One of the results I'm very happy with is to compile anything from the framework/engine code into static linked libraries that has the advantage that you could easiely modulate your code. I have set up everything to be as most standalone as possible but also capable to use the full power of any module that exists. When a module uses functions from another one it is not a problem to make a static linked library from them because linker will assemble anything into the executable correctly (Tested in MSVC 2010/2015, GCC LLVM)

To setup a new project a need just a build file where I specify whatever module I want to use and run the tool from the engine directory that setups a VS project for it even when I have also seen a lot of copy/paste project setup or starting a project from a tutorial of the library we then used, I think this is something more clean to get it.

In my system I have a macro that setups the typical main call for targeted platforms requireing an init, main and cleanup function to be called. In the engine I'm currently working on there is a file Engine that contains static functions in a namespace that setup memory management and clears it after anything is finished running. These two functions are put into the macro so that


int main(Array<const char*> const& args)
{
  return Engine::Run();
}
__app_entry(Engine::Initialize, main, Engine::Cleanup)

is anything that the executable needs to call to get the game up working. The Run function then takes control over the OS specific message loop, queries main thread of the job system and registeres a small event callback to shutdown everything properly; thats it! And in my opinion it is enougth because anything else like loading config settings into the engine or register systems at the task manager has to be done before Run.

The rest is scheduled by the task manager when the system is setup and running regardless of if it is an engine system like rendering or something gameplay related like character controller.

I also decided a completely data driven approach like old TES Oblivion did where replacing the main .bsa file meant to have a completely new game (what some modders did) but the decision against it was simply performance related where having anything in scripts may be a critical impact.

I'm happy with it because it is as generic as possible and also as near to the engine code as needed. Maybe you might think that there is something that better fits your needs

My engine provides an Application object that does all the mucky Win32-ish stuff, configurable by what it is passed to its constructor.

WinMain is in the game, but it's only five or six lines of code usually.

Qt takes this approach too. Good system to my mind.

This topic is closed to new replies.

Advertisement