module Godot

Overview

LibGodot for Crystal

High-performance Crystal bindings and 2-way host language integration for Godot Engine 4.8+.

Overview

LibGodot enables Crystal to act as the primary host language for Godot games, combining Crystal's LLVM-compiled speed and Ruby-like elegance with Godot's powerful scene tree, rendering, and editor tooling.

Key Features

Basic Example

require "libgodot"

node Player < CharacterBody3D do
  @[Export(range: 50.0_f32..800.0_f32, step: 10.0_f32)]
  property speed : Float32 = 300.0_f32

  @[Export(range: 100.0_f32..1000.0_f32, step: 25.0_f32)]
  property jump_velocity : Float32 = 450.0_f32

  signal health_changed(new_health : Int32, max_health : Int32)
  signal died

  def _ready
    puts "Player ready!"
  end

  def _physics_process(delta : Float64) : Void
    vel = velocity
    unless is_on_floor
      vel.y -= 980.0_f32 * delta.to_f32
    end
    if Input.is_action_just_pressed("jump") && is_on_floor
      vel.y = @jump_velocity
    end
    self.velocity = vel
    move_and_slide
  end
end

Extended Modules

Direct including types

Defined in:

editor/crystal_panel.cr
editor/debugger/crystal_debugger_plugin.cr
editor/debugger/debugger_session_tab.cr
editor/debugger/session_controller.cr
lapis.cr
lapis/color.cr
lapis/core/frame_arena.cr
lapis/core/static_signal_table.cr
lapis/cs_extras/signal_operators.cr
lapis/dsl/frame_arena_dsl.cr
lapis/extras/operators.cr
lapis/extras/wildcards.cr
lapis/gd_extras/editor_extras.cr
libgodot/autoload.cr
libgodot/bridge.cr
libgodot/channel.cr
libgodot/classes/audio_stream.cr
libgodot/classes/camera3d.cr
libgodot/classes/canvas_item.cr
libgodot/classes/character_body2d.cr
libgodot/classes/character_body3d.cr
libgodot/classes/collision_object2d.cr
libgodot/classes/collision_object3d.cr
libgodot/classes/collision_shape3d.cr
libgodot/classes/control.cr
libgodot/classes/input.cr
libgodot/classes/input_event.cr
libgodot/classes/main_loop.cr
libgodot/classes/node.cr
libgodot/classes/node2d.cr
libgodot/classes/node3d.cr
libgodot/classes/node_path.cr
libgodot/classes/object.cr
libgodot/classes/packed_scene.cr
libgodot/classes/physics_body2d.cr
libgodot/classes/physics_body3d.cr
libgodot/classes/progress_bar.cr
libgodot/classes/range.cr
libgodot/classes/ray_cast3d.cr
libgodot/classes/ref_counted.cr
libgodot/classes/resource.cr
libgodot/classes/scene_tree.cr
libgodot/classes/slider.cr
libgodot/classes/spin_box.cr
libgodot/classes/texture.cr
libgodot/collections.cr
libgodot/debugger/agent.cr
libgodot/debugger/context_classifier.cr
libgodot/debugger/decompiler.cr
libgodot/debugger/radare_driver.cr:14
libgodot/debugger/radare_driver.cr:712
libgodot/diagnostics.cr
libgodot/diagnostics/dispatch_profiler.cr
libgodot/diagnostics/leak_tracker.cr
libgodot/diagnostics/signal_spy.cr
libgodot/diagnostics/tombstone_tracker.cr
libgodot/doc_macro.cr
libgodot/docs.cr
libgodot/editor.cr
libgodot/editor_script_creation.cr
libgodot/errors.cr
libgodot/extensions.cr
libgodot/extensions/audio_server.cr
libgodot/extensions/config_file.cr
libgodot/extensions/image.cr
libgodot/extensions/input.cr
libgodot/extensions/multiplayer.cr
libgodot/extensions/node.cr
libgodot/extensions/packed_scene.cr
libgodot/extensions/physics.cr
libgodot/extensions/primitives.cr
libgodot/extensions/query.cr
libgodot/extensions/resource.cr
libgodot/extensions/scene_tree.cr
libgodot/extensions/spatial.cr
libgodot/extensions/timer.cr
libgodot/extensions/tween.cr
libgodot/extensions/viewport.cr
libgodot/generated/classes/classes_part1.cr
libgodot/generated/classes/classes_part2.cr
libgodot/generated/classes/classes_part3.cr
libgodot/generated/classes/classes_part4.cr
libgodot/generated/classes/classes_part5.cr
libgodot/generated/classes/classes_part6.cr
libgodot/generated/global_enums.cr
libgodot/generated/singletons.cr
libgodot/instance.cr
libgodot/logging.cr
libgodot/logging/channel.cr
libgodot/logging/filter.cr:1
libgodot/logging/filter.cr:125
libgodot/logging/logger.cr
libgodot/logging/macros.cr
libgodot/logging/record.cr
libgodot/logging/sink.cr
libgodot/macros.cr
libgodot/node_context.cr
libgodot/object.cr
libgodot/script.cr
libgodot/script/highlighter.cr
libgodot/script/language.cr
libgodot/script/lsp.cr
libgodot/script/resource_format.cr
libgodot/script/script.cr
libgodot/signals/bound_signal.cr
libgodot/signals/subscription.cr
libgodot/signals/typed_signal.cr
libgodot/system_io.cr
libgodot/testing.cr
libgodot/thread_safety.cr
libgodot/timer.cr:5
libgodot/timer.cr:222
libgodot/types/aabb.cr
libgodot/types/basis.cr
libgodot/types/color.cr
libgodot/types/plane.cr
libgodot/types/quaternion.cr
libgodot/types/rect2.cr
libgodot/types/rid.cr
libgodot/types/transform2d.cr
libgodot/types/transform3d.cr
libgodot/types/vector2.cr
libgodot/types/vector3.cr
libgodot/types/vector4.cr
libgodot/variant.cr

Constant Summary

CUSTOM_EXPORT_MACROS = {} of StringLiteral => ASTNode
EMPTY_VARIANT_ARGS = [] of Variant

Pre-allocated empty Variant array for zero-allocation parameterless signal dispatch.

MIN_CRYSTAL_VERSION = "1.20.0"
TARGET_CRYSTAL_VERSION = "1.21.0"
TARGET_GODOT_VERSION = "4.8-beta2"
VERSION = "0.2.3"

Class Method Summary

Macro Summary

Class Method Detail

def self.accessibility_server : AccessibilityServer #

def self.after(delay : ::Time::Span | Number, node : Godot::Node | Nil = nil, &block : TimerHandle -> Void) : TimerHandle #

Schedules a one-shot delay that fires after .delay. If node is provided, automatically cancels if the node is destroyed before expiry.


def self.audio_server : AudioServer #

def self.autoload(type : T.class) : T forall T #

Retrieves an autoload singleton instance by class type


def self.autoload(name : String) : Godot::Node | Nil #

Retrieves an autoload singleton instance by name


def self.await(target : Godot::Object, signal_name : String, timeout_sec : Float64 | Nil = nil) : AwaitStatus(Array(Variant)) #

Cooperatively awaits until the named signal is emitted on the target object. Returns an AwaitStatus(Array(Variant)) indicating completion, timeout, or object disposal (freed?).


def self.await(target : Godot::Object, signal_name : String, timeout_sec : Number) : AwaitStatus(Array(Variant)) #

def self.await(signal : Godot::TypedSignal(*T), timeout_sec : Float64 | Nil = nil) forall T #

Cooperatively awaits a typed signal.


def self.await(seconds : Number, timeout_sec : Float64 | Nil = nil) : AwaitStatus(Nil) #

Cooperatively pauses execution for the given duration in seconds. Returns AwaitStatus(Nil) indicating completion or timeout. Safe for use in cooperative fibers without blocking the Godot main loop.


def self.await(signal : Godot::TypedSignal(*T), timeout_sec : Number) forall T #

def self.await(seconds : Number, timeout_sec : Number) : AwaitStatus(Nil) #

def self.await(span : ::Time::Span, timeout_sec : Float64 | Nil = nil) : AwaitStatus(Nil) #

Cooperatively pauses execution for the given Time::Span duration.


def self.await(span : ::Time::Span, timeout_sec : Number) : AwaitStatus(Nil) #

def self.await(signal : Godot::BoundSignal, timeout_sec : Float64 | Nil = nil) #

