Skip to content

Search

Documentation

Coming from Godot

A Godot to Balaur mapping, one line each: project.godot to project.toml, .tscn to TOML, node types to components, GDScript to Rune, export to one binary.

Di halaman ini

Godot to Balaur, one row per file, node and call; the manual and the reference have the rest.

Files

Godot Balaur
project.godot project.toml: name, main_scene, input actions
scene.tscn scenes/scene.toml: a flat [[nodes]] list, parent by id
script.gd scripts/script.rn: Rune
.tres resources [[assets]] in TOML: mesh, material, animation_library, tileset, … (assets)
.gdshader .wesl in a material asset (shaders)
Export presets, templates balaur export my-game --target linux-x64: one fused binary (shipping)

Nodes

No class hierarchy. A node is whatever its components make it. A collider sits on the same node as its body.

Godot Balaur
Node2D, Node3D A node with a transform: position, rotation, scale. A node without one sits at its parent
RigidBody2D, StaticBody2D, AnimatableBody2D body2d = { kind = "dynamic" } (or static, kinematic); body3d in 3D
CollisionShape2D collider2d = { kind = "rectangle", size = [1.0, 1.0] }
CharacterBody2D, CharacterBody3D character2d, character3d
PinJoint2D, HingeJoint3D, … joint2d, joint3d
VehicleBody3D, VehicleWheel3D vehicle3d, wheel3d
Sprite2D sprite
TileMapLayer tilemap over a tileset asset
GPUParticles2D, CPUParticles2D particles2d
GPUParticles3D, CPUParticles3D particles3d
MeshInstance3D, CSGBox3D, … mesh for a model; shape3d for a primitive; boolean3d for CSG
Polygon2D polygon
Camera2D camera2d
Camera3D camera3d
PointLight2D, LightOccluder2D light2d, occluder2d
AudioStreamPlayer, AudioListener3D sound, listener
Skeleton2D / Bone2D, Skeleton3D Nodes with bone2d / bone3d
SkeletonIK, LookAtModifier, SkeletonModification2DFABRIK / CCDIK / Jiggle modifier2d and modifier3d: look_at, two_bone_ik, fabrik, ccdik, jiggle
BoneMap, SkeletonProfile A bone_map asset and animation.play(node, clip, { retarget })
PhysicalBone2D, Create Physical Skeleton physics2d.ragdoll(node), and a ragdoll component whose influence mixes the clip with the simulation
AnimationPlayer animation: a clip library and an autoplay
Label, Button, Panel widget with kind = "label" / "button" / "panel"
Control layouts, editor plugins The ui module: immediate-mode egui in draw_ui (UI)
Instancing a PackedScene instance = "scenes/crate.toml" on a node, with overrides (prefabs)

Scripts

Godot Balaur
extends Node2D Nothing. A script is free functions taking this
_ready() pub fn init(this)
_process(delta) pub fn update(this, dt)
_physics_process(delta) pub fn fixed_update(this, dt): fixed 60 Hz, before physics
_exit_tree() pub fn on_free(this)
@export var speed = 2.0 pub fn exports() { #{ speed: 2.0 } }, overridden in [nodes.script.props]
self this; the node is this.node
position, rotation, scale this.node.transform.position, .rotation_euler (radians), .scale; transform.global_position() in world space
$Path/To/Node, get_node("..") this.node.get_node("Path/To/Node"), "../Sibling"
queue_free() node.queue_free(): deferred to end of frame
apply_impulse(v) this.node.body2d.apply_impulse(x, y)
signal hit on a node, enemy.hit.connect(_on_hit) events::listen(this.node, "hit", enemy), then pub fn on_hit(this, payload) (events)
hit.emit(payload) from that node this.node.emit("hit", payload): delivered at the top of the next frame
Connecting to every emitter of a signal events::listen(this.node, "hit"): no emitter argument
Engine signals: pressed, animation_finished, request_completed Methods by name: on_click, on_animation_finished(name), on_response(r)
Calling a method on another node node.call("method", args)
Input.is_action_just_pressed("jump") input::action_just_pressed("jump")
Input.get_axis("left", "right") input::action_value("move_x"): one action, -1 to 1
InputMap in project settings [input.actions] in project.toml (input)
$AnimationPlayer.play("run") this.node.animation.play("run")
$AnimationTree["parameters/playback"].travel("run") this.node.state_machine.travel("run")
create_tween().tween_property(...) animation::tween(this.node, #{ steps: [...] }): Godot's easing names
await get_tree().create_timer(1.0).timeout task::wait(...) in an async fn; results land at the start of a tick
HTTPRequest http::request(url) (networking)
FileAccess, ConfigFile fs, settings, save
randf(), randi() random::: seeded, engine-owned, replayable
print() log::info()
preload, load assets::load("path.toml")

The editor

Godot Balaur
2D / 3D / Script / AssetLib tabs Workspaces: Scene, Script, Animation, Physics, UI (editor)
Script reloads on save Hot reload in milliseconds
Debugger tab Breakpoints in the gutter, frames and locals in a dock
Editor plugins in GDScript The editor is a Balaur project; editing its scripts hot reloads the editor
Remote scene tree Play in editor on the same scene and scripts, every run recorded

Differences

  • In Godot only Godot Rapier Physics's physics server is deterministic. In Balaur the whole tick is: fixed step, a digest per tick, record, replay and rollback (determinism).
  • There is no connect. Events are named: events::emit("hit", p) reaches a subscribed node as on_hit(p) next frame. Engine events work the same way, most with a polling form (input::key_just_pressed, events::emitted("hit")).
  • There are no editor-only nodes. Everything the editor does is a script call any project can make.
  • Balaur is new. Compare and the roadmap list what is missing.

Start

balaur import project.godot --project my-game   # convert a Godot project
balaur edit my-game

The importer writes the settings, every scene, the scripts and the theme, and reports what did not carry. examples/angrynerds is a small 2D game: bodies, colliders, widgets, one script per node. Getting started has the rest.