DEV Community

Fabio Arnold
Fabio Arnold

Posted on • Edited on

Setup Zig for Gamedev

UPDATE 2023-04-02: Updated the guide for the latest Zig version 0.11.0-dev.2336+5b82b4004

This is a step by step guide on how to get started with game development using the Zig programming language on Windows.

TL;DR, here's an 8 min video of every step:

Sadly, the video is outdated because it uses an old version of Zig (0.8.0-dev.1463+f950489ed). That's why the written guide is recommended.

Visual Studio Code

We're going to use Visual Studio Code as our IDE. It will provide us with syntax highlighting, code completion and an interactive debugging experience. So go to https://visualstudio.microsoft.com/downloads/ download and install Visual Studio Code.

Zig

Out of the box Visual Studio Code doesn't support the Zig language. So, first we need to grab the Zig compiler itself. Since Zig is currently pre 1.0 I recommend getting the latest master build from https://ziglang.org/download/ for your platform (at the time of writing zig-windows-x86_64-0.11.0-dev.2336+5b82b4004.zip). To make the zig command available from the command line extract the downloaded archive to a known location: e.g. C:\Users\<Username>\Downloads\zig and add the location to your PATH variable.

This is how you can edit your PATH variable on Windows:

  1. Search for "advanced system settings" in your start menu (be careful not to perform a Bing web search 😅)
  2. Click on environment variables
  3. Now you can edit the PATH for your user
  4. Add the location where you extracted Zig to your PATH
  5. You can verify the zig executable is found by running zig version from a new cmd prompt

Now you are able build Zig software from the command line! 😊

Setup Zig development in Visual Studio Code

In Visual Studio Code we want to install the Zig Language extension (extension id: ziglang.vscode-zig) to get features such as completions, goto definition, find references, rename symbol, and format code.

For debugging we want the C/C++ extension (extension id: ms-vscode.cpptools). If you're still unable to set breakpoints in your code go to Visual Studio Code's settings and activate "Allow Breakpoints Everywhere".

Now that everything is installed we can start our first Zig project. Choose "File > Open Folder...". I create a folder hello-gamedev and click Open. Then press Ctrl + J to open the terminal and type in zig init-exe. This will create the main source file of your project in src/main.zig and a build.zig file which tells Zig how to build your project.

To build your project we want to create a build task in Visual Studio Code. Hit Ctrl + Shift + P and run >Tasks: Configure Task. Then choose Create tasks.json from template, and then the Others tempate. Edit your tasks.json to look like this:



{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "build",
            "type": "shell",
            "command": "zig build",
            "group": "build",
            "problemMatcher": [
                "$gcc"
            ]
        }
    ]
}


Enter fullscreen mode Exit fullscreen mode

Now press Ctrl + Shift + B again and your project should build.

Finally, let's run your project from Visual Studio Code. Hit Ctrl + Shift + D to go to the debug activity and click on the link to create a launch.json file. You can choose any debugger. We will overwrite the contents anyways with the following:



{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "(Windows) Launch",
            "type": "cppvsdbg",
            "request": "launch",
            "program": "${workspaceFolder}/zig-out/bin/hello-gamedev.exe",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${workspaceFolder}",
            "environment": [],
            "preLaunchTask": "build"
        }
    ]
}


Enter fullscreen mode Exit fullscreen mode

Your program should run if you hit F5 and the breakpoints you have set in your code should be hit.

Graphics

Having only text can be a bit boring. So, let's create a window for to draw some fancy graphics! I recommend the SDL2 library to do this. Grab SDL2-devel-2.26.4-VC.zip and extract the archive to a known location such as C:\lib\. Edit your build.zig so it looks like this:



const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const exe = b.addExecutable(.{
        .name = "hello-gamedev",
        .root_source_file = .{ .path = "src/main.zig" },
        .target = target,
        .optimize = optimize,
    });

    const sdl_path = "C:\\lib\\SDL2-2.26.4\\";
    exe.addIncludePath(sdl_path ++ "include");
    exe.addLibraryPath(sdl_path ++ "lib\\x64");
    b.installBinFile(sdl_path ++ "lib\\x64\\SDL2.dll", "SDL2.dll");
    exe.linkSystemLibrary("sdl2");
    exe.linkLibC();
    exe.install();
}


Enter fullscreen mode Exit fullscreen mode

Now paste this code which draws three colored squares using SDL into your main.zig:



const std = @import("std");
const c = @cImport({
    @cInclude("SDL.h");
});