Cooperatively awaits a bound signal.


def self.await(signal : Godot::BoundSignal, timeout_sec : Number) #

def self.await(timer : SceneTreeTimer, timeout_sec : Float64 | Nil = nil) : AwaitStatus(Nil) #

=========================================================================== SceneTreeTimer Cooperative Await

Cooperatively awaits a SceneTreeTimer until its countdown expires. Returns AwaitStatus(Nil) indicating completion, timeout, or disposal (freed?).


def self.await(timer : SceneTreeTimer, timeout_sec : Number) : AwaitStatus(Nil) #

def self.await!(target : Godot::Object, signal_name : String, timeout_sec : Float64 | Nil = nil) : Array(Variant) #

Cooperatively awaits until the named signal is emitted on the target object. Returns the emitted arguments as Array(Variant), or raises Godot::DisposedObjectError if the target is freed, or Godot::TimeoutError if timeout_sec expires.


def self.await!(target : Godot::Object, signal_name : String, timeout_sec : Number) : Array(Variant) #

def self.await!(signal : Godot::TypedSignal(*T), timeout_sec : Float64 | Nil = nil) forall T #

def self.await!(seconds : Number, timeout_sec : Float64 | Nil = nil) : Void #

Cooperatively pauses execution for the given duration in seconds. Raises Godot::TimeoutError if timeout_sec expires before the delay completes.


def self.await!(signal : Godot::TypedSignal(*T), timeout_sec : Number) forall T #

def self.await!(seconds : Number, timeout_sec : Number) : Void #

def self.await!(span : ::Time::Span, timeout_sec : Float64 | Nil = nil) : Void #

def self.await!(span : ::Time::Span, timeout_sec : Number) : Void #

def self.await!(signal : Godot::BoundSignal, timeout_sec : Float64 | Nil = nil) #

def self.await!(signal : Godot::BoundSignal, timeout_sec : Number) #

def self.await!(timer : SceneTreeTimer, timeout_sec : Float64 | Nil = nil) : Void #

Cooperatively awaits a SceneTreeTimer, raising DisposedObjectError if freed or TimeoutError if timeout_sec expires.


def self.await!(timer : SceneTreeTimer, timeout_sec : Number) : Void #

def self.await?(target : Godot::Object, signal_name : String, timeout_sec : Float64 | Nil = nil) : Array(Variant) | Nil #

Cooperatively awaits until the named signal is emitted on the target object. Returns the emitted arguments as Array(Variant)?, or nil if timeout_sec expires or the target is freed.


def self.await?(target : Godot::Object, signal_name : String, timeout_sec : Number) : Array(Variant) | Nil #

def self.await?(signal : Godot::TypedSignal(*T), timeout_sec : Float64 | Nil = nil) forall T #

def self.await?(seconds : Number, timeout_sec : Float64 | Nil = nil) : Bool #

Cooperatively pauses execution for the given duration in seconds. Returns true if the delay completed, or false if timeout_sec expired.


def self.await?(signal : Godot::TypedSignal(*T), timeout_sec : Number) forall T #

def self.await?(seconds : Number, timeout_sec : Number) : Bool #

def self.await?(span : ::Time::Span, timeout_sec : Float64 | Nil = nil) : Bool #

def self.await?(span : ::Time::Span, timeout_sec : Number) : Bool #

def self.await?(signal : Godot::BoundSignal, timeout_sec : Float64 | Nil = nil) #

def self.await?(signal : Godot::BoundSignal, timeout_sec : Number) #

def self.await?(timer : SceneTreeTimer, timeout_sec : Float64 | Nil = nil) : Bool | Nil #

Cooperatively awaits a SceneTreeTimer, returning true on completion, false on timeout, or nil if freed.


def self.await?(timer : SceneTreeTimer, timeout_sec : Number) : Bool | Nil #

def self.awaiting_fiber_count : Int32 #

def self.awaiting_fiber_count=(awaiting_fiber_count : Int32) #

def self.breakpoint(can_continue : Bool = true) : Void #

Pauses execution in Godot's engine debugger or attached radare2 session


def self.camera_server : CameraServer #

def self.channel(name : String) : LogChannel #

Access or register a user-defined log channel


def self.class_db : ClassDB #

