How to Create a New Extension Class That Inherits From Built-In Godot Classes in gdext-nim

To create a new extension class that inherits from built-in Godot classes, define a Nim type as a ptr object of Node (or any engine class), annotate it with {.gdsync.}, and register it in your bootstrap file to expose it to the engine.

The gdext-nim library (available at godot-nim/gdext-nim) provides a type-safe Nim interface to the GDExtension API. Creating a new extension class that inherits from built-in Godot classes involves using specific pragmas that handle binding registration, property exposure, and lifecycle callbacks automatically.

Project Setup and Class Scaffolding

Start by generating a new extension project using the CLI wizard. The gdextwiz new-extension command creates a skeleton project including a src/bootstrap.nim registration file and template classes.

According to the wizard source in src/gdext/wizard/subcommands/extension.nim, the generated template includes a ready-made example class at src/classes/gdmyclass.nim that demonstrates the inheritance pattern.

Defining the Extension Class

Create a new Nim file in src/classes/ and define your type with the {.gdsync.} pragma. This pragma, implemented in src/gdext/private/userclass/procs.nim, marks the type for engine registration and generates the necessary glue code.


# src/classes/gdmyclass.nim

import gdext
import gdext/classes/gdNode  # Import the built-in class you want to inherit from

type MyClass* {.gdsync.} = ptr object of Node
  message* {.gdexport.}: string = "This is MyClass."
  address: uint64

Key components of this definition:

  • ptr object of Node — Declares inheritance from the built-in Node class (defined in src/gdext/classes/gdnode.nim). You can substitute Node with any engine class like Sprite2D or CharacterBody2D by importing the corresponding gdext/classes/gdSprite2D module.
  • {.gdsync.} — Required pragma that exposes the type and its members to Godot's ClassDB.
  • {.gdexport.} — Makes the field visible as a property in the Godot editor inspector.

Implementing Methods, Signals, and Lifecycle Callbacks

Define methods using Nim's method syntax combined with the {.gdsync.} pragma to implement Godot's virtual callbacks like _ready and _process.


# Lifecycle callbacks

method ready(self: MyClass) {.gdsync.} =
  echo "MyClass is ready"

method process(self: MyClass; delta: float) {.gdsync.} =
  # Per-frame logic here

  discard

For signals, declare a proc with the {.signal.} pragma. The return type must be Error, and you emit the signal by calling the proc:

proc mySignal(self: MyClass): Error {.gdsync, signal.}

method ready(self: MyClass) {.gdsync.} =
  assert self.mySignal() == ok  # Emit the signal

To expose custom getters and setters for properties, define procs with {.gdsync.} and bind them using gdexport:

proc address_readable(self: MyClass): string {.gdsync, name: "get_address_readable".} =
  self.address.toHex.insertSep(' ', 4)

gdexport "address_readable",
  address_readable,
  proc (self: MyClass; value: string) = (discard)  # Setter (optional)

Note that onInit can serve as a constructor-style hook for initialization logic, but it does not require {.gdsync.} because it operates outside the GDExtension API boundary.

Registering the Class with Godot

Every extension class must be registered in the bootstrap file to be recognized by the engine. The generated src/bootstrap.nim contains a register_classes proc executed at the initialize_scene level.


# src/bootstrap.nim

import gdext
import classes/gdmyclass

proc register_classes {.execon: initialize_scene.} =
  register MyClass  # <-- Add your class here

The register template, defined in the core gdext.nim and utilizing src/gdext/private/classindex.nim, stores the class metadata in Godot's ClassDB at the specified initialization level.

Complete Working Example

Below is a minimal, self-contained class inheriting from Sprite2D that moves right every frame:


# src/classes/gdplayer.nim

import gdext
import gdext/classes/gdSprite2D
import std/math

type Player* {.gdsync.} = ptr object of Sprite2D
  speed* {.gdexport.}: float = 300

method ready(self: Player) {.gdsync.} =
  echo "Player ready with speed: ", self.speed

method process(self: Player; delta: float) {.gdsync.} =
  self.position.x += self.speed * delta

Register it in src/bootstrap.nim:

import gdext
import classes/gdplayer

proc register_classes {.execon: initialize_scene.} =
  register Player

Build and run using the CLI:

gdextwiz run

The command compiles the Nim code into a GDExtension library, copies it to your Godot project, and launches the editor. The Player node will appear in the editor with an exposed Speed property.

Summary

  • Inheritance — Use ptr object of Node (or any built-in class from gdext/classes/) to extend engine functionality.
  • Exposure — Apply {.gdsync.} to types and methods, and {.gdexport.} to fields you want visible in the editor.
  • Lifecycle — Implement ready, process, and other virtuals as Nim methods with {.gdsync.}.
  • Registration — Call register YourClass inside the register_classes proc in bootstrap.nim marked with {.execon: initialize_scene.}.
  • CLI Workflow — Use gdextwiz new-extension to scaffold and gdextwiz run to build and test.

Frequently Asked Questions

How do I choose which built-in Godot class to inherit from?

Import the corresponding module from gdext/classes/ (e.g., import gdext/classes/gdCharacterBody2D) and declare your type as ptr object of CharacterBody2D. The inheritance chain follows Godot's native hierarchy exactly, so inheriting from Node gives you scene tree participation, while Node2D or Node3D adds spatial properties.

What is the difference between {.gdsync.} and {.gdexport.}?

{.gdsync.} is a type or method-level pragma that registers the symbol with Godot's extension system (handled in private/userclass/procs.nim), enabling the engine to instantiate and call it. {.gdexport.} is a field-level annotation that generates property bindings for the editor inspector, creating getters and setters automatically unless you provide custom ones.

Why does my class need to be a ptr object?

Godot's GDExtension API manages object lifecycles through pointers. The ptr object allocation model ensures memory compatibility with the engine's reference counting system. The gdsync macro generates the necessary boilerplate to handle reference counting and pointer safety automatically.

How do I expose a property with custom getter and setter logic?

Define a proc with {.gdsync.} and an explicit name pragma (e.g., name: "get_custom"), then bind it using the gdexport macro with the property name, getter proc, and an optional setter proc. This pattern is demonstrated in the template gdmyclass.nim and allows you to transform internal data (like converting a uint64 address to a readable hex string) while maintaining editor visibility.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →