Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Debug Functions

Runtime value inspection via the F3 debug panel.

Overview

The debug system allows you to register game variables for live editing and monitoring. Press F3 to open the debug panel during development.

Features:

  • Live value editing (sliders, color pickers)
  • Read-only watches
  • Grouped organization
  • Frame control (pause, step, time scale)
  • Zero overhead in release builds

Value Registration

Register editable values in init(). The debug panel will show controls for these values.

debug_register_i32

Registers an editable 32-bit signed integer.

Signature:

#![allow(unused)]
fn main() {
fn debug_register_i32(name_ptr: *const u8, name_len: u32, ptr: *const i32)
}

Example:

#![allow(unused)]
fn main() {
static mut ENEMY_COUNT: i32 = 5;

fn init() {
    unsafe {
        debug_register_i32(b"Enemy Count".as_ptr(), 11, &ENEMY_COUNT);
    }
}
}

debug_register_f32

Registers an editable 32-bit float.

Signature:

#![allow(unused)]
fn main() {
fn debug_register_f32(name_ptr: *const u8, name_len: u32, ptr: *const f32)
}

Example:

#![allow(unused)]
fn main() {
static mut GRAVITY: f32 = 9.8;
static mut JUMP_FORCE: f32 = 15.0;

fn init() {
    unsafe {
        debug_register_f32(b"Gravity".as_ptr(), 7, &GRAVITY);
        debug_register_f32(b"Jump Force".as_ptr(), 10, &JUMP_FORCE);
    }
}
}

debug_register_bool

Registers an editable boolean (checkbox).

Signature:

#![allow(unused)]
fn main() {
fn debug_register_bool(name_ptr: *const u8, name_len: u32, ptr: *const u8)
}

Example:

#![allow(unused)]
fn main() {
static mut GOD_MODE: u8 = 0; // 0 = false, 1 = true

fn init() {
    unsafe {
        debug_register_bool(b"God Mode".as_ptr(), 8, &GOD_MODE);
    }
}
}

debug_register_u8 / u16 / u32

Registers unsigned integers.

#![allow(unused)]
fn main() {
fn debug_register_u8(name_ptr: *const u8, name_len: u32, ptr: *const u8)
fn debug_register_u16(name_ptr: *const u8, name_len: u32, ptr: *const u16)
fn debug_register_u32(name_ptr: *const u8, name_len: u32, ptr: *const u32)
}

debug_register_i8 / i16

Registers signed integers.

#![allow(unused)]
fn main() {
fn debug_register_i8(name_ptr: *const u8, name_len: u32, ptr: *const i8)
fn debug_register_i16(name_ptr: *const u8, name_len: u32, ptr: *const i16)
}

Range-Constrained Registration

debug_register_i32_range

Registers an integer with min/max bounds (slider).

Signature:

#![allow(unused)]
fn main() {
fn debug_register_i32_range(
    name_ptr: *const u8, name_len: u32,
    ptr: *const i32,
    min: i32, max: i32
)
}

Example:

#![allow(unused)]
fn main() {
static mut DIFFICULTY: i32 = 2;

fn init() {
    unsafe {
        debug_register_i32_range(b"Difficulty".as_ptr(), 10, &DIFFICULTY, 1, 5);
    }
}
}

debug_register_f32_range

Registers a float with min/max bounds.

Signature:

#![allow(unused)]
fn main() {
fn debug_register_f32_range(
    name_ptr: *const u8, name_len: u32,
    ptr: *const f32,
    min: f32, max: f32
)
}

Example:

#![allow(unused)]
fn main() {
static mut PLAYER_SPEED: f32 = 5.0;

fn init() {
    unsafe {
        debug_register_f32_range(b"Speed".as_ptr(), 5, &PLAYER_SPEED, 0.0, 20.0);
    }
}
}

debug_register_u8_range / u16_range / i16_range

#![allow(unused)]
fn main() {
fn debug_register_u8_range(name_ptr: *const u8, name_len: u32, ptr: *const u8, min: u32, max: u32)
fn debug_register_u16_range(name_ptr: *const u8, name_len: u32, ptr: *const u16, min: u32, max: u32)
fn debug_register_i16_range(name_ptr: *const u8, name_len: u32, ptr: *const i16, min: i32, max: i32)
}

Compound Types

debug_register_vec2

Registers a 2D vector (two f32s).

Signature:

