Hi, I'm *squid*

Building a DOOM-Like Engine in 2026: Discipline Through Constraints

Darklilly

I'm building (Dark) Lilly Engine, a 3D DOOM-inspired engine in C# with OpenGL. Not to clone DOOM. Not to romanticize the past. But because rebuilding foundational ideas until they're yours is the only way to truly understand them.


Sometimes the only way to understand a system is to rebuild it. Not copy it. Not romanticize it. Just rebuild the ideas until they're yours.

I'm not building a DOOM clone. I'm building an engine that grew from the same questions DOOM answered, under different constraints.

Today's hardware is absurdly fast, which makes it easy to drown in abstraction. But speed doesn't fix bad architecture, it just hides it.

A small, focused engine forces you to be explicit about what you care about:

The DOOM style (sectors, linedefs, portals) is still a great way to test the core of a renderer. It's simple enough to implement, but complex enough to reveal real problems: how you batch, how you stream assets, how you structure scene transitions, how you map inputs to actions. If the engine can handle that cleanly, it's probably solid.

So the point isn't nostalgia. (It's discipline.)

You can treat this kind of engine as a laboratory: tight constraints, explicit trade‑offs, no shortcuts. It's a reminder that graphics aren't magic—they're just careful data and a well‑timed pipeline.

That's why I'm doing it.

A Minimal System (On Purpose)

One thing I borrowed from classic engines is the idea that a "system" should be boring. No magic, no reflection, just explicit hooks in a predictable order.

Here's the base system every other system inherits from:

public abstract class BaseSystem : IBaseSystem
{
    public string Name { get; }
    public uint Priority { get; }

    protected EngineRenderContext RenderContext { get; private set; }

    public virtual void Initialize(EngineRenderContext renderContext)
    {
        RenderContext = renderContext;
    }

    public virtual void Shutdown() { }
    public virtual void Update(GameTime gameTime) { }
    public virtual void FixedUpdate(GameTime gameTime) { }
    public virtual void Render(GameTime gameTime) { }
}

It's simple on purpose. The moment you start hiding behavior behind clever abstractions, you lose the ability to reason about time, order, and cost.

If it feels a bit too plain, that's usually a good sign. When I can explain the loop on a whiteboard without squinting, I know I'm on the right track.


Key Takeaway

Building small, focused systems isn't backward thinking—it's the fastest way to find architectural truth. Constraints expose what matters.

What's Next?

In the next post, I talk about the 3-layer architecture that holds this all together: Core, Engine, Game. It's how you keep things from becoming a tangled mess.


##game-development #doom #engine #constraints #programming #2026 #tutorial #software-engineering #retro-gaming #csharp