#Traction Point: A Zig-powered video game

1 messages · Page 1 of 1 (latest)

hasty tinsel
#

I have been working on a video game called Traction Point for a couple of years now, and I just got the Steam page for the game published, so I figured "this thing is probably gonna happen" and I can create a project for it here as well 🙂

First things first, here's the link to the Steam store page if you want to check it out, and maybe add it to your wishlist: https://store.steampowered.com/app/1485840/Traction_Point/

I'll add a few screenshots below as well.

The game is a vehicular, physics-driven, puzzle/exploration game set in a sci-fi universe. You embark on a road trip together with your crew in the single-player campaign, and you can experiment and play around in the sandbox mode. I am making 99% of the game as a solo developer, funding the whole thing myself, and I plan to release the game in 2026.

Now, for the technical stuff you guys probably care about the most. The game runs on my self-made game engine which is actually written in C++, and has a bunch of tools and editors, mostly written in C#. When I found out about Zig around 2019 I immediately wanted to see if I could create a game using it, while still using the game engine I had spent years building. Because of this, Traction Point has a hybrid code base where most of the techy things under the hood are written in C++, while most of the gameplay-related things are written in Zig. For example, the whole AI system allowing NPCs to drive around the world is 100% Zig.

If any of this sounds interesting, I invite you to check out my YouTube channel where I do regular live streams and other development videos: https://www.youtube.com/@MadrigalGames

And if you would like to support the project, please head to the Steam page and wishlist the game. It really helps, and it will keep you posted when the game goes live next year.

Traction Point is a vehicular, physics-driven, puzzle/exploration game. Embark on a sci-fi road trip together with your crew in the single-player campaign, or experiment and play around in the sandbox mode.

Experience the joy of driving as you explore wide open fields, drive along cliff edges, investigate industrial complexes and research sta…

Release Date

2026

#

I support loading multiple Zig libraries into the engine, either as DLLs or as WASM modules (using the WebAssembly Micro Runtime). The WASM support is still WIP, but it's already showing a lot of promise as a base for loading and running sandboxed mods. I plan to support first-class modding of the game and I already have an example mod which runs as a WASM module. Since Zig supports compiling to WASM out-of-the-box, it's super convenient to make mods for the game. You just need Zig and my small SDK to build a mod. This is true for DLL mods as well, of course.

scenic mist
#

Extremely cool and honestly an incredible amount of work. Did you write the graphics layer? Are the vehicle animations all physics/impulse based? Do you have a basic level editor? What wasm runtime are you using?

hasty tinsel
#

Thanks!

#

I did write the renderer yes, though I am using NVRHI to abstract away some of the hairier details of D3D12 (and possibly Vulkan in the future): https://github.com/NVIDIA-RTX/NVRHI

#

All the vehicle movement is driven by the physics engine yes, in this case PhysX 5.5. It's basically using the vehicle SDK which comes with PhysX but I have modified it a little, and I have a custom differential for vehicles with more than 4 wheels.

#

I do have a level editor and it is actually a very capable one. I hope to release this to you guys a bit later, as part of the modding SDK.

quasi barn
#

Great work!

#

I feel like I know you, but I can't tell...

latent birch
#

super cool! I've loved seeing your updates in #game-dev now and then! wishlisted ^-^

vernal onyx
#

Wow! That level editor look impressive!

hasty tinsel
hasty tinsel
quasi barn
#

hello

turbid coral
#

Hi. Very good work. I would like to ask how you chose physX for the physics engine?

hasty tinsel
hasty tinsel
#

Soon...

turbid coral
#

If I can ask.

hasty tinsel
turbid coral
#

I think a low time ago I saw a video that they used wasm.

hasty tinsel
#

Just like the game itself is a DLL written in Zig, your mod can contain its own DLL which gets access to almost all of the same APIs as the game.

#

And yes, mods can also be compiled to WASM modules to make it a sandboxed environment.

#

Running in WASM mode does come with some limitations though, but I aim to expose about 90% of the API surface to WASM as well.

#

The main limitation being that a WASM mod cannot read memory owned by the core engine. It has to explicitly be made available to the WASM module. This obviously comes with some overhead (both perf and memory).

