module
Lapis::Docs::C_GAMEPLAY_AND_DECLARATIVE_DSL::J_GDSCRIPT_TO_CRYSTAL_CHEATSHEET
Overview
GDScript to Crystal Migration Cheatsheet
Comprehensive conversion guide and syntax cheatsheet for migrating GDScript game logic into idiomatic, high-performance Crystal for Lapis.
Executive Summary & Key Topics
| Topic | Method / Anchor | Description |
|---|---|---|
| Cheatsheet Core Topics | .topic_00_cheatsheet_features |
Class declarations, node resolution via ~, exports, signals, and lifecycle. |
| Complete Syntax Comparison Matrix | .topic_01_syntax_matrix |
Side-by-side mapping of GDScript patterns to Lapis Crystal. |
| Class Declaration DSL Variants | .topic_02_class_declarations |
node, node2d, node3d, gdclass, and gmodule. |
| Node Resolution with Unary ~ | .topic_03_unary_tilde_node_resolution |
Matching GDScript's $ and % shorthands using Crystal's unary ~ operator. |
| Memory Safety & Dead-Pointer Defense | .topic_04_memory_safety |
queue_free vs .destroy and #alive? defensive checks. |
Related Guides & Source References
- Gameplay DSL:
src/libgodot/macros.cr - Node Context:
src/libgodot/node_context.cr - Live Specs:
spec/suites/test_node_dsl.cr
Defined in:
libgodot/docs/c_gameplay_and_declarative_dsl/j_gdscript_to_crystal_cheatsheet.crClass Method Summary
-
.topic_00_cheatsheet_features : Nil
Cheatsheet Core Topics: Class declarations, node resolution via ~, exports, signals, and lifecycle.
-
.topic_01_syntax_matrix : Nil
Complete Syntax Comparison Matrix: Side-by-side mapping of GDScript patterns to Lapis Crystal.
-
.topic_02_class_declarations : Nil
Class Declaration DSL Variants: node, node2d, node3d, gdclass, and gmodule.
-
.topic_03_unary_tilde_node_resolution : Nil
Node Resolution with Unary ~: Matching GDScript's $ and % shorthands using Crystal's unary ~ operator.
-
.topic_04_memory_safety : Nil
Memory Safety & Dead-Pointer Defense: queue_free vs .destroy and #alive? defensive checks.
Class Method Detail
Cheatsheet Core Topics: Class declarations, node resolution via ~, exports, signals, and lifecycle.
Key Topics & Information
- Class declarations: node, node2d, node3d, gdclass, gmodule
- Node path & unique lookup: ~Sprite2D, ~"path", ~"%unique", ~.as(T)
- Exported properties: @[Export], ranges, enums, file pickers
- Signals: typed declarations and synthesized emit_
methods - Non-blocking awaiting: await(signal), await(duration), await(timer)
Complete Syntax Comparison Matrix: Side-by-side mapping of GDScript patterns to Lapis Crystal.
| GDScript Pattern | Crystal Equivalent | Architectural Notes |
|---|---|---|
class_name Player extends CharacterBody2D |
node Player < CharacterBody2D do |
Explicit parent class. Registers in Godot ClassDB. |
class_name GameManager extends Node |
node GameManager do |
Defaults automatically to Godot::Node. |
class_name Player2D extends Node2D |
node2d Player2D do |
Convenience DSL macro defaulting to Godot::Node2D. |
class_name Player3D extends Node3D |
node3d Player3D do |
Convenience DSL macro defaulting to Godot::Node3D. |
class_name ItemData extends Resource |
gdclass ItemData < Godot::Resource do |
Registers custom Resource/RefCounted in ClassDB. |
$Sprite2D |
~Sprite2D |
Typed lookup of first child matching class in NodeContext. |
$"Sprite2D" |
~"Sprite2D" |
String relative path child resolution. |
$"../Player" |
~"../Player" |
Parent or sibling tree navigation. |
%"HealthBar" |
~"%HealthBar" |
Scene unique node resolution (% prefix). |
$SomeName as Player |
~"SomeName".as(Player) |
Explicit casting to custom Crystal node class. |
get_node_or_null("Sprite2D") |
~Sprite2D? |
Nilable lookup returning typed Sprite2D? or nil. |
@export var speed: float = 300.0 |
@[Export] |
Exposes typed property to editor Inspector. |
@onready var sprite = $Sprite2D |
getter(sprite) { ~Sprite2D } |
Lazy memoized getter resolving child on first access. |
signal health_changed(curr, max) |
signal health_changed(current : Int32, max : Int32) |
Synthesizes type-safe emit_health_changed method. |
await get_tree().create_timer(1.0).timeout |
await(1.0) |
Non-blocking signal awaiting without stopping engine loop. |
await enemy.died |
await(enemy.died) |
First-class bound signal awaiting with optional timeout. |
queue_free() |
queue_free |
Deferred deletion by engine at frame end. |
Class Declaration DSL Variants: node, node2d, node3d, gdclass, and gmodule.
Lapis supports 5 declarative class and module macros:
# 1. Explicit inheritance from any Godot engine node
node Player < CharacterBody2D do
@[Export]
property speed : Float32 = 250.0_f32
end
# 2. Shorthand defaulting to Godot::Node
node GameManager do
property score : Int32 = 0
end
# 3. Shorthand for 2D nodes (defaults to Godot::Node2D)
node2d Bullet do
property velocity : Vector2 = Vector2.new(10.0_f32, 0.0_f32)
end
# 4. Shorthand for 3D spatial nodes (defaults to Godot::Node3D)
node3d Turret do
property range : Float32 = 15.0_f32
end
# 5. Non-Node ClassDB classes (Resources, RefCounted)
gdclass SkillData < Godot::Resource do
@[Export]
property cooldown : Float32 = 2.5_f32
end
# 6. Reusable mixin modules with exported properties and signals
gmodule DamageableMixin do
@[Export]
property armor : Int32 = 5
signal damaged(amount : Int32)
end
Node Resolution with Unary ~: Matching GDScript's $ and % shorthands using Crystal's unary ~ operator.
In GDScript, nodes are retrieved using $ and %. In Lapis, use the unary ~ operator:
def _ready : Void
# Typed lookup of child node
sprite = ~AnimatedSprite2D
# Path lookup
hitbox = ~"HitboxArea/CollisionShape2D"
# Tree navigation
parent_mgr = ~"../GameManager"
# Scene unique node
health = ~"%HealthBar"
# Explicit downcasting to custom user class
player = ~"../Player".as(Player)
bar = ~"%HealthBar".as(ProgressBar)
# Nilable lookup (returns nil if child absent)
optional_light = ~PointLight2D?
end
Memory Safety & Dead-Pointer Defense: queue_free vs .destroy and #alive? defensive checks.
GDScript uses reference counting and garbage-collected variants, while Godot nodes are C++ instances.
- Nodes in SceneTree: Call
node.queue_freeto let Godot deallocate them cleanly at frame end. - Unparented Nodes: Standalone nodes created via
Godot.create(Node2D)MUST be cleaned up vianode.destroyif they are never added to the SceneTree. - Defensive Checks: When retaining references to enemies or bullets, check
#alive?before accessing them to avoid segmentation faults.