So with all the current stuff in the news about Unity/Improbable and the counter offers from unreal, and the big epic vs steam thing going on… I think there are probably a few developers out there who think to themselves… ‘I wish I *did* have my own engine, then I wouldn’t have to worry about ANYBODY else’s code. The problem is… if you are used to unity or similar systems, you might have no idea where to start right?

The windows basics:

To have an app that runs under windows you basically need just two functions, WinMain and a Windows ‘procedure’ function that handles windows message. A stripped down winmain looks like this:

int APIENTRY WinMain(
                     HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR pszCmdLine,
                     int nCmdShow)
MSG msg;

gInstance = hInstance;
GetGame()->InitApp();

//main program message pump loop//////////////////////////////////
while(1)
{
if( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) )//any for us?
    {
    if( !GetMessage( &msg, NULL, 0, 0 ) )//if so get em
        {
          return msg.wParam;
        }
    else
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
else
    {
        if(BActive)
        {
            GetGame()->GameProc();
        }
        else
        {
            Sleep(1);
        }
    }
}
return msg.wParam;
}

The only two exiting parts are InitApp() where I create all the Directx3D stuff, and load in any data I need, and GameProc() which is basically the games main loop. The window proc looks like this:

LRESULT CALLBACK WindowProc(HWND hWnd,
                                 UINT uMsgId,
                                 WPARAM wParam,
                                 LPARAM lParam
                                 )
 {
 switch (uMsgId)
     {
     //main switch statement for handling messages
     case WM_DESTROY:    //end the application
         PostQuitMessage(0);
         GetGame()->ReleaseResources();
         return 0;
         break;
default://default behaviour
        return DefWindowProc(hWnd,uMsgId,wParam,lParam);    
      }
}

Again the only exciting thing there is ReleaseResources() which basically closes down all that directx stuff and releases all the textures and any memory I allocated.

meanwhile inside that InitApp() thing I need to do this:

int width = GetSystemMetrics(SM_CXSCREEN);
int height = GetSystemMetrics(SM_CYSCREEN);

Which gets the desktop res for me to create a window, and show it to the world:

gWnd = CreateMainWindow("Production Line", gInstance, IDI_ICON1, IDI_ICON1, WindowProc, width, height, bwindowed, bborderless, 0, 0);
UpdateWindow(gWnd);
ShowWindow(gWnd,1); 

After that I initialise all the directx stuff, which I have written about a decade ago, its pretty easy to grab all that from the directxSDK. BTW that CreateMainWindow function is mine too, and looks like this:

HWND CreateMainWindow(char* appname,HINSTANCE hInstance,int IDI_SMALL_ICON,int IDI_TINY_ICON,
                       WNDPROC proc,int width,int height,bool bwindowed,bool borderless,int left,int top)
 {
     WNDCLASSEX wcex;
HICON icon = NULL;
HICON iconsmall = NULL;
if(IDI_SMALL_ICON == NULL)
{
    icon = (HICON)LoadImage(NULL,"data/icon.ico",IMAGE_ICON,0,0,LR_LOADFROMFILE);
}
else
{
    icon = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_SMALL_ICON), IMAGE_ICON, 64, 64, 0);
    iconsmall = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_TINY_ICON), IMAGE_ICON, 32, 32, 0);
}

wcex.cbSize           =    sizeof(WNDCLASSEX);
wcex.hInstance        =    hInstance;
wcex.lpszClassName    =    appname;
wcex.lpfnWndProc      =    proc;
wcex.style            =    CS_CLASSDC | CS_DBLCLKS;
wcex.hIcon            =    icon;
wcex.hIconSm          =    iconsmall;
wcex.hCursor          =    NULL;
wcex.lpszMenuName     =    NULL;
wcex.cbClsExtra       =    0 ;
wcex.cbWndExtra       =    0 ;
wcex.hbrBackground    =    (HBRUSH) GetStockObject (BLACK_BRUSH);

RegisterClassEx(&wcex); 