pub fn main() !void {
    _ = c.SDL_Init(c.SDL_INIT_VIDEO);
    defer c.SDL_Quit();

    var window = c.SDL_CreateWindow("hello gamedev", c.SDL_WINDOWPOS_CENTERED, c.SDL_WINDOWPOS_CENTERED, 640, 400, 0);
    defer c.SDL_DestroyWindow(window);

    var renderer = c.SDL_CreateRenderer(window, 0, c.SDL_RENDERER_PRESENTVSYNC);
    defer c.SDL_DestroyRenderer(renderer);

    mainloop: while (true) {
        var sdl_event: c.SDL_Event = undefined;
        while (c.SDL_PollEvent(&sdl_event) != 0) {
            switch (sdl_event.type) {
                c.SDL_QUIT => break :mainloop,
                else => {},
            }
        }

        _ = c.SDL_SetRenderDrawColor(renderer, 0xff, 0xff, 0xff, 0xff);
        _ = c.SDL_RenderClear(renderer);
        var rect = c.SDL_Rect{ .x = 0, .y = 0, .w = 60, .h = 60 };
        const a = 0.001 * @intToFloat(f32, c.SDL_GetTicks());
        const t = 2 * std.math.pi / 3.0;
        const r = 100 * @cos(0.1 * a);
        rect.x = 290 + @floatToInt(i32, r * @cos(a));
        rect.y = 170 + @floatToInt(i32, r * @sin(a));
        _ = c.SDL_SetRenderDrawColor(renderer, 0xff, 0, 0, 0xff);
        _ = c.SDL_RenderFillRect(renderer, &rect);
        rect.x = 290 + @floatToInt(i32, r * @cos(a + t));
        rect.y = 170 + @floatToInt(i32, r * @sin(a + t));
        _ = c.SDL_SetRenderDrawColor(renderer, 0, 0xff, 0, 0xff);
        _ = c.SDL_RenderFillRect(renderer, &rect);
        rect.x = 290 + @floatToInt(i32, r * @cos(a + 2 * t));
        rect.y = 170 + @floatToInt(i32, r * @sin(a + 2 * t));
        _ = c.SDL_SetRenderDrawColor(renderer, 0, 0, 0xff, 0xff);
        _ = c.SDL_RenderFillRect(renderer, &rect);
        c.SDL_RenderPresent(renderer);
    }
}


Enter fullscreen mode Exit fullscreen mode

Hit F5 and after the build is complete you should have this on your screen:

three colored squares

Congratulations! 🎉 Now you can finally start programming games in Zig.

Top comments (8)

Collapse
 
zealouswombat profile image
ZealousWombat

Got the example running on mac using a homebrew installed SDL2 using the following build.zig.

const std = @import("std");

pub fn build(b: *std.build.Builder) void {
    const target = b.standardTargetOptions(.{ .default_target = .{ .abi = .gnu } });
    const mode = b.standardReleaseOptions();

    const exe = b.addExecutable("hello-gamedev", "src/main.zig");
    exe.setTarget(target);
    exe.setBuildMode(mode);

    exe.addIncludeDir("/usr/local/include/SDL2");
    exe.addLibPath("/usr/local/lib");
    exe.linkSystemLibrary("sdl2");
    exe.linkLibC();
    exe.install();
}
Enter fullscreen mode Exit fullscreen mode

Thanks for the example!

Collapse
 
fabioarnold profile image
Fabio Arnold • Edited

Nice one!
I think you don't even need exe.addIncludeDir and exe.addLibPath since Zig makes use of pkg-config. Given you have pkg-config installed.

Collapse
 
fabioarnold profile image
Fabio Arnold

I agree. Zig is awesome. 😃 I think it makes systems programming really fun.

You can include C header files which automatically get translated by Zig's translate C feature. You can also link C libraries. So it integrates well with existing C/C++ code. 😊

The output is native by default. This means x86_64 on my machine. But since Zig uses LLVM it can generate code for any processor architecture supported by LLVM.

Collapse
 
jpv001 profile image
jpv001

A few little updates needed for zig version 0.14

build.zig
Replace src/deps/ with your path. The .install() call is no longer needed.

    exe.addIncludePath(b.path("src/deps/SDL2-2.26.4/include"));
    exe.addLibraryPath(b.path("src/deps/SDL2-2.26.4/lib/x64"));
    b.installBinFile("src/deps/SDL2-2.26.4/lib/x64/SDL2.dll", "SDL2.dll");
    exe.linkSystemLibrary("sdl2");
    exe.linkLibC();
Enter fullscreen mode Exit fullscreen mode

main.zig
@floatToInt and @intToFloat are no longer supported, they now have newer syntax to do the same job, here's the two you'll need:

const a = 0.001 * @as(f32, @floatFromInt(c.SDL_GetTicks()));
...
rect.x = 290 + @as(i32, @intFromFloat(r * @cos(a)));
Enter fullscreen mode Exit fullscreen mode
Collapse
 
erezavidan profile image
Erez

for those interested in how to fix the syntax of the build to compile successfully,
change the relevant lines to these:

exe.addIncludePath(std.Build.LazyPath{ .path = sdl_path ++ "include" });
exe.addLibraryPath(std.Build.LazyPath{ .path = sdl_path ++ "lib\\x64" });

Collapse
 
rofrol profile image
Roman Frołow
Collapse
 
laurentongaro profile image
laurentOngaro

thanks for this post !
For now I just tested the first part of the tuto for setting up the debugger and it worked like a charm for me with ZIG 0.12 and VScode 1.85.0.

Collapse
 
lunarum profile image
Remco S. van Maanen • Edited

Thanks, this helped me setting up my VSCode Run/Debug for Zig
I'm going to use SDL (good tip!) later on for my Vic20 emulator (yes, I know qemu is better, but it's fun to build my own)