#![allow(unused)]
fn main() {
fn debug_register_vec2(name_ptr: *const u8, name_len: u32, ptr: *const f32)
}

Example:

#![allow(unused)]
fn main() {
static mut PLAYER_POS: [f32; 2] = [0.0, 0.0];

fn init() {
    unsafe {
        debug_register_vec2(b"Player Pos".as_ptr(), 10, PLAYER_POS.as_ptr());
    }
}
}

debug_register_vec3

Registers a 3D vector (three f32s).

Signature:

#![allow(unused)]
fn main() {
fn debug_register_vec3(name_ptr: *const u8, name_len: u32, ptr: *const f32)
}

debug_register_rect

Registers a rectangle (x, y, width, height as four f32s).

Signature:

#![allow(unused)]
fn main() {
fn debug_register_rect(name_ptr: *const u8, name_len: u32, ptr: *const f32)
}

debug_register_color

Registers a color (RGBA as four u8s).

Signature:

#![allow(unused)]
fn main() {
fn debug_register_color(name_ptr: *const u8, name_len: u32, ptr: *const u8)
}

Example:

#![allow(unused)]
fn main() {
static mut TINT_COLOR: [u8; 4] = [255, 255, 255, 255];

fn init() {
    unsafe {
        debug_register_color(b"Tint".as_ptr(), 4, TINT_COLOR.as_ptr());
    }
}
}

Fixed-Point Registration

For games using fixed-point math.

#![allow(unused)]
fn main() {
fn debug_register_fixed_i16_q8(name_ptr: *const u8, name_len: u32, ptr: *const i16)
fn debug_register_fixed_i32_q8(name_ptr: *const u8, name_len: u32, ptr: *const i32)
fn debug_register_fixed_i32_q16(name_ptr: *const u8, name_len: u32, ptr: *const i32)
fn debug_register_fixed_i32_q24(name_ptr: *const u8, name_len: u32, ptr: *const i32)
}

Watch Functions (Read-Only)

Watches display values without allowing editing.

#![allow(unused)]
fn main() {
fn debug_watch_i8(name_ptr: *const u8, name_len: u32, ptr: *const i8)
fn debug_watch_i16(name_ptr: *const u8, name_len: u32, ptr: *const i16)
fn debug_watch_i32(name_ptr: *const u8, name_len: u32, ptr: *const i32)
fn debug_watch_u8(name_ptr: *const u8, name_len: u32, ptr: *const u8)
fn debug_watch_u16(name_ptr: *const u8, name_len: u32, ptr: *const u16)
fn debug_watch_u32(name_ptr: *const u8, name_len: u32, ptr: *const u32)
fn debug_watch_f32(name_ptr: *const u8, name_len: u32, ptr: *const f32)
fn debug_watch_bool(name_ptr: *const u8, name_len: u32, ptr: *const u8)
fn debug_watch_vec2(name_ptr: *const u8, name_len: u32, ptr: *const f32)
fn debug_watch_vec3(name_ptr: *const u8, name_len: u32, ptr: *const f32)
fn debug_watch_rect(name_ptr: *const u8, name_len: u32, ptr: *const f32)
fn debug_watch_color(name_ptr: *const u8, name_len: u32, ptr: *const u8)
}

Example:

#![allow(unused)]
fn main() {
static mut FRAME_COUNT: u32 = 0;
static mut FPS: f32 = 0.0;

fn init() {
    unsafe {
        debug_watch_u32(b"Frame".as_ptr(), 5, &FRAME_COUNT);
        debug_watch_f32(b"FPS".as_ptr(), 3, &FPS);
    }
}
}

Grouping

debug_group_begin

Starts a collapsible group in the debug panel.

Signature:

#![allow(unused)]
fn main() {
fn debug_group_begin(name_ptr: *const u8, name_len: u32)
}

debug_group_end

Ends the current group.

Signature:

#![allow(unused)]
fn main() {
fn debug_group_end()
}

Example:

#![allow(unused)]
fn main() {
fn init() {
    unsafe {
        debug_group_begin(b"Player".as_ptr(), 6);
        debug_register_vec3(b"Position".as_ptr(), 8, PLAYER_POS.as_ptr());
        debug_register_f32(b"Health".as_ptr(), 6, &PLAYER_HEALTH);
        debug_register_f32(b"Speed".as_ptr(), 5, &PLAYER_SPEED);
        debug_group_end();

        debug_group_begin(b"Physics".as_ptr(), 7);
        debug_register_f32(b"Gravity".as_ptr(), 7, &GRAVITY);
        debug_register_f32(b"Friction".as_ptr(), 8, &FRICTION);
        debug_group_end();
    }
}
}