int flags = 0;
if(bwindowed)
{
    if(borderless)
    {
        flags  = WS_POPUP;
    }
    else
    {
        flags = WS_OVERLAPPED |  WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_BORDER;
    }
}
else
{
    flags  = WS_POPUP;
}

HWND gWnd = CreateWindow(appname,appname,flags,
    left,top, width, height,
    NULL,//GetDesktopWindow(),
    NULL, hInstance, NULL);

return gWnd;
}

TBH the only fiddly bit is finding a program to actually create a decent windows ico .ico file in all of its myriad different sizes. Plenty of them exist, but its something you probably did not realise you need. I also have to use a batch file and a little command line thing to stuff the finished icon into the exe, so that it shows up in windows explorer as well as when the window is launched (weirdly these are different things…).

Thats pretty much all of the ‘initialisation’ code you need to create a windows app. Obviously you then need an actual graphics engine, but thats not *too bad* to write yourself if you are not coding something that is huge and requires amazing performance or tons of shaders.

In 2D, everything is pretty simple. You just need a sprite class (I have a Base Sprite, which is just 4 vertexes and a way to manipulate them by scaling, changing UV values, positioning them etc), maybe a textured sprite (the same but with a directx/opengl texture pointer), and if you want to optimise stuff you need a system that handles vertex buffers.

In general I just have one (big) vertex buffer that I stuff with sprites during the main game render loop, and then call draw() on it every time I need to change the texture or one of the render states.

Input stuff is pretty simple. You should just hook into the windows WM_LBUTTONDOWN messages and others like WM_CHAR, and do some processing on them. Way simpler than using middleware for all that. If you need to know the current state of a key you can use this:

bool CBaseInputManager::KeyDown(int key)
{
bool down = (bool)(GetAsyncKeyState(key)& 0xFF00);
return down;
}

I *do* use middleware for sound, but you can get pretty affordable, pretty simple sound middleware from lots of places these days. I have not updated my sound middleware for a LONG time. I don’t do any fancy sound processing so why would I? Playing an ogg file or streaming one…is not complex.

Now obviously if you have never coded outside unity, then there is a LOT of stuff you take for granted that you would have to now write some code for, but you only need to learn these things once. Loading in a file, or browsing a folder for files, is just a few lines of code. Even writing an absolutely bullet proof ini file loader that operates efficiently and correctly and without bugs is only 913 lines of code by my count, and thats a LOT of whitespace, helper functions and wrappers around it.

If you start writing an engine early, when you are still doing hobby games, preferably simple ones, you will find it quite easy to scale it up as you make more complex games. The code samples in this blog post have been stripped of their error checking, and some game-specific checks and extra processing to make them clear on first reading.

Basically every game I make involves me adding some new functionality to my engine. Production Line was the first game where I had to create an isometric object class, and isometric renderer (basically just a system to sort by Z and then render…), an animation compression system and some metric reporting and user-survey stuff, but that was probably just adding an extra 3-4% to the size of the total engine.

I wrote the multi-threading stuff ages ago (Democracy 3 if I recall), although I seriously improved it this time. The original windowed GUI system was coded for Starship Tycoon (OMG). Most of the directx stuff comes from Gratuitous Space Battles 2. The current text renderer dates back to Democracy 2 for its initial version. The vertex buffer code started in GSB 1.

Don’t get me wrong, there is a LOT of code in an engine, and it DOES take a fair bit of time, but its a big, long term investment that definitely pays dividends. I don’t have to worry if unity supports X or Y, or if it conflicts with Z, or if they are going to remove it without warning next week (or break it). All the code works in a style I like, and with absolutely zero bloat.

The source folder for my engine is 100 files and 620k total. Its not *that* big. Obviously it compiles pretty quickly and easily.

…and don’t forget you can still code using an IDE (Like Visual Studio) with built in code syntax highlighting and intellisense (I recommend visual assist!). Do not fall into the trap of thinking its unity OR just typing in a command line window without any help!

