The most common criticism of HGE is that it is Windows-only. That is fair. The engine was built around DirectX, Win32 windowing and Windows audio APIs. But the engine being Windows-only does not mean the game has to stay that way.
Porting an HGE game to Linux and macOS is a proper engineering job, not a weekend tidy-up. It is doable, but the work is in the plumbing: rendering, windowing, audio, input and file handling all need attention. The payoff is that you end up with a clearer understanding of what the game actually depends on.
What changes first
HGE’s platform dependencies fall into four groups.
Rendering: DirectX needs a replacement
This is the biggest piece. HGE uses DirectX 8/9 for all rendering, and Linux and macOS do not provide DirectX. You need another backend.
OpenGL is the most practical choice for broad compatibility. Linux supports it natively, and macOS supports it through a compatibility layer, although Apple has deprecated it. Vulkan is a better long-term bet, but it is much more work to implement. It is available on Linux natively and on macOS through MoltenVK. SDL2 plus OpenGL is another common route, because SDL handles window creation and OpenGL context setup while you deal with the rendering layer itself.
Windowing: Win32 has to go
Win32 window creation, message pumps and input handling need replacing. SDL2 is the standard answer because it covers window creation on all platforms, input for keyboard, mouse and gamepad, audio device management and OpenGL context creation. That is most of the unglamorous work done in one place.
Audio: swap out the Windows-specific bits
HGE’s audio depends on platform-specific APIs. SDL_mixer is the simplest replacement for most game audio. OpenAL is stronger if you need positional audio. miniaudio is another option: lightweight, single-header and portable.
File I/O and system calls
Windows-specific path handling, file operations and system calls usually port cleanly once you move to standard C++ filesystem or a thin platform abstraction. This part is rarely the hard bit. It just needs care.
The main ways to approach the port
Strategy A: keep the API, replace the backend
This is the cleanest route. Rework HGE’s internal implementation to use cross-platform libraries while leaving the public API intact. Your game code stays put; the engine internals change underneath it.
That means reimplementing Gfx_* functions with OpenGL instead of DirectX, Input_* functions with SDL2 input, Stream_* and Effect_* functions with SDL_mixer, and System_* functions with SDL2 windowing.
The upside is obvious: existing HGE game code can compile and run against the new backend without a broad rewrite. The downside is that this is a serious amount of work, and some HGE behaviour is tied to DirectX assumptions such as blend mode constants and texture formats.
Strategy B: put a thin abstraction above HGE
Another route is to build a small abstraction layer that the game targets, with one implementation for Windows using HGE and another for Linux and macOS using SDL2 and OpenGL.
Your Game Code
│
└── Platform Abstraction Layer
├── HGE Backend (Windows)
└── SDL2 + OpenGL Backend (Linux/macOS)
This keeps HGE where it already works well on Windows, while leaving the other platforms free to diverge where they need to.
Strategy C: move everything to SDL2/OpenGL
If you are going to rewrite the rendering layer anyway, it may be cleaner to migrate the whole game away from HGE and onto SDL2/OpenGL on every platform. You give up HGE-specific conveniences, but you get one codebase instead of two.
The HGE sprite concepts - texture regions, hot spots, colour modulation - translate neatly to OpenGL textured quads. The API changes, but the mental model carries over.
Getting the project ready
Separate the platform code first
Before touching cross-platform code, go through the project and split out the pieces that are genuinely platform-dependent.
Keep pure game logic together: physics, AI and game state should not need HGE calls if the code is structured sensibly. Then isolate rendering calls, input handling, audio calls and system or lifecycle code such as initialisation, shutdown and timing.
If the codebase is in decent shape, most of the game logic should stay untouched and the platform-specific code should be concentrated in a few files. If it is not, this is where the mess shows up.
Set up SDL2 on Linux and macOS
Start with a basic SDL2 window and an OpenGL context on the target platforms.
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);
SDL_Window *window = SDL_CreateWindow("My Game",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
800, 600, SDL_WINDOW_OPENGL);
SDL_GLContext ctx = SDL_GL_CreateContext(window);
That replaces HGE’s System_Initiate() and gives you a surface to draw on.
Port rendering after the window exists
This is where most of the work lives. For each HGE rendering concept, implement the OpenGL equivalent.
Texture loading usually moves to stb_image or SDL_image instead of HGE’s Texture_Load. Sprite rendering becomes textured quads in OpenGL, using the same coordinate system your game expects. HGE’s blend mode constants need to be mapped to OpenGL blend functions. Render targets become OpenGL framebuffer objects.
Translate input into your own abstraction
SDL2 input events should feed a game-specific input layer, not be scattered through gameplay code. SDL2 gives you cpp
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_KEYDOWN:
// Map to your input system
break;
case SDL_MOUSEMOTION:
// Update mouse position
break;
}
}
, which is enough to cover the usual cases without inventing your own platform layer from scratch.
Replace audio with SDL_mixer or something similar
SDL_mixer is the most direct substitute for HGE’s audio stack.
Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048);
Mix_Chunk *sfx = Mix_LoadWAV("laser.wav");
Mix_PlayChannel(-1, sfx, 0);
Mix_Music *music = Mix_LoadMUS("theme.ogg");
Mix_PlayMusic(music, -1);
Set up cross-platform builds
Use CMake for the build system.
cmake_minimum_required(VERSION 3.20)
project(MyHGEGame)
if(WIN32)
# HGE backend
find_package(HGE REQUIRED)
target_link_libraries(${PROJECT_NAME} hge hgehelp)
else()
# SDL2/OpenGL backend
find_package(SDL2 REQUIRED)
find_package(OpenGL REQUIRED)
target_link_libraries(${PROJECT_NAME} SDL2::SDL2 OpenGL::GL)
endif()
How to check the port is holding together
Watch for visual mismatches
The most common porting bugs are visual. OpenGL’s Y-axis is inverted compared with DirectX. Texture filtering defaults are different. Blend modes do not always behave exactly the same way. Line rendering width and antialiasing vary too.
Screenshot comparison tools that diff the Windows and Linux outputs pixel by pixel will catch problems that are easy to miss by eye. That is tedious, yes, but so is chasing a one-pixel shift through half the render code.
Profile performance on every target
Do not assume cross-platform means equal performance. Test each platform on its own terms.
Linux OpenGL drivers behave differently from Windows DirectX drivers. macOS’s deprecated OpenGL implementation has its own performance characteristics. Frame timing also shifts depending on the compositor and window manager.
Test the feel of input on real hardware
Controller behaviour, mouse acceleration and keyboard handling all differ between operating systems. Use actual hardware on each platform, not just a desktop shortcut and a hopeful attitude.
What I would do in practice
For an existing HGE game that needs Linux and macOS support, Strategy B is usually the best balance of risk and reward. Start with Linux, because the development workflow is closer to Windows. Use SDL2 for the platform-specific parts. Port rendering last, after input, audio and windowing are working. And accept some visual differences - pixel-perfect parity is not worth infinite engineering time.
The HGE API documentation is still the best reference for what each function does internally, which is exactly what you need when reimplementing it elsewhere.
The work is real. So is the result: a game that runs on more than one desktop platform, built on code you understand instead of code you merely inherited.
Discuss porting strategies with other developers in our forum, or explore the HGE demos to understand the rendering features you’ll need to replicate.