turbid coral
hasty tinsel
#

Yep, that's the entire point.

#

They cannot access anything not explicitly made available to them.

hasty tinsel
#

Yello! After a period of quiet I emerge from my cave with a new piece of shiny tech (hopefully the last big piece for a while since I am hoping to do mainly "gamedev" rather than "tech dev" next year).

Some of you know that I have a few tools built around node graphs, and some of you might even know that I made a particle engine on top of the node graph framework last year. That wasn't a particularily good fit since the node graphs are interpreted and were never designed for simulating thousands of particles simultaneously. Since then the system has been used for a few things in the game, eg. the big smoke pillar, but I haven't really been pushing the system as it has been kinda slow.
To solve this once and for all I started working on node graph nativization a few weeks ago. In a nutshell, rather than running the node graph through the interpreter we look at the flow of data and generate native code out of it, eliminating a lot of indirection in the process. It's still a work-in-progress and I haven't done any serious profiling yet, but the new system allows me to completely eliminate some of the code paths that are slow in the interpreted version, so I am hopeful! 🙂

As a first goal I wanted to have a simple node graph, running multiple execution cursors simultaneously, generated and running in the game when loaded as native code from a dll. You can see the node graph below. No particles or anything fancy yet, just the basics of running a graph.

#

The generated code for the node graph above can be seen below. In the image I've enabled the node ID display, so you can see which node goes where in the code by looking for their numeric ID. I think the code is actually pretty easy to read, and of course you can even debug it. I am keeping it pretty verbose for now, to make it easy to reason about and generate. We can make it tighter later if needed. The generated code consists of a node graph "kernel" and all of the nodes owned by it. The kernel is then wrapped in a NodeGraph object which can be run just like before.

#

Next up, porting a few more of the nodes to the new system. Each node that we want to use in a nativized node graph needs to have support for it. Ie. you need to tell the system how to generate the code for the node. This means writing a code gen function in C# with some framework calls and Zig fragments that the framework then puts together. For example, here's the code gen function for the Float Compare node:

