IronHammer Engine

Introduction

IronHammer started as a question: what does a game engine actually look like under the hood?

I have experience working with commercial engines like Unity and Unreal. While I can use them comfortably and work with their built in systems, I always wanted to understand what is actually happening beneath the surface. It is easy to attach a collider component and have collision magically work, but what is that magic, exactly? This project is my attempt to find out by building those systems myself from scratch.

The result is a complete 2D game engine and editor built without any framework or scaffolding, only libraries for windowing, graphics, and UI. Every architectural decision had to be made deliberately, from how thousands of game objects are stored in memory, to how the editor converts between three different coordinate systems. The engine is still actively in development. This page is a record of the systems built so far and the thinking behind them.

Entity Component System

The ECS is the foundation that everything else is built on. Rather than storing game objects as individual classes scattered across memory, IronHammer groups entities that share the same set of components together in one contiguous block, an archetype. Within each archetype, all transforms are packed together, all sprites are packed together, and so on.

This layout, known as Structure of Arrays, means that iterating 10,000 entities to move them is a single sequential read through memory, rather than 10,000 random jumps between objects. Modern CPUs are designed to handle sequential reads efficiently, so the difference in performance is significant and measurable.

Custom Memory Allocators

Most programs rely on the operating system to hand out and reclaim memory, a process that works well in general but has measurable overhead when done thousands of times per frame. IronHammer implements three purpose built allocators that pre-claim large blocks of memory up front and manage them internally, keeping allocations fast and predictable.

Physics Pipeline

The physics system runs in three separate stages each frame. Splitting it this way keeps each stage focused and independently testable — a design decision that paid off significantly during debugging.

Rendering System

At the beginning of this project the one area where I felt most lacking was graphics programming. I decided to use SFML's graphics module, which is built on top of OpenGL, to handle simple rendering of sprites, shapes, lines, and text. The key point is to ensure efficient rendering when entity count grows and draw calls increase. The rendering system avoids issuing one draw call per entity by accumulating all geometry into a single vertex array and flushing it in one call, or as few calls as possible. For sprites, a new flush only happens when the texture changes.

Sprite geometry is computed manually per entity: rotation is applied using a precomputed cosine/sine pair, UV coordinates are mapped from each sprite's texture rectangle, and the result is written directly into the batch. No intermediate objects, no allocations per frame.

Multithreaded Debug Logger

When something goes wrong in a game, knowing exactly where in the code it happened saves hours of debugging. IronHammer's logger captures a full stack trace on every log call. The problem is that translating raw memory addresses into readable function names and file paths is extremely slow. Doing it on the game thread would cause a noticeable frame drop on every log.

The solution is to split the work across two threads. The game thread captures the raw trace instantly and pushes it to a queue. A dedicated background thread picks it up, resolves the symbols at its own pace, and places the finished result in a buffer. At the end of each frame, the main thread moves everything from the buffer into the log window.

Editor & Tooling

IronHammer ships with a fully integrated editor built using Dear ImGui. The goal was a complete authoring environment where game worlds can be built, simulated, modified, and saved without leaving the application. All editor panels share a single context object — no global state, no singletons, passed by reference to every panel that needs it.