8000 GitHub - moohaad/gdzig: Zig bindings for Godot 4 · GitHub
[go: up one dir, main page]

Skip to content
moohaadPublic
forked from gdzig/gdzig

About

Zig bindings for Godot 4

Resources

Stars

1 star

Watchers

0 watching

Forks

 
 

Repository files navigation

gdzig

Idiomatic Zig bindings for Godot 4.

DISCLAIMER

This library is currently undergoing rapid development and refactoring as we figure out the best API to expose. Bugs and missing features are expected until a stable version is released. Issue reports, feature requests, and pull requests are all very welcome.

Prerequisites

  1. zig 0.17.0
  2. godot 4.7

Note: gdzig targets Godot 4.7 only. Earlier versions are not supported; pin an older gdzig if you need one.

Note: We are targeting stable releases of Zig only. 0.16.x is no longer supported; use a tag prior to the 0.17 migration if you need it.

Usage:

See the example folder for reference.

zig build

That downloads a matching Godot automatically. To build against a specific version or an engine you already have:

zig build -Dgodot-version=4.7
zig build -Dgodot-path=/absolute/path/to/godot   # must be absolute

In a project of your own, zig build watch rebuilds on every change and restarts Godot, clearing the stale ~-prefixed libraries that otherwise block the next load.

zig build test runs the unit and Godot tests; zig build test-all adds the integration gates that cover the build tooling itself, which is what CI runs. zig build test-docs compiles the complete Zig examples marked in the guides, so API changes cannot leave copy-paste examples silently stale.

The default install also puts gdzig in zig-out/bin B04C ; zig build cli installs just this command. Add that directory to PATH or use the executable's full path. From a project's build root, use it to verify the local toolchain, generated descriptor, native library, and Godot import state before opening the editor:

gdzig doctor                       # project.godot is beside build.zig
gdzig doctor --project project     # split Zig/Godot layout
gdzig doctor --godot /path/to/godot

doctor is read-only and exits nonzero for broken wiring. If Godot is not in PATH, that is only a warning because gdzig can download the selected engine during zig build; pass --godot when you want a particular executable checked. It checks root-level generated descriptors for the current host's debug selector. Build-source checks are advisory; doctor does not load libraries or verify exports.

Starting a project

For an optional Godot bottom panel with Build, Doctor, and class generation, run gdzig setup-editor (or gdzig setup-editor --project project) and enable the addon in Project Settings > Plugins. See editor integration.

For a complete playable introduction, follow Star Catcher: input, typed scenes, signals, resource ownership, a coroutine countdown, and desktop export. zig build test-tutorial builds and play-tests its source.

Fastest route: zig build cli in a gdzig checkout installs gdzig; then gdzig init --name mygame --out mygame scaffolds a working project anywhere. doc/quickstart.md is that path end to end.

From the generated project's directory:

gdzig build
gdzig run -Dgodot-version=4.7
gdzig editor
gdzig watch -Doptimize=safe
gdzig export -- --preset "Windows Desktop" --output ../exports/v1/Game.exe

Build options are forwarded to zig build. run and editor build and import before launching the same Godot selected by the dependency. Pass Godot arguments after --, for example gdzig run -- --headless --quit. watch forwards build options to each rebuild. Existing projects need the run, editor, and watch steps from the current scaffold in their build file. The former standalone scaffolder and its build step have been removed; use gdzig init.

Desktop exports require a matching Godot preset and export templates. The command checks the platform/architecture, imports, and exports into an empty directory outside the project. See exporting for debug mode, limitations, and the build helper for existing projects.

Generate registered classes from the project directory:

gdzig add node Player --base CharacterBody3D
gdzig add resource Inventory
gdzig add plugin Tools

The command creates the Zig file and adds it to the scaffold's shared classes tuple. Nodes get _ready and _physics_process; plugins get editor registration and enter/exit hooks. Existing files are refused. Use --entry src/mygame.zig if entry detection is ambiguous. Custom registration layouts require manual setup; gdzig add --help lists supported bases. With selective bindings, include the chosen base class in your build's class selection.

To build one by hand instead, doc/getting-started.md walks this same setup and finishes with a character you can drive with WASD. What follows is the condensed reference for the files involved.

The extension side is three hand-written files and a generated descriptor. The example is the working reference for all of it.

Your Zig code and a normal Godot project sit side by side, and the build installs the library into the Godot project:

mygame/
  build.zig
  build.zig.zon
  src/main.zig              root module: must export `register`
  project/project.godot     an ordinary Godot project
  project/mygame.gdextension  generated by `zig build`
  project/lib/              the build installs here

Depend on gdzig.

