Cached at:
07/05/26, 10:30 AM
**TL;DR:** After 3 years of game development with 100,000 lines of Zig code, Zig proves to be a lightweight, efficient, and comfortable language for game logic. Despite the standard library and language still being in flux, the positive changes far outweigh the inconveniences, especially when mixing with a C++ engine.
## Background & Project Overview
Hi everyone, I'm Sebastian from Medical. It's been a while since my last video because I've been heads-down developing *Traction Point* — the whitebox-stage game you're seeing here. Today I want to talk about the Zig programming language, because I often get asked: "What's it really like using Zig for games? How's the dev experience?" I didn't want to answer before because I hadn't shipped a real product with Zig, and Zig wasn't at 1.0 yet. But now many very large, mature codebases have been written in Zig, so I feel I can share my thoughts.
My project is about to release pre-alpha 4, a Steam demo. It's now over 100,000 lines of Zig (I know discussing line counts is silly, but it gives you a sense of scale). I think I have some authority to talk about using Zig for games.
## How I Got Started With Zig
Most people know Zig for its excellent C interoperability — you can just throw a C library at Zig, and Zig will ingest, compile, and use it perfectly. But in *Traction Point* I barely used that feature, because I combined Zig with a C++ game engine called Basis.
When I learned about Zig in 2019, I had already been working on Basis for about 10 years. It's a fairly mature engine with a level editor, full asset pipeline, etc. I didn't want to throw that away, nor did I want to write game logic in C++ (C++ is too complex, has historical baggage, and overcomplicated build systems). I wanted a simpler language for game logic — one that allows heavy iteration and is fun to write. I felt Zig was that language.
So I created a binding layer between Zig and the Basis engine. Zig wasn't stable enough in 2019, so I didn't push it. But by 2023 when I started *Traction Point*, the binding layer was quite solid. So we decided: okay, we're going to use Zig for this game. Now the codebase is a mix of C++ and Zig, with roughly twice as much C++ as Zig (not exact), not counting external libraries like the Fusex physics engine.
## Language Stability: The Elephant in the Room
Zig is still unstable. How does that affect me? I think it's important to separate two categories: language changes and standard library changes.
- **Language changes:** Almost all positive. Occasionally there are small syntax tweaks, requiring find-and-replace for certain function calls or details. But nearly every change has made the code better — cleaner, easier to reason about. Sometimes Zig's formatter even helps automatically; just save the file and it auto-converts some things. Great.
- **Standard library changes:** A bit more annoying. Especially the build system (written in Zig, using Zig scripts) — its API changes occasionally, and it's not always obvious how to fix things until docs catch up.
I've always tried to stay as close to Zig's master branch as possible, and that's entirely my choice. Once I know what they're planning next, I don't want to stay on a released version; I want to prepare early so I don't have to fix a ton of stuff all at once when I eventually update. This means occasional minor issues, but nothing that would make you give up. If you have a bit of a tinkering spirit, handling these small annoyances is totally fine.
## Daily Dev Experience: Lightweight, Comfortable, Compact
Writing Zig feels "lightweight, comfortable, compact." You want a new file? Just create `new_file.zig`. Put a declaration: `thing = 1`, then somewhere `import new_file.zig`, type `thing.` and there it is. No project files, no CMake scripts to regenerate — everything is simple.
To start with Zig, just go to ziglang.org and download Zig itself (you can start with stable 16.0, or download master if you're adventurous). That gives you the compiler, standard library, and the entire toolchain. Then install ZLS (Zig Language Server). If you're using VS Code, the Zig extension can auto-install ZLS. With those three things, you have everything needed to write Zig code, whether you're on Windows, Linux, or Mac. And it's *much* smaller than Visual Studio on Windows (several GB) — fast and lightweight.
In *Traction Point*, I use a component model (similar to Unity's MonoBehaviour, but only similar). For example, a tire track component written in Zig draws tire marks behind vehicles, erases grass, etc. It's a struct with some declarations, private inner structs, fields, and functions. You see lines with `_` all over the code. I have a snippet specifically for adding them.
## A Few Things That Might Surprise You
### No Operator Overloading
If you're coming from C++, Zig has zero operator overloading. For example, in math code, `state_info.contact_point` is a 3D vector; to add, you must use `a.add(b)` or a free function `add(a, b)`. Nowadays Zig has good support for SIMD vector types: you can directly use `@vector` with a length, like `const ve3 = @Vector(3, f32)`, and SIMD vectors support standard math operators. But when I set up the binding layer, SIMD vectors weren't stable yet, so I didn't use them. If I started fresh, I'd probably use SIMD vectors with free functions like `dot(a, b)`. There's the zmath SIMD math library in Zig gamedev that handles most of this. Remember: Zig has no operator overloading — it might feel regrettable at first, but now I'm used to it.
### Unused Parameters Must Be Explicitly Ignored
Zig warns about unused function parameters. You must explicitly ignore them with `_`. For example:
```zig
tick(self: *Self, _tick_delta_time: f32) void {
// tick_delta_time not used
}
```
If you remove the `_`, the compiler will error. ZLS auto-fixes by adding `_` and an annotation. The design idea: if you were supposed to use `tick_delta_time` somewhere but forgot, ZLS will automatically remove the ignore annotation when you save (if you later use it). This has saved me a few times when I made copy-paste mistakes and didn't use a passed parameter.
But at first it's a bit annoying: create a new function, add a few parameters, save, and they all become `_`. However, you can leave them and then write `const s = a + @intFromBool(b)`, and on save the ignore annotation will vanish automatically. Now I'm used to it and actually like this workflow.
## Summary
Using Zig for games, especially as a game logic layer mixed with a mature C++ engine, is a great experience. Although the language isn't stable yet, the positive language changes and lightweight toolchain make development enjoyable. The standard library changes occasionally, but it's entirely manageable. If you want to try Zig for game development, download it from ziglang.org, combine it with ZLS, and start writing code. My Steam demo of *Traction Point* is proof that it works.
---
Source: YouTube video (https://www.youtube.com/watch?v=HXpUShkr2VQ)