# How to Handle Godot Object Callbacks like `_ready` and `_process` in gdext-nim

> Learn to handle Godot callbacks like _ready and _process in gdext-nim by overriding virtual functions with the {.gdsync.} pragma. Integrate Nim with Godot seamlessly.

- Repository: [godot-nim 4+/gdext-nim](https://github.com/godot-nim/gdext-nim)
- Tags: how-to-guide
- Published: 2026-03-02

---

**In gdext-nim, you implement Godot callbacks by declaring Nim methods with the `{.gdsync.}` pragma that override the virtual functions defined in `gdnode.nim`, which automatically registers them with the engine's virtual method table.**

gdext-nim is the official Nim language binding for Godot 4's GDExtension API. When building games or editor tools with this framework, you must respond to engine lifecycle events through callbacks such as `_ready` and `_process`. These virtual methods are bridged to Nim through a compile-time registration system that eliminates manual boilerplate.

## Understanding the Virtual Method Architecture

Godot's engine invokes callbacks through a virtual method table (VMT) stored on each class. In `gdnode.nim`, gdext-nim pre-defines registration helpers that wire these engine calls to your Nim implementations.

### The Registration Mechanism

The binding layer provides typed registration functions for each callback:

| Callback | Registration Function | Location |
|----------|---------------------|----------|
| `_ready` | `registerVirtual_ready` | `src/gdext/classes/gdnode.nim` |
| `_process` | `registerVirtual_process` | `src/gdext/classes/gdnode.nim` |
| `_physics_process` | `registerVirtual_physicsProcess` | `src/gdext/classes/gdnode.nim` |

When you declare a method with the correct signature and `{.gdsync.}` pragma, the compiler generates a call to the appropriate `registerVirtual_*` function. This stores a C-compatible function pointer in the class's `vmethods` table, which Godot invokes when the corresponding event occurs.

### Method Signature Requirements

Your callback methods must match the expected signatures exactly. The base definitions in `gdnode.nim` use Nim's `method` dispatch, requiring the first parameter to be `self: YourClassType`.

## Implementing Core Lifecycle Callbacks

To receive engine callbacks, define a `ptr object` inheriting from a Godot base class and override the virtual methods.

### Overriding `_ready`

The `ready` method executes once when the node enters the scene tree. Declare it with no return value and the `{.gdsync.}` pragma:

```nim
import gdext
import gdext/classes/gdNode

type MyNode* {.gdsync.} = ptr object of Node

method ready(self: MyNode) {.gdsync.} =
  ## Called when the node enters the scene tree.

  echo "MyNode is initialized"

```

### Overriding `_process`

The `process` method runs every frame, receiving the frame time `delta` as a parameter. You must enable processing via `set_process(true)` (typically in `ready`) or the engine will skip this callback:

```nim
method ready(self: MyNode) {.gdsync.} =
  self.set_process(true)

method process(self: MyNode; delta: float) {.gdsync.} =
  ## Called each frame while processing is enabled.

  self.position.x += 100.0 * delta

```

### Using the `onInit` Constructor Hook

Unlike `ready`, `onInit` is a gdext-nim-specific hook that runs immediately when the native object is allocated, before Godot initializes the node:

```nim
method onInit(self: MyNode) =
  ## Runs once during native object construction.

  self.custom_id = cast[uint64](self)

```

Use `onInit` for low-level initialization that must occur before any Godot callbacks or property setters run.

## Complete Working Example

Below is a minimal, compile-ready class that demonstrates property export, the `ready` callback, and frame-based movement:

```nim

# src/classes/player.nim

import gdext
import gdext/classes/gdNode2D

type Player* {.gdsync.} = ptr object of Node2D
  speed*: float = 400.0  # Exported to the Godot editor

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

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

```

When this code compiles, the `{.gdsync.}` pragma on the type generates the GDExtension class registration. The method pragmas trigger the insertion of function pointers into the virtual method table, allowing Godot's C++ core to dispatch `_ready` and `_process` calls directly into your Nim methods.

## Enabling Physics Process

For physics-frame logic, override `physics_process` and enable it separately:

```nim
method ready(self: MyNode) {.gdsync.} =
  self.set_physics_process(true)

method physicsProcess(self: MyNode; delta: float) {.gdsync.} =
  ## Runs at fixed timestep (default 60 TPS).

  self.velocity = self.calculate_movement(delta)

```

Note that the Nim method name uses camelCase (`physicsProcess`) while the engine callback is `_physics_process`. The registration macro maps these correctly.

## Summary

- **Declare callbacks as methods** with the exact signature (`self: YourClass`) and the `{.gdsync.}` pragma to auto-register them via `registerVirtual_*` functions in `gdnode.nim`.
- **Enable processing** explicitly using `self.set_process(true)` or `self.set_physics_process(true)` inside `ready`, or the engine will not invoke `process` or `physicsProcess`.
- **Use `onInit`** for construction-time logic that must run before Godot initializes the node properties.
- **Inherit from Godot classes** using `ptr object of Node` (or `Node2D`, `Control`, etc.) to ensure proper memory layout and VMT compatibility.

## Frequently Asked Questions

### How do I know if my callback is actually registered with Godot?

If you declare a method with `{.gdsync.}` and the correct signature, the gdext-nim macro system automatically inserts the registration code at compile time. You can verify this by checking that your class compiles without errors and that the method executes when you run the scene. The bridge code in `gdnode.nim` handles the `vmethods` table insertion transparently.

### Why isn't my `process` method being called?

The `process` callback only runs if processing is enabled on the node. You must call `self.set_process(true)` (or `self.set_physics_process(true)` for physics frames), typically inside your `ready` method. Without this flag, Godot skips the node during the main loop iteration to save performance.

### Can I rename the Nim method to something other than `ready` or `process`?

No, you must use the exact method names `ready`, `process`, and `physicsProcess` (camelCase for the latter) because the registration macros in `gdnode.nim` look for these specific identifiers when building the virtual method table. Using different names would result in the engine calling the base stub implementation instead of your override.

### What is the difference between `onInit` and `ready`?

`onInit` is a gdext-nim-specific hook that runs immediately when the native Nim object is allocated, before Godot has finished setting up the node or its properties. The `ready` callback runs later, once the node has entered the scene tree and all its children are initialized. Use `onInit` for native memory setup and `ready` for game logic initialization that depends on the scene state.