zig fetch --save git+https://github.com/moohaad/gdzig

build.zig. Three calls: take the dependency, make a module that imports it as godot, and hand that module to addExtension.

const gdzig_dep = b.dependency("gdzig", .{
    .target = target,
    .optimize = optimize,
    .@"godot-version" = b.option([]const u8, "godot-version", "Godot version to download"),
    .@"godot-path" = b.option([]const u8, "godot-path", "Path to a Godot executable"),
    // Optional: omit for the full API. Dependencies and ancestors are added.
    .classes = b.option([]const u8, "godot-classes", "Godot classes to generate"),
    // Absolute because a dependency resolves paths from its own build root.
    .godot_project = b.root.joinString(b.allocator, "project") catch @panic("OOM"),
});

const mod = b.createModule(.{
    .root_source_file = b.path("src/main.zig"),
    .target = target,
    .optimize = optimize,
    .imports = &.{.{ .name = "godot", .module = gdzig_dep.module("gdzig") }},
});

const extension = gdzig.addExtension(b, .{
    .name = "mygame",
    .root_module = mod,
    .entry_symbol = "mygame_init",
    .target = target,
    .optimize = optimize,
    // Names every `.tscn` under the project as an import, so
    // `Scene(@embedFile("Player.tscn"))` can reach one. Omit if unused.
    .godot_project = "project",
}) orelse return;

const install_library = b.addInstallFileWithDir(
    extension.output,
    .{ .custom = "../project/lib" },
    extension.filename,
);
const install_manifest = b.addInstallFileWithDir(
    extension.manifest,
    .{ .custom = "../project" },
    extension.manifest_filename,
);
b.default_step.dependOn(&install_library.step);
b.default_step.dependOn(&install_manifest.step);

Passing godot_project to the dependency reads the current Input Map into godot.input.Action, catalogs .tscn files for cross-scene typing, and enables compile-time res:// checks. For example, godot.input.Action.jump exists only while jump is configured in project.godot; renamed and misspelled actions fail during compilation. The separate relative godot_project on addExtension makes scenes importable to @embedFile and runs project preflight checks.

For smaller projects, pass a comma-separated class list to the dependency:

zig build -Dgodot-classes=CharacterBody3D,Camera3D,AnimationPlayer

Both Godot names (GPUParticles2D) and generated gdzig names (GpuParticles2d) are accepted. Bindgen automatically includes inheritance and every class referenced by the selected APIs, while builtins, global enums, utility functions, and native ABI structures remain available. An omitted class is absent from godot.class, so accidentally using it is a compile error. Leave the option unset for the complete Godot class surface.

godot.Scene(@embedFile("levels/Main.tscn")) follows inherited scene bases and uses an instanced scene's root type for its generated Child field. For roots implemented by your extension rather than Godot, use SceneWith and provide a namespace such as struct { pub const Player = PlayerNode; }.

project/mygame.gdextension. Generated by addExtension. Its entry symbol comes from .entry_symbol, and its library selector comes from the resolved target and the actual output filename, so those values cannot drift apart. Install extension.manifest beside project.godot, as above. If the library goes somewhere other than lib/, set .manifest = .{ .library_dir = "path" } on addExtension and install it there.

compatibility_minimum is the engine gdzig itself requires. Claiming an older one does not buy compatibility: Godot loads the extension and gdzig then refuses it, which is a worse error than Godot declining in the first place.

The manifest defaults to reloadable = true, which opts into hot reload. Set .manifest = .{ .reloadable = false } to disable it. Exported properties and the instances themselves survive; a class whose base changed does not, and says so rather than crashing. example/ has a harness -- zig build reload-test -- that reloads the extension and checks what came through.

src/main.zig. gdzig provides the entry point itself; your root module only registers. register is required, unregister is optional -- the registry unregisters anything left behind either way.

pub fn register(r: *Registry) void {
    r.addClass(PlayerNode, r.allocator, .auto);
}

const godot = @import("godot");
const Registry = godot.extension.Registry;
const PlayerNode = @import("PlayerNode.zig");

Then zig build, and open project/ in Godot.

Before writing much, skim doc/what-gdzig-gives-you.md. It is short, and it exists because a real port hand-wrote five things that were already in the box.

Threading

Engine calls are safe from any thread; the scene tree is still main-thread only. See doc/threading.md for the contract and the callDeferred pattern.

Code Sample:

https://github.com/gdzig/gdzig/blob/1cdfec61d185a9440e6419b122a08e003ad3dcde/example/src/GuiNode.zig#L1-L56

Community

Find us in the gdzig Discord server.

About

Zig bindings for Godot 4

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

0