esbuild
and preact
.
There was no scrollbar in the node_modules
folder!
But I still felt like I could do better.
Browsers are so wasteful and slow.
And web apps feel so much worse than well-written native apps.
The initial spark for writing my own game engine came from the web.
I wanted to get back into game development, but was extremely frustrated by the 5+ second build times of Game Maker Studio.
I wondered if I could bring hot reloading from the web to game making?
My computer should be able to reload things instantly!
I decided to use C because I knew that lots of game engines are written in C and C++.
I also knew that my code could run as fast as the hardware would allow.
I also decided to take it one step further by not allowing myself to use many libraries.
I do use the C Runtime Library (CRT) and some third-party single-file header libraries, but I try them to a minimum.
It's the Handmade ethos if you will.
What I learned to love about C is it gives you almost nothing.
You are in control of everything that happens.
You're just about as close to the computer as you can get without writing assembly code.
And you can even see the instructions if you want to!
Learning how to make game engines from scratch is also a great way to learn
how to build applications from scratch.
Games have to do what every application on your computer needs to do. They need to open a window, get user input, display the results, and play sound.
And they have to run in 16ms per frame, every frame.
And they have to build a fun experience on top of that!
At their core, games need to:
- Get input from the outside world
- Do some updates based on that input
- Display the result to the user
GetInput(); Update(); Draw();In practice, what this actually looks like is more like:
f64 then = OS_Time(); while (!ShouldCloseApp()) { f64 now = OS_Time(); f64 delta_time = then - now; now = then; GetInput(); Update(delta_time); Draw(delta_time); WaitUntilNextFrame(); }I mean, really more like this timestep, but hopefully you get the idea. Because I had to build everything myself, I had to learn how everything works from first principles. I didn't write everything myself. I got lots of inspiration and code snippets from others. The trick was finding a good balance between using what I knew and exploring what paths others had gone down. I eventually decided to write a base layer that I could build on top of similar to what you get in other programming languages. And if something has a bug, that means I could fix it! And if something has a bug, that means I have to fix it. This base layer includes:
- Basic types and macros
- Arenas and allocators for memory management
- Strings and Unicode conversions
- File IO, dates and times
- Threading primitives like semaphores and mutexes
- Arrays and Tables
- Math
- Windows and mouse and keyboard input
- Controller input
- Graphics
- Fonts
- Audio
- Sound mixing
- Networking
- Image reading and writing
- An asset system
- Hot reloading
- Entities
- Want to open a window? Well, there's a lot of quirks with that.
- Want graphics to show up on the screen? You're going to have to wrangle GPU APIs (similar to doing your taxes). Or at the very least learn a lot about software rendering.
- Want sounds? You need to really care about the speed of that! Not to mention sound mixing.
- Want controller input? Well there's like 4 different Windows APIs and they all give different and overlapping information: XInput, DirectInput, Raw Input, ...
- Want fonts? That's a rabbit hole. At the very least, you have to write a font-atlas and space out all the glyphs manually, not to mention dealing with other languages! And did I mention there are some right-to-left languages?
You really appreciate everything when you have nothing. C gives you a first-principles mindset to problems in computing more generally. In some sense, you know exactly what the hardware is doing. Too many things are built on top of towers of abstractions, and it's harder to reason about what's actually going on. Why do we do things the way we do them now? Are we getting good results? If not, why not? It seems like modern day programming is inundated with libraries, OOP, TDD, slowness, bad UX, and outright malicious practices. And there are lots of programmers who don't even try to get better. They just repeat the same dogma. Maybe because it's all they know. Maybe they're scared of confronting what they don't know? I think it's safe to say that the software industry has massive problems today. Programmers have failed. Software continues to get slower and worse. Users are increasingly being used for business profits with little or no care. Maybe as a culture we've also forgotten how fast computers used to be? I certainly did! I had forgotten how fast applications used to start up. There were no load times, no updates, no restarts. Things just worked better. Don't believe me? Check this out. If we did literally did nothing, then software that is 20 years old should be 1000x faster today by De Morgan's Law. And yet, the software we use today is slower by at least an order of magnitude! And that's being generous. So maybe we need to change our mindset? Maybe we need to start caring? Maybe we need to stop using so many goddamn dependencies. We need to count in milliseconds, not seconds. We need to get back to the basics. And that is why I write C.
I still encounter many things I don't understand and probably always will. But I've learned that I can learn anything with enough time and practice. It's okay to get inspiration from others, but try not to worry about exactly how others solved their problems. Solve it for yourself. Make sure you really understand. Start where you are, use what you have, and do what you can. And remember to be kind to yourself!