#
[NativeCodeGen(typeof(FloatCompareNode))]
public static void GenerateNativeCode(NativeCodeGenContext ctxt, FloatCompareNode node)
{
    var codeGenNode = ctxt.GetNode(node.ID);
    var value1Pin = node.GetInputPinOrThrow("Value 1");
    var value2Pin = node.GetInputPinOrThrow("Value 2");
    var resultPin = node.GetOutputPinOrThrow("Result");

    codeGenNode.RegisterInlineDataForInputDataPin(value1Pin, node.Value1.ToString("0.000"));
    codeGenNode.RegisterInlineDataForInputDataPin(value2Pin, node.Value2.ToString("0.000"));
    codeGenNode.RegisterSymbolOnOutputDataPin(resultPin, "getResult()");

    var compOp = NativeCodeGenUtils.GetCompareOpSymbol(node.Comparison);

    codeGenNode.AddCustomFunction(@"
pub fn getResult(self: *Self) bool {
    const value1 = ", Fgmt.InputPinValue(value1Pin), @";
    const value2 = ", Fgmt.InputPinValue(value2Pin), @";
    return value1 " + compOp + @" value2;
}");
}
#

The C# verbatim strings makes the indentation a bit wonky, but other than that I kinda like it. The framework will have to be improved as we go.

To make all of this work I had to port Sprinter, the library for actually executing these node graphs, from C++ to Zig. Luckily it's a pretty small library, and I intend to keep both versions functioning since the interpreted versions aren't going anywhere. But it will take some time before the Zig version has full feature parity with the C++ version. Don't expect this to happen any time soon btw. I desperately need to put the final touches on Pre-Alpha 3 and kick it out the door. It will be a little light on gameplay improvements, but that can't be helped. Next year is all about gameplay and game content!
Bonus points to anyone who can tell me what the test graph above prints when run 😎

near crystal
hasty tinsel
near crystal
pure turret
#

looks very good this game, gl

hasty tinsel
gusty yoke
#

I think I remember seeing you stream some of this on Twitch years(?) ago. Congrats on getting to the point of announcing it on Steam! Just wishlisted.

hasty tinsel
hasty tinsel
#

Yesterday on stream I finally got my most complex particle effect, the smoke pillar, running through generated zig code. The performance gains are pretty significant when compared to the old, interpreted, version. That means I can finally move on to other things 😎

#

Basically the effect now consists of a number of small Zig structs chained together like this:

const Node46 = struct { // Sprinter.Nodes.Math.RandomFloatNode
    const Self = @This();

    pub const InputPin = enum(u16) { Min = 0, Max = 1 };
    pub const OutputPin = enum(u16) { Result = 0 };

    const ReturnSingleValue: bool = false;

    kernel: *Kernel = undefined,
    rng: std.Random.DefaultPrng = undefined,
    singleValuePending: bool = true,
    singleValue: f32 = 0.0,

    pub fn init(self: *Self, kernel: *Kernel) void {
        self.kernel = kernel;
        self.rng = .init(@intCast(self.kernel.merlinEffectPtr));
    }

    pub fn getResult(self: *Self) f32 {
        if (ReturnSingleValue) {
            if (self.singleValuePending) {
                self.singleValue = self.getNewValue();
                self.singleValuePending = false;
            }
            return self.singleValue;
        } else {
            return self.getNewValue();
        }
    }

    pub fn getNewValue(self: *Self) f32 {
        const min = 0.900;
        const max = 1.100;
        return sprinter.math.getRandomFloat(self.rng.random(), min, max);
    }
};

//----------------------------------------------------

const Node49 = struct { // Sprinter.Nodes.Math.VectorNode
    const Self = @This();

    pub const InputPin = enum(u16) { X = 0, Y = 1, Z = 2, W = 3 };
    pub const OutputPin = enum(u16) { Result = 0 };

    kernel: *Kernel = undefined,

    pub fn init(self: *Self, kernel: *Kernel) void {
        self.kernel = kernel;
    }

    pub fn getResult(self: *const Self) sprinter.VecVariant {
        sprinter.maybeUnused(self);
        const x = self.kernel.node27.getX();
        const y = self.kernel.node27.getY();
        const z = self.kernel.node27.getZ();
        return sprinter.vec_variant.init3(x, y, z);
    }
};
calm ocean
#

just wondering, why are you calling your objects NodeN?

hasty tinsel
#

The nodes in the graph don't have names but they do have IDs (the numbers) so using them to make each node unique is pretty handy.

hasty tinsel
#

Oh, and when I say "generated" I mean it is written out by code I have lovingly hand-crafted, not by some LLM/AI thingy.

hasty tinsel
haughty relic
#

this is very cool, visually looks like a mix betwen Scrap Mechanic and Astroneer

hasty tinsel
#

Thanks! You can try out the Pre-Alpha version using the link above!

brave coral
# hasty tinsel

What did you use to create the UI for this ? (if you dont mind answering)

hasty tinsel
#

It has been vastly improved since the video was made, but the main concepts are the same.

brave coral
hasty tinsel
#

Nope, Traction Point is D3D11/12.

#

You can switch in the options menu.

brave coral
#

For the UI?

hasty tinsel
#

For everything.

#

But yes, including the UI.

brave coral
#

Ok thanks

hasty tinsel
#

I've just written a backend for it which works with my renderer, instead of OpenGL.

hasty tinsel
latent birch
hasty tinsel
#

It's an older version (I use NVRHI these days) but 90% of the code is the same.

#

So really, the only closed-source parts is a very basic widgeting system for menus as well as integration with AngelScript.

#

The UI views are populated from AngelScript and I can hot-reload the views as the scripts change allowing me to add and move things at runtime, which means I don't really need a WYSIWYG editor.

latent birch
#

building UIs using SVG is a really interesting idea, idk if i've seen it before

#

makes complete sense in hindsight tho

nova sand
latent birch
#

what do they typically use for that? juce or something?

nova sand
#

but people often use nanovg, cairo and other stuff

#

even imgui

latent birch
#

fair enough!

nova sand
#

rust plugins are becoming more common thanks to nih-plug which supports egui, iced and vizia so these are quite popular

latent birch
#

idk that much about the audio plugin space tbh, despite being interested in it ^-^

nova sand
#

basically, if you can easily embed it in a graphics context, it's good for plugins

nova sand
#

but the topic comes up often in #audio-dev

latent birch
#

wait that channel exists?? hell yea

hasty tinsel
#

Also, it should be noted, I don't actually render SVG shapes as-is at runtime. You really don't want to parse SVG data at runtime, as it is XML-based and kinda gnarly to parse.

#

Instead I parse the SVG data offline and convert it into a sort of bytecode which then translates directly to NanoVG rendering commands at runtime.

#

It does give you resolution independent rendering though. Here's me scaling up a tiny HUD element by a factor of 50 or so, to debug the rendering.

latent birch
#

very nice ^-^

#

yea it makes complete sense, i'd just never really thought of defining the whole UI with svg before

#

i feel like picosvg or tinyvg could be quite nice for this too

hasty tinsel
#

Yeah, you basically just have to convert the data to commands, to render your vector graphics.

#

I just went with NanoSVG because it directly outputs "move to", "line to" "bezier to" etc. commands that you can pipe through to NanoVG.

hasty tinsel
#

Got Zig code hot reloading working in my game engine. 😎 Now if we could just get the new, faster, compiler working on windows I wouldn't have to wait so long for rebuilds. 😅

jade mural
#

is the new compiler coming for windows with 0.16?

hasty tinsel
hasty tinsel
#

FYI, I am moving my live streams over to a new YouTube channel, so be sure to subscribe if you want to be notified when I go live: https://www.youtube.com/@MadrigalGamesLive

karmic matrix
#

@hasty tinsel Are you the person who created a WGPU youtube video several years ago? The 3D modeling of the game looks so familiar to me.

hasty tinsel
#

Yeah, that's me 😄

glad pendant
#

I am sorry, are you by any chance from Finland? The "javascript" from the webgpu video screams finland 😄 It's cool video though!

glad pendant
#

love that accent. I am an ethnical hungarian, and gave it a go to learn finnish back in the days, but all I remember is Mina olet Citrus and sopo pupu. Keep up the good work on the traction point mate ❤️

hasty tinsel
#

Cheers dude! And good luck with learning Finnish, it's a weird language 😄

glad pendant
#

same messed up as hungarian! 😄

onyx bluff
#

I'm mostly interested in the game engine

hasty tinsel
wet mango
#

awesome

hasty tinsel
jovial cipher
#

is the game engine itself written in zig? or is it just the game, I remember seeing your WebGPU video a long time ago, I guess you were working with C++
Tho I can never have your consistency and persistence working on this project, congrats!

#

when did you transition to zig btw, is there a video for that

hasty tinsel
#

I've been using Zig since 2019 or so, first for smaller experiments with other projects (while still using the same engine). I started working on this game in 2023 and it has been written in Zig from the very beginning.

jovial cipher
drifting locust
#

amazing work

hasty tinsel
drifting locust
#

can you send a link to the game engine or is it all in house

hasty tinsel
#

The engine is not released as a general-purpose game engine you can just build games with. It's not quite that polished.

#

Instructions are found here and some of the tools are available here (more to come)

hasty tinsel
hasty tinsel
#

Traction Point now builds with Zig 0.16 🎉 Looking forward to trying out the new incremental compilation. Should be very nice in combination with my hot-reloading support 😎

hasty tinsel
hasty tinsel
hasty tinsel
hasty tinsel
#

Conversation system coming together. Here's a test conversation I slapped together on today's stream. We have camera transitions, custom camera animations, placeholder TTS audio, branching dialogue, remembering state across conversations, etc. Combined with the normal mission scripting, I think this is going to be pretty powerful!

#

Here's the script that produces the above conversation:

VAR firstEntry = true
VAR hasSelectedOption = false

=== main ===
#dink
voice 1
{
    - firstEntry && not hasSelectedOption:
        ROWAN: Hi Vet, there you are!
    - not firstEntry && hasSelectedOption:
        {shuffle:
            - ROWAN: Anything else I can help you with?
            - ROWAN: Anything else?
            - ROWAN: Wanna talk about other things?
        }
    - else:
        ROWAN: Oh, you're back?
}

~ firstEntry = false

+ [Where's everyone?]
    ~ hasSelectedOption = true
    -> whereIsEverone
+ [Elevator powered on yet?]
    ~ hasSelectedOption = true
    -> askAboutElevator
+ [(Leave)]
    ROWAN: Bye!
    ~ hasSelectedOption = false
    -> END
-> DONE

= whereIsEverone
#dink
voice 1
SYSTEM (playcameraanim): 0
ROWAN: Mother's out on a scrap run with Steller.
ROWAN: Diego, went to get some water.
ROWAN: Not sure what Marty's up to actually...
-> main

= askAboutElevator
#dink
voice 1
ROWAN: No, not enough power cells in the receptacles yet.
ROWAN: In fact, why don't you go see if you can find some.
-> main
hasty tinsel
hasty tinsel
hasty tinsel
hasty tinsel
#

Here's what we created during the past two streams. Some lightmap fixup to do still, but I think it turned out pretty well!

hasty tinsel
hasty tinsel
hasty tinsel
hasty tinsel
hasty tinsel
#

Making a new car for Mrs. Florence, a character in the game. I set out to make a station wagon, but it seems to come out as more of an SUV. Fun to drive though! Obviously very WIP model still...

hasty tinsel
hasty tinsel
#

Here's the result from yesterday's stream (+ a little more this morning ;))

hasty tinsel
#

So far I haven't used a whole lot of specular highlights except for the windshields, but figured I should try out making at least these vehicles a bit more shiny. So far so good. I still need to mask away the sheen where dirt has been painted, but I think this is gonna work with my mostly matte art style. Some objects just beg to be a little shiny.

warped lintel
#

dam yo, those driving physics are so smooth

hasty tinsel
hasty tinsel
#

The project reached a bit of a milestone today, as I have finally completed a level for the game. I am sure I'll do some small polish passes, but overall the level should be done, and ready to be put into the campaign. It will also be featured in the demo, releasing later this summer!

hasty tinsel
#

I made a video about my zig gamedev experience so far: https://www.youtube.com/watch?v=HXpUShkr2VQ

So, I am making a game using Zig. How is that working out?

Wishlist Traction Point on Steam: https://store.steampowered.com/app/1485840/Traction_Point/
Join the Discord Server: https://discord.gg/k8CxSpsCnB
Live stream channel: https://www.youtube.com/@MadrigalGamesLive
How I made my Zig gameplay code hot reloadable: https://www.madrigalgames.c...

▶ Play video
upbeat crow
# hasty tinsel I made a video about my zig gamedev experience so far: https://www.youtube.com/w...

I'm finally watching this, and do have a comment re-error handling: I've been using this construct in my projects lately.

pub fn check(v: anytype) @typeInfo(@TypeOf(v)).error_union.payload {
    return v catch |err| fatal(err);
}

pub fn fatal(err: anyerror) noreturn {
    if (@import("builtin").mode == .Debug) @panic(@errorName(err));
    std.debug.print("Fatal error {t}.\n", .{err});
    std.process.exit(1);
}

Whenever I have a boundary between a library which returns errors, and me knowing that the error is unrecoverable, I wrap it with check. Eg, const item = check(array_list.addOne(allocator));

I did at one point have it take a @src() argument, but just got rid of it when I realized that I could call panic and just look at that stack trace. I figure a more refined version of fatal will get whatever stack information a release build is capable of collecting, and put up a pop-up with an easy to copy-paste error message with instructions on how to report bugs. I'm too far away from actually having users to bother with that so far.

hasty tinsel
#

I need to have a think where such a check function would go though, as it would have to be available in most files and it should have as short of a name as possible while remaining readable. This ties a bit into the “name shortening” pet peeve I briefly mentioned in the video.

upbeat crow
#

For me it's in root because fatal has different implementations on different mains, so I put near the top const check = @import("root").check;. I'm assuming you could do something like const check = basis.check;

hasty tinsel
#

Yep I could, that gets tiresome really quickly though, which was kinda my point in the video.

#

But these are small things…

upbeat crow
#

LOL, I paused the video to reply, then unpaused at got the exact moment you were complaining about the lots of imports.

#

"Hey you can do this." *Unpaused video "So I'm doing this and it's a bit annoying"

hasty tinsel
#

Yeah 😁 overall I like the way of being able to “redeclare” anything under any name. I just tend to want a certain set of names available in a lot of files and having to do the shortening manually everywhere is not optimal. It might just be my way of writing zig though….

#

I had a chat about it with Loris after he watched the video and he mentioned a possibility of zls helping with this in the future. Might be nice I guess.

upbeat crow
#

I was going to suggest reducing name stutter, but I rewound and it wasn't too bad. Eg: basis.resource.FooResourcePtr has resource twice, so why not basis.resource.FooPtr. Not saying it definitely makes sense for you, but name stuttering is an easy habbit to pick up from working in languages with poor namespacing.

#

Well. I guess I did suggest it

hasty tinsel
#

I think I might actually want to add a “common declarations” file and add the most used things there 🤔

upbeat crow
#

cd.check

hasty tinsel
#

Yeah, this codebase has code that has survived from 2019 or so, when I really didn’t know how to structure my zig 😆

#

The resource stuff has lots of that

upbeat crow
#

Don't let @vale halo hear me tell you this, but if you really wanted to invoke the dark magic:
Add a build step which takes an input path, a output path, and common declarations. Have it find all of the zig files in the build path, and copy them to the output path while appending the common declarations to the end of the zig file. Then have your zig mod in the build reference the output files.

hasty tinsel
#

Ooh, that’s evil!

upbeat crow
#

I hadn't thought of doing that before now, but it actually might not be a horrible idea for gameplay code specifically.

#

The other suggestion actually is to let files be bigger. sema.zig is 37+k lines of code. When I asked Andrew about having so many lines in one file, he said "The code has to go somewhere", which really stuck with me. Since then I've tended to just be ok with some files being really long.

#

For example, I made a prototype where I had a ~300 line main file, a ~100 line image parsing file, a big file of table for where all the sprites were in the spreadsheet. Then I had literally all of the gameplay code in the last file. I think if I had continued the project, the gameplay code would've maybe ended up in 3 files.

#

Anywho, finished the video. Thanks for making it I like seeing other's detailed feedback.

hasty tinsel
#

Yeah, I have a zig file containing the core engine c++ api's "beautified" version (ie. making the code a little bit more zig friendly) which is 8k lines atm.

#

I'm trying to be very pragmatic these days, and not care if a file is "too long" or whatever, unless it becomes an actual technical problem.

#

But yeah, regarding the shortening, I think I can easily add one more line to the top of the file, especially if it replaces shortenings I now do by hand

upbeat crow
#

Well, the compiler's limit is 4gigs, so good luck on hitting that.

hasty tinsel
#

I've already split my codebase into libraries so I usually begin my files with some combination of:

const std = @import("std");
const basis = @import("basis"); // core engine
const timbre = @import("timbre"); // audio
const goofy = @import("goofy"); // UI
const nemo = @import("nemo"); // scripting and world facts
const merlin = @import("merlin"); // particle effects
const means = @import("../means.zig"); // game root
#

So I think I can manage a const cd = basis.common_declarations; or whatever

hasty tinsel
# upbeat crow Well, the compiler's limit is 4gigs, so good luck on hitting that.

At my day job at a large studio, we've hit MSVC object file limits, having to compile with -bigobj or whatever the flag was. That in turn disabled some other things (this was years ago, don't remember exactly). But yeah, there can be weird limits sometimes, especially with macros or comptime code generating unholy amounts of code.