def self.clear_signal_subscriptions(target_id : UInt64, yield_awaiters : Bool = true) : Void #

Cleans up all signal subscriptions associated with a target instance ID (as emitter or receiver). Marks emitter subscriptions as freed and optionally yields on the Main Thread so awaiting fibers unblock immediately.


def self.configure_channel(name : String, status : String = "active", min_level : LogLevel | Nil = nil, &block : LogChannel -> ) : LogChannel #

Configure a user-defined log channel with status, level, and filters


def self.create(class_name : String) : Node | Nil #

Constructs a new native Godot engine object of the given class name (e.g. "Node2D", "MeshInstance3D", "BoxMesh")


def self.create(type : T.class) : T forall T #

Constructs a new native Godot engine object and wraps it in the given Crystal class


def self.create(type : T.class, &block : T -> ) : T forall T #

Constructs a new native Godot engine object, wraps it in T, and configures it in a block


def self.debug(*args) #

Alias for .print_verbose


def self.delay(seconds : Number, timeout_sec : Float64 | Nil = nil) : AwaitStatus(Nil) #

Cooperatively pauses execution for the given duration in seconds. Alias to Godot.await(seconds).


def self.delay(seconds : Number, timeout_sec : Number) : AwaitStatus(Nil) #

def self.delay(span : ::Time::Span, timeout_sec : Float64 | Nil = nil) : AwaitStatus(Nil) #

Cooperatively pauses execution for the given Time::Span duration.


def self.delay(span : ::Time::Span, timeout_sec : Number) : AwaitStatus(Nil) #

def self.delay!(seconds : Number, timeout_sec : Float64 | Nil = nil) : Void #

def self.delay!(seconds : Number, timeout_sec : Number) : Void #

def self.delay!(span : ::Time::Span, timeout_sec : Float64 | Nil = nil) : Void #

def self.delay!(span : ::Time::Span, timeout_sec : Number) : Void #

def self.delay?(seconds : Number, timeout_sec : Float64 | Nil = nil) : Bool #

def self.delay?(seconds : Number, timeout_sec : Number) : Bool #

def self.delay?(span : ::Time::Span, timeout_sec : Float64 | Nil = nil) : Bool #

def self.delay?(span : ::Time::Span, timeout_sec : Number) : Bool #

def self.dispatch_profiler #

def self.display_server : DisplayServer #

def self.dump_dispatch_profile(io : IO = STDOUT, top_n : Int32 = 20) : Void #

def self.dump_leaks(io : IO = STDERR) : Int32 #

def self.editor_hint? : Bool #

Returns true if the code is currently executing inside the Godot Editor


def self.editor_interface : EditorInterface #

def self.editor_text_input_focused? : Bool #

Returns true if running inside the Godot Editor and a text input control (LineEdit, TextEdit, or CodeEdit, such as an active EditorSpinSlider numeric input or Inspector string field) currently holds GUI keyboard focus. Useful for @[Tool] scripts to avoid rebuilding heavy geometry or resources mid-keystroke while the user is typing a value in the Inspector.


def self.emit(signal : Godot::TypedSignal(*T), *args : *T) : Void forall T #

Emits a typed signal with compile-time type safety.


def self.emit(signal : Godot::BoundSignal, *args) : Void #

Emits a bound signal dynamically on its target object.


def self.engine : Engine #

def self.engine_debugger : EngineDebugger #

def self.every(interval : ::Time::Span | Number, node : Godot::Node | Nil = nil, &block : TimerHandle -> Void) : TimerHandle #

Schedules a recurring timer that fires every interval. If node is provided, automatically cancels when the node is destroyed.


def self.gd_extension_manager : GDExtensionManager #

def self.gd_script_language_protocol : GDScriptLanguageProtocol #

def self.geometry2_d : Geometry2D #

def self.geometry3_d : Geometry3D #

def self.get_tree : SceneTree #

Returns the global SceneTree, or raises if not yet booted


def self.get_tree? : SceneTree | Nil #

Returns the SceneTree if engine is currently running, or nil in headless/standalone specs


def self.has_signal_subscribers?(target_id : UInt64, signal_name : String) : Bool #

Returns true if (target_id, signal_name) has any active SignalSubscription listeners.


def self.has_untyped_signal_subscribers?(target_id : UInt64, signal_name : String) : Bool #

