game design, code, writing, art and music

How I wrote my own 3D game engine and shipped a game with it in 20 months

My game Speebot is finally out on Steam! If you’re a fan of 3D platformers, be sure to pick it up while the launch discount is active. There’s also a free demo to try before you buy.

Speebot has been in development since January 2016, and I’ve been working on it in my spare time by myself as a hobby. I’ve done all the programming, game design, graphics and music. I’ve also written the game engine from scratch.

People often ask me why I’ve decided to write my own game engine in the year 201X, when there are multiple general use engines available for free. There are many reasons, but in retrospect I can say that “inventing my own wheel” was an even better idea than I initially thought it was.

One of the biggest advantages of a custom engine is the absolute code control. I can customize it literally any way I want and optimize it for my specific use case. Universal game engines are called that because they are designed for general use, which results in bloat and often considerable slow downs.

The second advantage is the workflow control. I find that an efficient workflow is crucial in game development, and this decision lets me integrate my own tools in a way that makes it easier and faster for me to produce content.

Of course, there’s also the benefit of not being tied to any ToS and not having to pay license fees.

Also, it’s fun. And that should be a reason enough, I think.

I started developing the engine in January 2016. I called it YUME (which means “dream” in Japanese), and it’s written in Haxe, C++ and OpenGL. I use a custom fork of the Lime library, which is a lightweight framework that enables things like asset management and access to the GL context.

This is what the YUME engine was capable of in its first week.
This is what the YUME engine was capable of in its first week.

I knew almost nothing about 3D game development when I started with YUME. I had to learn OpenGL from documentations, archived forum posts and tutorials meant for Java and C++ programmers. The 3D algebra was especially difficult for me, and sometimes I got stuck at problems for days. But I figured it all out eventually, and in a couple of months I had a pretty solid 3D renderer.

Besides the 3D renderer, I had to implement various other sub-systems, including: a 2D renderer, state machines, a timestep system (more on this later), a UI system, a mouse input system, a keyboard input system, a gamepad input system, dynamic shadows, a 3D sound system (using OpenAL), model loading (in my own file format based off IQM), skeletal animations, bone attachments, real-time reflections, bitmap text rendering and so on and so forth.

As a result, I ended up with a functional little 3D framework. Many of the things I listed are essentials that are no doubt found in every other game engine, but there are a few key differences that justify early YUME’s existence. One of those things is my timestep system.

A timestep system ensures that an engine displays frames at specific intervals. Unlike most current game engines, YUME does not have a framerate cap, i.e. it's not tied to 30 or 60 frames per second. In fact, the framerate can be anything from 1 to 250 frames per second, and the game will still appear to be running at the same speed no matter what the framerate is (only the screen will be updated at different intervals). This is achieved by separating game logic from rendering. Rendering a single frame can take different amount of time on different computers, so the render framerate depends on the program's performance. The logic cycle, on the other hand, is tied to a 64 tick rate - meaning the game logic is updated 64 times per second, no matter what the render framerate is. There are a few edge cases that the timestep system must account for, but the principle remains the same. As a result, the framerate decreases on weaker computers without ruining the game flow.

Early YUME demo.
Early YUME demo.

I had a framework, but not a game engine. It was time to start designing my game, and decide what features I need to implement. I’ve had a few ideas by that time already, and knew that I wanted to experiment with tilesets in 3D.

I began writing a map system that was similar to 2D tile maps that I was used to, but with an additional dimension. I made an editor for myself to speed up this process of map creation (spoiler alert: this feature ended up as the custom level editor that’s included in the final game). I made it so that the engine would automatically figure out which tile type to display based on its neighbors (some call it “autotiling”). I thought of a way to batch all the tiles into a single mesh, which means that the entire map was treated by the engine as a single model and could be drawn in a single call to the GPU. As a result, the performance was really good. I didn’t have to compile maps, and could edit and play them in real time. It was perfect for productivity.

I wrote a simple physics system – basic collision, frictions, nothing too complex – and began experimenting with gameplay… In a couple of weeks, I had a cylinder that was jumping around a tile map in 3D.

Thanks to YUME, I was able to prototype and experiment with the game in a really efficient way.

Speebot prototype in July 2016
Speebot prototype in July 2016

The YUME engine evolved as my progress on Speebot continued (I picked the name Speebot around this time). I found and recognized problems with my architecture as I worked with the engine, and refactored it twice. The engine now had a simple physics system, particles, water reflections, camera animators, interactive entities, ray casting… It evolved from a framework into a game engine.

I was adding new gameplay mechanics and figuring out what worked well. I kept what was fun, and removed or repurposed what wasn’t. I started experimenting with the art direction for the game, and made a little robot character in Blender. I found that the cartoonish rubber band animations for the character worked really well, they made the game feel lighthearted and juicy. I kept that look and feel for the future NPCs and entities as well.

Speebot was starting to look and feel like a game!

At that point I was working on a game, rather than a game engine. I continued to create new maps, props, NPCs and mechanics. The level editor grew with features, and so did the engine itself.

In the winter of 2016/2017 I took a couple months off from the usual game development routine, and focused on practicing composing music. I have no formal musical education, but I enjoy it, and I’ve written the soundtrack for my previous game Hypnorain in the past. I wanted to do better for Speebot, so I delved into music theory again and made a lot of practice tracks. I used SunVox for making music, which is a modular synthesizer and tracker program. The final version of Speebot has 23 tracks, which amount to about an hour of original music. I am actually really happy with the results. I will continue to improve my skills for the next game.

From that point, I was alternating between creating new levels, introducing new gameplay mechanics, writing music and fixing bugs. I really like this workflow, because it lets me control every aspect of the game, and when I get bored doing one activity I just switch to another.

After releasing a couple of demos, collecting feedback from players and improving the game – Speebot was released in October 2017. The final game has 200 stages, 4 worlds, a custom level editor, various extra modes, unlockable cosmetics and other side content.

No doubt one of the biggest challenges was to stay motivated and keep working on the game, every day after university/work and during the weekends. Still, it was great fun, and well worth it.

I will continue using and improving YUME in my future games. I have already started working on my next project. More information coming soon!

Speebot was my most ambitious project to date, and my best game so far.

But my next game is going to be much better.