upbeat crow
#

Fun fact: Java's limit on number of enums is (was? been years) due to the invisible initializer function growing too big. The bytecode jumps inside functions using 16 bit numbers, limiting the function bytecode length to 65k.

hasty tinsel
#

hehe

#

Thanks for your comments and feedback btw, highly appreciated!

formal mural
#

wishlisted

hasty tinsel
# upbeat crow I'm finally watching this, and do have a comment re-error handling: I've been u...

I did play around with this approach a little and now I can do things like this:

var list = basis.check(basis.BoundedArray(i32, 4).init(0));

basis.check(list.append(0));
basis.check(list.append(0));
basis.check(list.append(0));
basis.check(list.append(0));
basis.check(list.append(0)); // Error

Having to type basis.check everywhere is a little annoying but of course it can be shortened a little. What's a bigger problem is that zls seems to completely lose track of the actual returned type, so in the above case it can no longer give me autocomplete suggestions for list, which is kind of a dealbreaker for me for everyday work use. It seems like there a hundred different ways to make zig errors work a little better for gamedev, but none that solves all the small annoyances, at least for me personally...

vale halo
#

In my programs where I don't plan to handle OOM I usually do list.append(0) catch fatal.oom(), where fatal is my namespace containing error reporting functions. oom needs to be defined as returning noreturn to get the best ergonomics.