Returns true if (target_id, signal_name) has any active untyped BoundSignal / .await listeners.


def self.in_typed_emit=(in_typed_emit : Bool) #

Re-entrancy guard when TypedSignal#emit dispatches through C++ Bridge.emit_signal.


def self.in_typed_emit? : Bool #

Re-entrancy guard when TypedSignal#emit dispatches through C++ Bridge.emit_signal.


def self.input : Input #

def self.input_map : InputMap #

def self.instantiate_scene(path : String, type : T.class) : T forall T #

def self.instantiate_scene(path : String) : Godot::Node #

def self.instantiate_scene?(path : String, type : T.class) : T | Nil forall T #

def self.instantiate_scene?(path : String) : Godot::Node | Nil #

def self.ip : IP #

def self.java_class_wrapper : JavaClassWrapper #

def self.java_script_bridge : JavaScriptBridge #

def self.leak_tracker #

def self.load(path : String, type_hint : String = "", cache_mode : Int64 = 0_i64) : Resource #

=========================================================================== Resource & Scene Loading Helpers


def self.load(path : String, type : T.class, type_hint : String = "", cache_mode : Int64 = 0_i64) : T forall T #

def self.load(path : String, *, as type : T.class, type_hint : String = "", cache_mode : Int64 = 0_i64) : T forall T #

Named argument variant for as: (e.g. Godot.load("res://...", as: PackedScene))


def self.load?(path : String, type_hint : String = "", cache_mode : Int64 = 0_i64) : Resource | Nil #

Safe nil-returning resource loader (get_or_null parity)


def self.load?(path : String, type : T.class, type_hint : String = "", cache_mode : Int64 = 0_i64) : T | Nil forall T #

Safe nil-returning typed resource loader


def self.load_as(type : T.class, path : String, type_hint : String = "", cache_mode : Int64 = 0_i64) : T forall T #

def self.load_scene(path : String) : PackedScene #

def self.load_scene?(path : String) : PackedScene | Nil #

def self.log_history : Array(LogRecord) #

Returns the circular in-memory buffer of recent log records


def self.logger : DiagnosticLogger #

Master diagnostic logger instance accessor


def self.marshalls : Marshalls #

def self.native_menu : NativeMenu #

def self.navigation_mesh_generator : NavigationMeshGenerator #

def self.navigation_server2_d : NavigationServer2D #

def self.navigation_server2_d_manager : NavigationServer2DManager #

def self.navigation_server3_d : NavigationServer3D #

def self.navigation_server3_d_manager : NavigationServer3DManager #

def self.next_frame(timeout_sec : Float64 | Nil = 5.0) : Void #

Cooperatively yields until the next process (render/idle) frame has completed. Includes an optional timeout (default: 5.0s) to prevent deadlock if frames are not advancing.


def self.notify_object_queued_for_deletion(target_id : UInt64, yield_awaiters : Bool = true) : Void #

Marks all non-teardown signal subscriptions on a node queued for deletion (queue_free) as freed and yields on the Main Thread so awaiting fibers unblock immediately without waiting for frame end.


def self.notify_signal(target_id : UInt64, signal_name : String, args : Array(Variant) = EMPTY_VARIANT_ARGS) : Void #

Notifies active subscribers that a signal has fired on an object. Uses a stack-allocated StaticArray snapshot for <= 64 listeners (zero heap allocations).


def self.notify_untyped_signal(target_id : UInt64, signal_name : String, args : Array(Variant) = EMPTY_VARIANT_ARGS) : Void #

Notifies only untyped BoundSignal / .await subscribers when a TypedSignal has already dispatched all typed fast-path subscribers through StaticSignalTable.


def self.on_main_thread(&block : -> Void) : Void #

Global convenience delegator


def self.os : OS #

def self.performance : Performance #

def self.physics_frame(timeout_sec : Float64 | Nil = 5.0) : Void #

Cooperatively yields until the next physics process frame has completed. Includes an optional timeout (default: 5.0s) to prevent deadlock if physics frames are not advancing.


def self.physics_frame_count : Int64 #

Returns the total number of physics process steps executed since the engine started.


def self.physics_server2_d : PhysicsServer2D #

def self.physics_server2_d_manager : PhysicsServer2DManager #

