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.

På den här sidan

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.