#

another trick is to "strategically" pick some functions for returning error{OutOfMemory}!whatever and using try in them, to finally collect their result in a single catch fatal.oom() call.

vale halo
# hasty tinsel I did play around with this approach a little and now I can do things like this:...

also this might help you maybe (it doesn't break zls)

const std = @import("std");

pub fn main(init: std.process.Init) !void {
    var list = checkExplicit(std.ArrayList(i32), .initCapacity(init.gpa, 5));
    check(list.append(init.gpa, 42)); // your current check impl
    std.debug.print("{}\n", .{list.items[0]});
}

fn checkExplicit(T: type, val: error{OutOfMemory}!T) T {
    return val catch @panic("oom");
}
hasty tinsel
#

Yeah, I actually already did a version of checkExplicit for just this purpose, but the above example becomes just kinda stupid with that, unless you capture the type into another constant first.

#

Something like var list = basis.checkT(basis.BoundedArray(i32, 4), basis.BoundedArray(i32, 4).init(0));

#

At this point we've left ergonomics behind a long time ago

#

You can do const List = basis.BoundedArray(i32, 4); and then use that, but at that point it's workarounds on top of workarounds.

jade flax
#

Hey this is fantastic, I've been keeping up with your development on this game for a while!
I love Zig but have been torn between using Odin or Zig for my next game project,
Vendor packages aside, would you say you experienced any friction when it comes to actual game architectural patterns/workflows when using Zig? For example I read that theres no safe way to do ZII unless all pointers are marked as optional but not sure if in practice thats even a problem.

Now that you worked so much with Zig on a serious game project, would you say you'd pick Zig over the alternatives again if you could go back in time?

hasty tinsel
#

I think I'd still choose Zig because I like the language a lot. I'm not 100% sold on Odin (or Jai for that matter which has some syntactical similarities to Odin but is otherwise pretty different) as a language.

#

That said, I think the toolchains and "ecosystem" for lack of a better word, around Zig could use a lot of work, and Odin being "batteries included" is pretty neat.

#

But more than anything, just choose one and go with it. Zig and Odin (and C3 and Jai and...) are all perfectly fine.

jade flax
#

awesome thanks for the quick reply, ya I like Zig too much to let it go so looks like it's the right choice for now, I haven't had the chance to try jai but since I actually have access to Zig it seems like the obvious choice right now, thanks! Looking forward to playing your game!

hasty tinsel
#

Join the discord and you can play right now 😉

#

A pre-alpha build, that is

jade flax
#

perfect will do the moment I get a chance!

upbeat crow
# hasty tinsel I did play around with this approach a little and now I can do things like this:...

I don't use zls, so yeah that's not an issue I'd notice.

I did notice you're using bounded array here. When that got removed from the std, I promptly copied it to my own files, and started changing it. Specifically I removed the error return on append and instead made it an assertion. In your case here you could make it a fatal error. So that's another way to potentially tackle some of your issues here: cutting off the errors earlier in the stack, even if that means modifying said stack higher up than normal. Probably only worth it for heavily used things, but food for thought.

hasty tinsel
#

Sure yeah, I have a lot of my own containers as well and non of them use zig errors exactly for this reason 😄

#

Though, I'm sure you'll agree that it's not really a solution to the problem.