def self.physics_server3_d : PhysicsServer3D #

def self.physics_server3_d_manager : PhysicsServer3DManager #

def self.preload(path : String, as type : T.class) : T forall T #

def self.preload(path : String) : Resource #

Preloads/loads a resource from the given path (convenience alias to Godot.load)


def self.preload?(path : String, as type : T.class) : T | Nil forall T #

def self.preload?(path : String) : Resource | Nil #

def self.print(*args) #

Prints a formatted message to the Godot console and records it to log sinks


def self.print_error(msg : String, func : String = "", file : String = __FILE__, line : Int32 = __LINE__) #

Prints a rich structured error with function, file, and line details


def self.print_verbose(*args) #

Prints a verbose diagnostic message that only appears when verbose logging is active


def self.print_warning(msg : String, func : String = "", file : String = __FILE__, line : Int32 = __LINE__) #

Prints a rich structured warning with function, file, and line details


def self.printerr(*args) #

Prints an error message to the Godot console and standard error


def self.process_frame_count : Int64 #

Returns the total number of frames rendered since the engine started.


def self.project_settings : ProjectSettings #

def self.pump_awaiting_fibers_once_per_frame! : Void #

Cooperatively yields once per engine process frame if any fibers are actively awaiting signals.


def self.register_filter(name : String, &block : LogFilterProc) : Void #

Register a globally named log filter


def self.rendering_server : RenderingServer #

def self.resource_loader : ResourceLoader #

def self.resource_saver : ResourceSaver #

def self.resource_uid : ResourceUID #

def self.signal_spy #

Master diagnostics helpers for Lapis


def self.signal_subs #

def self.signal_subs_mutex #

def self.spawn(&block : -> Void) : Fiber #

Spawns a cooperative gameplay fiber with managed exception logging.


def self.subscribe_signal(target_id : UInt64, signal_name : String, flags : ConnectFlags = ConnectFlags::None, callback : Proc(Array(Variant), Void) | Nil = nil, receiver : Godot::Object | Nil = nil) : SignalSubscription #

Subscribes an awaiter or callback to a signal on a target object instance ID


def self.text_server_manager : TextServerManager #

def self.texture_streaming : TextureStreaming #

def self.theme_db : ThemeDB #

def self.time : Time #

def self.to_godot_res_path(path : String) : String #

Converts any filesystem or relative path to a normalized Godot res:// path


def self.tombstone_tracker #

def self.translation_server : TranslationServer #

def self.unsubscribe_signal(sub : SignalSubscription) : Void #

Unsubscribes a signal subscription


def self.verbose? : Bool #

Returns true if the engine was launched with verbose logging enabled (--verbose)


def self.wake_freed_awaiters! : Void #

Yields on the Main Thread until all recently freed awaiting subscriptions have woken up and exited .await.


def self.worker_thread_pool : WorkerThreadPool #

def self.xr_server : XRServer #

Macro Detail

macro define_log_filter(name, tag = nil, name_tag = nil, color = nil, ansi = nil, level = nil) #

Alias for log_filter


macro define_log_filters(&block) #

Block DSL for registering multiple log filters cleanly


macro define_packed_array(name, elem_type, default_val) #

macro log(level, *args, **kwargs) #

Master log macro with AST elision, automatic tagging, and status handling


macro log_channel(channel, level, message, **kwargs) #

Logs directly to a specific channel


macro log_debug(category, message = nil, **kwargs) #

macro log_error(category, message = nil, **kwargs) #

macro log_filter(name, tag = nil, name_tag = nil, color = nil, ansi = nil, level = nil) #

Registers a single log filter definition anywhere in the codebase


macro log_info(category, message = nil, **kwargs) #

macro log_internal(category, message = nil, **kwargs) #

macro log_public(level, category, message, **kwargs) #

Logs a message explicitly tagged as "public" with "public" status for public channels and distribution


macro log_public_error(category, message = nil, **kwargs) #

macro log_public_info(category, message = nil, **kwargs) #

macro log_public_warn(category, message = nil, **kwargs) #

macro log_secret(level, category, message, **kwargs) #

Logs an internal/confidential message tagged as secret. Public log channels & sinks reject these!


macro log_trace(category, message = nil, **kwargs) #

macro log_warn(category, message = nil, **kwargs) #