Understanding VEngine's Architecture

When people hear the word architecture, they often think it's something only large companies need to worry about.

I used to think the same.

But once you start working on a project that you'll be maintaining for months—or even years—you quickly realize that writing code is only half the job. The other half is organizing that code so it remains easy to understand, extend, and maintain.

That's what software architecture is all about.

Although I'll use VEngine as an example throughout this article, these ideas apply to almost every large software project, not just game engines.


What is a Good Software Architecture?

A good architecture divides an application into smaller systems, where each system has a single responsibility.

Instead of one class trying to do everything, responsibilities are separated into independent components.

For example:

Each system has one job.

The application simply coordinates these systems to build the final program.

This makes the project much easier to understand and maintain as it grows.


VEngine's Current Architecture

Right now, VEngine is still very small.

Its architecture looks like this:

          Application
          /         \
         /           \
    Window        Renderer
         \           /
          \         /
            Raylib

Although simple, this diagram already demonstrates an important design principle.

The Application class does not create windows directly.

It does not render graphics.

It does not call Raylib functions itself.

Instead, it coordinates the different systems.

You can think of the Application class as a manager.

It tells each system when to perform its work, but it doesn't perform that work itself.


The Window System

The Window system is responsible for everything related to the application window.

Window
│
├── Create()
├── Destroy()
├── Running()
└── SetTargetFPS()

Every window-related Raylib call belongs here.

If something goes wrong with the window, I know exactly where to look.


The Renderer System

The Renderer has a completely different responsibility.

Renderer
│
├── BeginFrame()
├── EndFrame()
├── Clear()
├── DrawCircle()
├── DrawRectangle()
└── DrawText()

Everything here is related to drawing.

Instead of calling Raylib directly, the rest of the engine communicates with the Renderer.

The Renderer then communicates with Raylib.


Where Does the Game Code Live?

If the engine owns all these systems, where does the actual game logic go?

That's where Sandbox comes in.

Sandbox is not an engine system like Window or Renderer.

It is the user's application.

It inherits from Application, allowing the engine to provide all the infrastructure while the developer focuses entirely on the game.

For example, this is where the developer writes code such as:

The game doesn't worry about creating windows or managing the render loop.

The engine already takes care of those responsibilities.


How Everything Connects

The overall flow looks like this:

Sandbox
     │
     ▼
Application
     │
     ▼
Window / Renderer / Input
     │
     ▼
Raylib

Notice that the flow always moves downward.

The Sandbox talks to the engine.

The engine talks to its systems.

The systems talk to Raylib.

The Sandbox never communicates with Raylib directly.


Three Layers of VEngine

You can think of VEngine as three separate layers.

                USER
────────────────────────────────

             Sandbox
                  ▲
                  │ inherits
                  │

────────────────────────────────
               ENGINE

            Application
          /      |       \
         /       |        \
    Window   Renderer    Input
         \       |        /

────────────────────────────────
              BACKEND

               Raylib

Each layer has its own responsibility.

User Layer

This is where game developers write their games.

Engine Layer

This layer provides reusable systems like Window, Renderer, and Input.

Backend Layer

Raylib is simply the graphics backend used by the engine.

Because Raylib stays hidden behind the engine, the user never depends on it directly.


Why Does This Matter?

At first, this architecture might seem like extra work.

Why not just call Raylib directly?

The answer is flexibility.

Imagine that one day I decide to replace Raylib with another graphics library, such as SDL, GLFW + OpenGL, or Vulkan.

If every part of my game called Raylib directly, I would have to rewrite code throughout the entire project.

With this architecture, only the systems that depend on Raylib need to change.

The Sandbox remains exactly the same.

Similarly, if I discover a bug in window management, I know it must be inside the Window system.

If rendering behaves incorrectly, I immediately look at the Renderer.

Each responsibility has a single home.


Final Thoughts

VEngine is still in its early stages, but I believe building a solid architecture first will make every future feature easier to implement.

Today there are only a few systems.

Tomorrow there might be rendering pipelines, physics, audio, scenes, asset management, scripting, and much more.

The architecture doesn't make the engine more powerful.

It makes the engine easier to grow.

Github: VEngine

go back to tech