8 Responses to “Writing your own engine for 2D games in C++: basic considerations”

  1. Guntha says:

    I couldn’t agree more: I worked in a studio that ported several old games using an in-house engine before having to move to Unity for “bigger” projects and, to this day, they can still port the in-house engine games with a minimal amount of work on new platforms (it was originally for releasing games on iPhone 3G, now some of them are on the Nintendo Switch. Even switching from OpenGLES to the Switch’s own rendering API was not overwhelming!). On the other hand, only updating Unity can break projects using an older version of it, and you need to hunt for everything in the game that may have broken.

    You could argue that a few years earlier, you HAD to do your own engine because Unity and other engines ready-to-use and cheap engines were not a thing yet, but today, it is also easier than before to build an engine for which you control all the source code! For example, instead of buying a sound middleware or creating your own, you can use SoLoud if you don’t need crazy performance, people who were used to SDL can now use SDL2 to get hardware acceleration, and even adding a debug GUI is now super fast and painless with dear imgui. The only hard thing now is that it’s pretty much impossible to debug reliably hardware-accelerated rendering…

    One argument I hear from people who prefer using Unity is that they don’t want to have to manage memory by themselves… But using an engine, you still at least have to deal with fragmentation anyway, or make a game static enough that there isn’t really any memory to “manage” at all, so I don’t know what to answer…

  2. cliffski says:

    Wow do people really use unity because of memory management? Thats funny. I think one approach to memory management is not cramming your game with a ton of code that you never use in the first place :D
    Plus how difficult is it really to write your own memory defragger, or new allocator if you really need to? Don’t people get taught object re-use and creation factory techniques any more? :(

    • d.zoid says:

      I’m using unity because of fast results, but not memory managment (that is unimportant thing). I think it’s a common cause.
      I’m agree Unity has a lot(!) of disadvantages, but for example if you are making own game engine for mobile you have to deal with two rendering backends (OpenGLES, Metal) that is threatens me to not follow that way.
      When you go on the enthusiasm you have to iterate quick to stay on goal and any complexity can kick out indie out of buisness, writing your own game engine is one of them.
      But still I like the post), i think about the subject all the time but not so confident in shifting.

  3. Cygon says:

    That’s… surprisingly old-style code for a seasoned programmer – no unicode, no const-correctness, globals and singletons.

    For non-Windows platforms, do you switch out the whole WinMain and window setup code, calling those GetGame()->InitApp(), GetGame()->GameProc(), GetGame()->ReleaseResources() methods from a different startup/window setup implementation?

    I’ve walked the same path until around 2005 but decided to go with third-party engines when the effort of staying up-to-date with 3D tech became too much for me. I’ve since reallocated the time I’d spent on engine code to do my own art and animations :)

    • cliffski says:

      I am super old school. Sadly I started PL without unicode because my existing engine is MBCS and not unicode (although the democracy 3 engine has been converted, i wasn’t able to wait for the conversion before starting PL).

      I don’t have multi-platform plans for PL right now, but 99% of my singletons wouldn’t be affected. Only the 3D and sound engine ones.

    • CdrJameson says:

      I’m sort-of going the other way.

      I’m using Cocos2d-x for a multi-platform engine, because it was a quick way to get things working on all platforms I need to support.
      As time goes on I’m slowly replacing the generic, engine-y bits with my own systems. So far sound, networking, video decode & input are all custom and I’m thinking next about replacing OpenGL (glfw) with DirectX. The common-denominator approach works up to a point, but eventually you need to get platform specific.
      I have to say though I do appreciate their UI widgets. It’s much under-appreciated how much faff goes into just dealing with title screens and options menus if you have to do it from scratch in C++. Putting a UI together in a few lines is worth many gold stars.

      • cliffski says:

        yeah I do find that coding GUI stuff in my engine is slower than something like unity, but then the final code output is likely way slimmer and faster, and its still only a very small part of my dev time.

  4. Vas says:

    Wow. It is indeed looks super old-school.
    However boosted with your game design and developement on purpose – reaching market and sales – skills = the key of success.
    Well done!