Frame Control

debug_is_paused

Check if game is paused via debug panel.

Signature:

#![allow(unused)]
fn main() {
fn debug_is_paused() -> i32
}

Returns: 1 if paused, 0 otherwise


debug_get_time_scale

Get the current time scale.

Signature:

#![allow(unused)]
fn main() {
fn debug_get_time_scale() -> f32
}

Returns: Time scale (1.0 = normal, 0.5 = half speed, 2.0 = double)

Example:

#![allow(unused)]
fn main() {
fn update() {
    unsafe {
        if debug_is_paused() != 0 {
            return; // Skip update when paused
        }

        let dt = delta_time() * debug_get_time_scale();
        // Use scaled delta time
    }
}
}

Debug Keyboard Shortcuts

KeyAction
F3Toggle debug panel
F5Pause/unpause
F6Step one frame (while paused)
F7Decrease time scale
F8Increase time scale

Complete Example

#![allow(unused)]
fn main() {
// Game state
static mut PLAYER_X: f32 = 0.0;
static mut PLAYER_Y: f32 = 0.0;
static mut PLAYER_VEL_X: f32 = 0.0;
static mut PLAYER_VEL_Y: f32 = 0.0;
static mut PLAYER_HEALTH: f32 = 100.0;

// Tuning parameters
static mut MOVE_SPEED: f32 = 5.0;
static mut JUMP_FORCE: f32 = 12.0;
static mut GRAVITY: f32 = 25.0;
static mut FRICTION: f32 = 0.9;

// Debug
static mut GOD_MODE: u8 = 0;
static mut SHOW_HITBOXES: u8 = 0;
static mut ENEMY_COUNT: i32 = 5;

fn init() {
    unsafe {
        // Player group
        debug_group_begin(b"Player".as_ptr(), 6);
        debug_watch_f32(b"X".as_ptr(), 1, &PLAYER_X);
        debug_watch_f32(b"Y".as_ptr(), 1, &PLAYER_Y);
        debug_watch_f32(b"Vel X".as_ptr(), 5, &PLAYER_VEL_X);
        debug_watch_f32(b"Vel Y".as_ptr(), 5, &PLAYER_VEL_Y);
        debug_register_f32_range(b"Health".as_ptr(), 6, &PLAYER_HEALTH, 0.0, 100.0);
        debug_group_end();

        // Physics group
        debug_group_begin(b"Physics".as_ptr(), 7);
        debug_register_f32_range(b"Move Speed".as_ptr(), 10, &MOVE_SPEED, 1.0, 20.0);
        debug_register_f32_range(b"Jump Force".as_ptr(), 10, &JUMP_FORCE, 5.0, 30.0);
        debug_register_f32_range(b"Gravity".as_ptr(), 7, &GRAVITY, 10.0, 50.0);
        debug_register_f32_range(b"Friction".as_ptr(), 8, &FRICTION, 0.5, 1.0);
        debug_group_end();

        // Debug options
        debug_group_begin(b"Debug".as_ptr(), 5);
        debug_register_bool(b"God Mode".as_ptr(), 8, &GOD_MODE);
        debug_register_bool(b"Show Hitboxes".as_ptr(), 13, &SHOW_HITBOXES);
        debug_register_i32_range(b"Enemy Count".as_ptr(), 11, &ENEMY_COUNT, 0, 20);
        debug_group_end();
    }
}

fn update() {
    unsafe {
        // Respect debug pause
        if debug_is_paused() != 0 {
            return;
        }

        let dt = delta_time() * debug_get_time_scale();

        // Use tunable values
        PLAYER_VEL_Y += GRAVITY * dt;
        PLAYER_VEL_X *= FRICTION;

        if button_held(0, BUTTON_RIGHT) != 0 {
            PLAYER_VEL_X = MOVE_SPEED;
        }
        if button_held(0, BUTTON_LEFT) != 0 {
            PLAYER_VEL_X = -MOVE_SPEED;
        }
        if button_pressed(0, BUTTON_A) != 0 {
            PLAYER_VEL_Y = -JUMP_FORCE;
        }

        PLAYER_X += PLAYER_VEL_X * dt;
        PLAYER_Y += PLAYER_VEL_Y * dt;
    }
}
}

See Also: System Functions