Zig gamedev project – left my job to build games in Zig lang
github.com
github.com
I ran into a compiler crash: https://github.com/ziglang/zig/issues/7865. I attempted to fix the compiler crash myself (https://github.com/ziglang/zig/pull/8372), and I found a fix that worked, but apparently it was not the right fix, and I was told the correct fix would be "quite hard" and "To fix some of the bugs you would need to rewrite quite a bit of code."
It sounds like a big milestone will be when Zig's stage1 compiler is self-hosting, written in Zig instead of in C++. I got the sense that the C++ code I was modifying is somewhat crufty, and the Zig version of it will be better designed in many ways (ie. https://github.com/ziglang/zig/issues/89#issuecomment-382110...), which will make fixing bugs like the one I encountered will be more tractable. I personally will be interested to revisit Zig when this migration occurs.
I also found that the language was changing from release to release in ways that made me update the code.
This is all totally reasonable for a language in development, and I mean no disrespect to the Zig devs -- it's an extremely interesting language. I only say this because stories I saw on HN gave me an impression that Zig was farther along than it is, at least in my experience.
1. Shows to others that your language is capable of a project of moderate complexity, as well as display what an "idiomatic" version of writing code is
2. Remove dependencies on parts you can't control (once you rewrite it in Zig, rather than C++, you don't need to worry about new C++ features or deprecations between versions)
3. Writing code in the language helps catch bugs in the language specification and in the compiler's implementation of the language.
These are just the ones off the top of my head, but people with more PL-Design experience may be able to elaborate.
I know it's entirely other end of the language spectrum, but I think that's why Typescript got successful while Flow did not; Typescript is in Typescript, while Flow is in OCaml. And nobody knows OCaml.
OTOH, eslint is making waves now and it's in golang, so. Who knows.
* Debugging is easier, faster, and more straightforward
* Fewer bugs due to footguns in the language
* Typical Zig code runs faster than typical C++ code. Part of this is due to Zig's safety features - there are design optimizations for performance that I won't even attempt in C or C++ because it's footgun city. Meanwhile in Zig it's actually completely safe in debug builds; you get a compile error or runtime panic if you mess up something.
* Floating point operations work on f16 and f128 without a dependency on SoftFloat library
* Cross compilation is trivial
* Comptime features make build options feasible and maintainable, such as enabling/disabling logging, whether to link libc, whether to link llvm, whether to enable tracy profiling, etc.
* Zig's error handling makes correct code the default instead of the other way around in C++
* The std lib HashMap, ArrayHashMap, and ArrayList data structures are really nice
* If the compiler is written in Zig, then adding a new target backend to the Zig compiler makes the Zig compiler work on that target. With another language we are stuck only supporting whatever that language supports.
Not only that but the Zig compiler available for download today is already backed by majority of lines of code written in Zig rather than C++; what is available today is already partially self-hosted. For example the following features are already written in Zig, not C++: `zig fmt`, `zig translate-c`, the command line interface, `zig cc`/`zig c++`, `zig build`, the cache system, compiler-rt, the macOS linker, and compile error checks that operate before type checking.
Reading Zig's std lib HashMap code the first time was an unforgettable experience for me.
I've spent literally months on some state of the art hash table papers, implementations and tons of hash functions, and Zig's HashMap implementation has got to be right up there with some of the best and most performant out of the box.
However, what impressed me most was just the level of control offered by the interface, and how clean and readable the code is.
On the other hand, because Zig has incredible velocity, we're also seeing that std lib contributions or fixes we make (e.g. io_uring) are landed quickly, and that the community has some of the most talented programmers we've seen, who are also well aware of where the language is at (and like you, also looking forward to self-hosted).
As for TigerBeetle, when we made the decision to go with Zig, the only alternative for us was C since we needed a safe way to handle memory allocation failure and also wanted a simple language with high orthogonality and power, and C interop. However, C's toolchain even then was still not as "ready" as Zig's and Zig also presented a unique approach to safety (e.g. the compiler can check that all syscall errors are handled at compile time, and integer arithmetic and overflow is checked by default) plus comptime, which is a force multiplier.
To be fair, the days of debugging we save through Zig's safety compared to C's undefined behavior, we could easily spend a few hours contributing small fixes back to the language. And breaking changes between releases have also been fairly tame, and the changes welcome. We appreciate the velocity here.
The final insight for us was realizing that it would also take us some time to get our project to production, and that our roadmap would probably intersect with Zig's stability. We go into this decision some more in the Q&A at the end of our Zig SHOWTIME talk [2].
We could have skated to where the puck was at, or where we believed it would be, and we picked the latter. Technological waves can be incredible if you can spot the value ahead of the curve. In hindsight, this decision has paid off again and again. We would never go back to C. For new projects, there are a good many reasons to pick Zig, and for projects that may have a long half-life, this decision may be all the more important.
[1] https://www.tigerbeetle.com
[2] https://www.youtube.com/watch?v=BH2jvJ74npM (TigerBeetle — A Million Financial Transactions Per Second In Zig)
> Why not Unity or UE5? Why Zig?
I'm building relatively small game, I don't really need full game engine. I try to write simple code that does only what I really need. I want to push Zig forward, I want to promote it. I was working with UE5 and Unity at AMD - those engines are really huge, bloated and they are changing very fast. I want to keep control over my code. Again, I don't need 90% of their features. I like low-level stuff and I want to master my skill.
> Why zig-gamedev project?
I want to help others learning Zig. I want to create demos, audio-visual experiments and even some art stuff. I'm interested in intersection of art and science. I want to prototype and experiment with ideas that potentially will be used in game (tech, algorithms, etc.).
> Why Windows and DX12?
DX12 is really well supported by Microsoft. They have great tools. They have interop with Direct2D and DirectWrite. They have DirectML (built on top of DX12). Windows itself has builtin decoders for most video formats (I will need this and I don't want to include entire ffmpeg).
I'm most familiar with DX12. DX12 is a bit less verbose than Vulkan.
I don't have a bandwidth to care about other platforms. Focusing only on Windows lets me write simpler and often more optimized code.
Also, drivers are really mature and stable.
> Why to take risk?
I was working for big corporations for ~12 years now. Money is good but you don't really realize your ideas there. You don't own what you build. Now, I want to create my own stuff, my way, using my ideas. Money will come later.
Early on, I remember dealing with lots of bugs in C compilers in the mid and late 80s. It takes time.
Otherwise it is pretty much Modula-2, with C flavoured syntax and compile time execution.
Overall, the use-after-free is what irks me.
- Low level
- great C interop
- Good toolchain ergonomics, except for no pkg manager yet
- Great cross-compilation
- Fast compilation (incremental coming soon)
- Ideal for things where ownership rules can get complicated (ECS e.g.)
- Fast compilation: isn't it llvm? So how faster compared to C++/Rust/others that use it?
- Ownership rules/ECS: what about zig makes this especially good? The only language that comes to mind in this respect is Rust and its borrow checker, is it similar in Zig?
2. Zig doesn't have ownership rules, which can get in the way of important data structures for gaming.
We deserve and can afford a lot more comfort than Pascal (and siblings) provides. Incidentally, modern language ergonomics also adds much in the way of coding safety, making usage of explicit allocation control a more manageable problem than it was 30 years ago.
For Zig to be part of that list, a story for use-after-free must exist as well.
Others won't agree with me, and that is fine, different strokes for different folks.
For example, Zig offers spatial memory safety, and provides test allocators to catch temporal memory safety issues. That's already an order of magnitude improvement over C.
Memory safety is also just one aspect of safety, whereas sometimes programmers conflate the two. It's important, but things like checked arithmetic should also be right up there, and should be enabled by default in safe build modes. I think Zig's approach here is also spot on, having worked a little in security, where an integer overflow can be almost as dangerous as a buffer overflow. Yet I don't see many other languages taking checked arithmetic as seriously as Zig does.
Sad that we have to go in circles to keep programming fashion going, instead of adopting best practices from the get go.
How you structure the financials and the community around the language has also a gigantic impact on the final result, and this is an area where Zig bringing to the table something completely new.
Outside Apple computers, the dialects created by Borland gained such following, specially in Europe, that Turbo Pascal became the official Pascal dialect, even though Extended Pascal fixed most of the original design flaws.
Naturally they going enterpreisy lost the crowd to VB and VC++ folks (later .NET).
Modula-2 did have some nice offerings, specially on Amiga, but on the PC and Mac, Turbo/Object Pascal made it irrelevant as it offered all the improvements Modula-2 brought to the table (no one cared about co-routines on home computers back then).
Ada was the only one from those that yeah, actually quite expensive, and I think only SGI and SUN had UNIX compilers for them, with HP having BASIC and Pascal compilers for their OSes.
Then there was the Amsterdam Compiler Kit, the "LLVM" for the 1980's, which had support for C, Pascal, Modula-2, Occam, and BASIC.
Looking forward to see how Zig evolves, specially regarding issues like #2301.
C won due to UNIX, had UNIX not been a kind of free beer that companies could build their workstations with and universities avoid paying for commercial OSes like VMS, history would have taken a different path.
Also, lisp has pretty much always been more powerful than C. But what zig brings to the table is an extremely simple language which is also very powerful. Much of the power results from the entire language being available at compile time. You seem so ready to throw that away in favor of could-have-been nostalgia, I wonder if you've taken the time to understand what you're criticizing.
Zig isn't the only AOT compiled language with compile-time type reflection in 2021, and exactly because of could-have-been nostalgia, we don't need newer systems programming languages that don't have an answer for use-after-free in safe code.
https://github.com/ziglang/zig/issues/2301
https://github.com/ziglang/zig/issues/1966
Naturally I have taken the time, and did not come out impressed, given my background and programming language beliefs.
However as I say in another thread, other people seem to be happy with such shortcomings.
It is not a "nuanced and balanced" approach: Zig is simply not memory safe.
[1]: https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html#...
It's of course true that some developers may judge the tradeoffs differently for their individual projects—that's why they're tradeoffs! But there are benefits to memory safety that go beyond security.
everything else is just bloat and noise that hurts iteration time
and even if one would still value them, you'd need to check only once for whatever memory check you want to run, when you build your allocators for example, and not at every builds, and you could even write the logic yourself and have a debug allocator to ensure memory safeties
you want sub second and not "double digit seconds" build times
it takes one to try to make a game to trully understand why iteration time is far more important that anything else (other than performance of course)
you don't want to wait multiple seconds everytime you change the speed of your character, or tweak the rendering/AI code
that's why then some devs end up using scripting language and they loose all the advantages of their native language, because they want to speed up iteration time
that's why i personally stick to D for my game, my engine + game fully rebuild in under 1 second
you don't get to create memory bugs when you work on your gameplay code ;)
Disagree, but in any case, we're talking about the fact that Zig doesn't provide use-after-free protection.
Probably if your game is single-threaded …
Rust's raison d'être was type-check thread-safety, and even if we don't talk about this aspect much anymore it's still the domain where it has no competitors (Pony could have been, but didn't get traction).
And it's invaluable.
While there is some truth to that, if that's the only analysis you're willing to do, then you're going to fail spectacularly hard at recognizing the advantages of TypeScript over JS, for example. Zig and C share a similar relationship across many axes, including memory safety.
[1]: https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html#...
Full time indie is a fun and scary adventure at the same time, good luck!
One question, why DX 12? i'm kinda sad when i see indie choose proprietary non-portable tech..
Not to say that vulkan is better (i hate it), but i'm curious
We solved this issue already with WebGPU, it uses what ever is native to the platform you target..
https://devlog.hexops.com/2021/mach-engine-the-future-of-gra...
Everyone wins
Michal is a seasoned AAA dev who has worked at AMD, Frostbite, EA DICE, Intel - I'm sure he knows exactly what tradeoffs he's making.
Different folks have different goals, and that's OK. My goal with WebGPU is to aim for a future where we have truly cross-platform graphics. I suspect your goal is similar.
But I don't think it's wrong to aim for the present, to use an API you're familiar with, or to want to get as low-level as you can get. That's pretty admirable, honestly, and I'm happy people are doing that in the Zig ecosystem.
Heck, maybe one day Michal's work with DirectX here leads to a DirectX backend for a Zig WebGPU implementation. :)
Experimentation is good.
WebGPU is just 3D rendering. I will also need vector graphics, machine learning stuff, video playback, etc. Windows gives me that for "free" (DirectML, Direct3D 12 Video, Direct2D, DirectWrite).
My main goal is to ship a game and build interesting demos/mini-games as part of zig-gamedev project. I don't want to spend my time supporting and maintaining code for several platforms.
Also, using wrappers like WebGPU adds another layer of abstractions. I want to write my shaders in HLSL using dxc.exe, I want to directly see messages from DX debug layer and have full control over my code. WebGPU would just slow me down.
My game will run only on Windows 10+ and I'm fine with that.
Switch uses GL/Vulkan
And the PS4/5 uses something akin to Vulkan
On PC Vulkan is probably more used than DX12, i can't remember the time i seen DX12 in a setting.. i think i only seen it once..
I was thinking about using Zig with raylib for my next project. The show stopper was that C ABI compatibility is not yet complete: https://github.com/ziglang/zig/issues/1481
I could have probably worked around it but decided to go the C and Lua for scripting route for now which works great but I can totally see Zig as a decent C replacement.
I am not interested in Windows but I am wishing you best of luck with your project. Seems quite interesting.
That, combined with generally really fast compiles (again relative to C++ and Rust) is a nice combination for game devs.
I can understand if someone left their job to write $FOO - you see an opportunity and you take a risk on it. What does the language have to do with it?
To me, taking such a risk, and then adding further risk (using a language you weren't using daily) seems to be a recipe for disaster.
If I were taking a risk (by leaving my job) on a new product, I would do my best to reduce all the other risks (use a familiar and popular tech stack, for one).
The real reason was of course that I wanted to play with the new thing.
You are right, Market fit and the idea matter most (when assessing a new company)
But not all ideas should be weighed purely on profit. If op is in a healthy financial state and can shoulder the risk for a year, it’s hard to weigh the opportunity risk for him as a person. It might reinvigorate a love for programming or lead to a job in this new language. And ofc it may also just work ;)
Zig is perfect for game developers, and being one of the first in the space is going to be an advantage.
Also, the product here is actually defined as "Building gamedev ecosystem for @ziglang!" — Zig itself is part of the strategy here.
There are no business opportunities in independent games - they are passion projects, with little chance of payoff. So, if someone is passionate about his game idea and also passionate about Zig, it makes sense to combine both motivations in one project. He might not have enough passion to do the game in C++ or some other language he already knows and hates :)
So my question is, why is Zig so good for game dev specifically (thats by far the most common use I've seen for the language so far, in my limited dealings with it)?
Expect some maturity/stability issues with either one. Zig is much earlier in its lifecycle and I have seen showstoppers, but it's also been moving faster. Nim has struggled in the past with stabilizing its high-concept features - use it mostly like C and it's fine.
Zig's biggest appeal is in how close it is to a fully bootstrapping environment - low level, clearly defined control over all resources is just what you want if you are writing a kernel.
1. It's the game from which the "all your base" meme comes.
2. The fighter craft that the player controls is called "the ZIG".
Move every ZIG; you know what you doing.
Async stuff is interesting to see
I know it might be fun to develop your own engine but it will take a lot of time and it still won't be as usable as an engine developed by 100x more engineers.
In regards to developing your own engine - it's almost never more efficient to do so nowadays, but it is almost always significantly more fun. If the author has enough wealth accumulated to quit their job and work on a passion project full time, I imagine they can afford to do something just for fun as the baseline.
That's it right there. You can spent years pretending to write a game while optimizing your from-scratch engine for features that probably no-one will ever use [1]. But I think it depends on what your goal is.
If it's a small hobby project, and you enjoy the process of writing your own engine (which, granted, can also be a great learning experience), then why not? You're probably in for the fun anyway.
I know some indie devs who learned the hard way that if your goal is in fact to eventually release a game, doing it without using an existing engine can reaaally drag things out.
I think it's tempting: you can get started on a new "engine" pretty easily, and get something up and running in a day or two. But then comes the long tail of adding all the other stuff, which often isn't so much fun.
A lot of the game development process isn't actually programming, so by writing everything from scratch, a programmer can artificially shift the focus into the direction they're more comfortable with.
If you have some amount of self-discipline and implement your custom engine to be minimal and only tailored to your game (as many indie games have demonstrated), I don’t see how it can’t be done.
Even ignoring that. I think there is a more fundamental question here as well. What type of maker do you want to be?
The problems usually start when people are more interested in writing the engine than making a game. Then they gravitate towards all sorts of fun technical problems that would be useful in a general purpose engine but not really for their specific set of problems. I’ve also seen this tinkering trap with general purpose engines but quite technical projects.
Please don't make assumptions about my experience based on that though. It's pretty impolite before I'm even able to respond to the new criteria and makes you look like you're not actually responding in good faith.
I'll also direct you towards the HN Guidelines: https://news.ycombinator.com/newsguidelines.html
Be kind. Don't be snarky. Have curious conversation; don't cross-examine. Please don't fulminate. Please don't sneer, including at the rest of the community.
Comments should get more thoughtful and substantive, not less, as a topic gets more divisive.
When disagreeing, please reply to the argument instead of calling names. "That is idiotic; 1 + 1 is 2, not 3" can be shortened to "1 + 1 is 2, not 3."
Before making my decision to go with Zig I've built small game in UE5 (https://michalziulek.itch.io/upacman). I've also used UE5 and Unity at AMD. I simply don't like those engines.
Different people have different approaches and mindsets. I'm much faster building my small, minimal codebase that does only what I need. Also, I'm improving my low-level programming skills and experimenting, doing things differently than most people (not using Unity) might lead to very original game.
In my case, simplicity is the key for productivity.