Skip to main content

The editor

A Balaur project. Its scripts draw the interface through the ui module, and editing one hot reloads the editor. Every binding it uses (filesystem access, TOML parsing, scene instantiation, script reload, camera control, debug drawing) is available to any project.

balaur edit my-game

The editor on examples/angrynerds: the node tree, the viewport with its 2D grid and gizmo, the inspector, and the dock tabs

The project manager​

Started with no project, the editor opens on the manager. It has three tabs: the projects this machine has opened, the examples that ship with the editor, and the engine build itself. It is a screen of the editor rather than a window of its own, so it hot reloads like the rest of the shell and works on a phone.

The Projects tab: a filter, the three ways to start, and the projects this machine has opened with their paths, times and engine versions

A row says when it was last opened and which engine opened it. A project whose folder has gone stays in the list and says so, because a missing one is usually an unplugged disk. New project opens a dialog; Open folder and Import Godot use the platform's own folder picker.

Opening a project starts the editor on it and closes this one. One process is one project, because the export and import modules and the file root are built for the project the editor booted with.

Open project…, New project… and Recent projects… are in the command palette, so the manager is reachable without closing what is open. Opened that way, it has a Back button, and Escape returns to the project.

Examples​

The Examples tab: a card per example project, each with its name and a line saying what it is

The example projects ship beside the editor. Opening one copies it into your projects folder and opens the copy, so the shipped example stays as it was and a second copy is a second project.

Engine versions​

The manager's third tab is the build itself: which version this is, which channel it follows, and every release that channel has published. Opening the tab reads the release feed once; installing replaces the whole install, which is what balaur update does from a terminal.

The Engine tab: this build and whether it is current, the channel to follow, and the releases on it

Follow picks the line: alpha, beta, rc, stable or nightly. The releases under it are that line's, newest first, with the installed one marked. Each button says which way it goes, Install or Downgrade, and a release can also be named exactly. An install downloads in the background with a progress bar; restart to use it.

A build from a checkout belongs to no channel and says so. An install that cannot replace itself says why before offering anything: a macOS bundle offers the release's disk image to download, and a cargo target directory offers nothing.

About Balaur​

The About dialog: the version, build, channel, platform and license, and where this build's channel stands

About Balaur, in the menu under the logo, shows the version, build, channel, platform and license. It checks the build's own channel once and offers the newest release on it, through the same code as the Engine tab.

The document and the mirror​

  • The scene TOML is parsed into a document model and mirrored as a node subtree. The viewport shows the scene and physics builds the bodies, paused until play.
  • Edits are written back as TOML. Every one is undoable (⌘Z, ⇧⌘Z).
  • Play-in-editor attaches the game's scripts to the mirrored nodes and unpauses.
  • Every run is recorded. See Determinism.
  • Stop rebuilds the mirror from the document.

Workspaces​

The top bar switches between five workspaces. Each arranges the same panels (node tree, viewport or code, docks, inspector) around one job.

  • Scene: the tree, the viewport with its gizmos, and the inspector generated from the component registry, third-party components included. A 2D scene gets a 2D grid, a pan-and-zoom camera, a 2D gizmo, click-picking and collider overlays. See Scenes and nodes.
  • Script: one buffer per file with Rune highlighting, the hooks the file declares, and the events the node receives. Save writes to the project and hot reloads the code, while playing too. A click on a line number sets a breakpoint. The Debugger dock shows frames and locals; continue and step over / into / out are F5, F10, F11 and Shift+F11. See Scripting.
  • Animate: the timeline over the selected clip's tracks. See Animation.
  • Physics: bodies and colliders with their overlays. See Physics.
  • Interface: widgets laid out over the safe area. See UI.

Selecting​

The selection is a set. Its first node is the active node.

  • Shift or ⌘ click a row or a node; drag on empty viewport to box-select. Every node in the set is lit in the tree, not only the active one.
  • A right click on a row picks it and opens the node's own menu: add a child, move it among its siblings, attach a script, duplicate, hide, lock, delete.
  • A gizmo drag moves, turns or scales the set about its centre.
  • ⌘G groups at the centroid keeping every world pose, ⇧⌘G ungroups.
  • Six aligns and three distributes, in the palette.
  • H hides, ⌘L locks, / isolates. Hidden writes the node's visible; locked is editor state the gizmo and box select skip.
  • The inspector shows the active node's rows and writes to every selected node that carries the same component.

The Library dock open under the viewport, with a torus selected and pointed at the library's metal material

Tools​

The strip beside the viewport: Select, Move, Rotate, Scale, Pen, and two rig tools. Rig bone grows and poses a skeleton. Polygon edits a skinned polygon's points, triangulation, UVs and weights. Both are under Skeletons and skins.

Pen edits a path2d asset in place: click adds an anchor, drag pulls its handles, alt breaks the pair, right-click removes one, and clicking the first anchor closes the path.

Asset imports​

A file dropped on the window becomes a node.

  • An image lands under art/, as a sprite in 2D and a textured plane in 3D.
  • A .glb, .aseprite, .tmx or .ldtk goes through balaur import, and the scene it wrote is instanced.
  • A .wesl becomes a shader and the material that names it.
  • An .hdr becomes the environment's sky.
  • A scene file is instanced as a prefab; a font lands under fonts/.

Docks, palette, themes​

Eleven docks sit under the viewport.

  • Output: the engine's log ring buffer, with level filters.
  • Problems: what the linter found in every script, and a shader that would not link.
  • Assets: the project's files.
  • Docs: the script reference, module by module, from the running engine. ⌘B on an engine call opens it. See Tooling.
  • Timeline: the animation editor's.
  • Debugger: a paused script's frames and locals.
  • Recordings: play-in-editor's recordings. It replays one, with lanes for its input, network and log events.
  • Profiler: a row per stage of the last frame, with its cost and a bar against the 16.7 ms of a 60 Hz frame, from engine::timings().
  • Cost: what the frame draws, from render::stats(), in named columns: triangles, texture bytes and a cloner's copies, per node.
  • Library: stock materials, skies, models, lighting setups, camera and control rigs, and project templates. Clicking a material copies it in and points the selected node at it. A sky is copied under skies/ and named on the scene's environment. A model, a lighting setup or a rig is instanced as a prefab; a rig brings its script. The skies are gradients and the models are primitives. The library is kilobytes and carries no third-party licence.

The Cost dock: a row per node with its triangles, texture bytes and a bar, and a cloner drawing sixteen copies

  • Every ⌘ chord in this manual answers to Ctrl as well, on every platform and in the browser.
  • The command palette (⌘K) lists nodes, commands and workspaces.
  • Focus (⇧⌘\) gives the code the whole window: the three docks fold, the split goes off, and the file's hooks stay beside the pane. A second press restores the layout. Opening a panel keeps the panel and ends focus; so does leaving the code tab.

The code pane with the window to itself, the hooks list beside it, and the Focus chip lit in the top bar

  • Dark and light themes.
  • Show input overlay draws the keys, buttons and pointer the game is being fed. See Input.

Start-up states​

The editor starts in any state named on the command line.

balaur edit examples/rig --offscreen --frames 100 \
--state "anim,select:Thigh,tool:bone,shot=thigh.png"
  • --state is a comma list: a workspace (scene, script, anim, phys, ui), select:<node>, tool:<id>, mode:<id>, dock:<name>, zoom:<percent>, palette, light, play, input, focus, manager, examples, versions, about.
  • shot=<file> captures one frame. frames=<dir> captures every other frame into a numbered directory.
  • show:<name> runs a scripted sequence (this manual's clips). It feeds input through input::feed_key, feed_mouse and feed_mouse_button.
  • Offscreen frames advance on the fixed 1/60 step. Frame 90 is the same picture every run.
  • The engine repository's scripts/showcase.sh takes every image and clip on this site this way.

Extending the editor​

A plugin is a Rune file adding docks, windows, palette commands, inspector sections, property editors, viewport overlays and start-up states.

  • editor/plugins/ ships with the editor. A project's editor/ directory is that game's own.
  • An addon brings its own: every addons/<name>/editor/ loads while the project carries that addon. The Gamend dock arrives this way.
  • All of them load at start-up, in name order.
  • A plugin declares register(), returning what it adds, and optionally state(), whose object lives at S.plugins.<name>.
// mygame/editor/counter.rn
pub fn state() {
#{ shown: -1 }
}

pub fn register() {
#{
docks: [#{ id: "counter", name: "Counter", draw: draw }],
commands: [#{ label: "Count scripted nodes", icon: "Σ", run: count }],
}
}

pub fn count(S) {
let n = 0;
for node in S.doc {
if node.contains_key("script") {
n += 1;
}
}
S.plugins.counter.shown = n;
}

pub fn draw(S, k, host) {
let empty = S.kit.empty_row;
let rows = S.kit.put_rows;
rows(S, host, "body", [empty(format!("{} scripted nodes", S.plugins.counter.shown))]);
}

S is the editor's whole state, the same object every built-in module gets: the document (S.doc), the live mirror, the selection (S.sel), history and the theme. Call the history recorder before mutating the document.

Each list is optional.

  • docks take a draw(S, k, host), windows a draw(S, k) and commands a run(S). A dock with tall: true makes the bottom dock grow while it is open.
  • sections are draw(S, k, node), drawn under the built-in inspector.
  • overlays are draw(S) and draw viewport lines.
  • editors map a schema datatype to draw(S, k, target, spec, value), replacing the built-in editor for that type.
  • states map a name to run(S): a plugin's own start-up state.

A dock is a node. The editor makes each registered dock a subtree and hands draw its path: a head for verbs, a side for pages, a body of rows and a foot. A plugin fills it with S.kit, the editor's own parts. A plugin cannot use the editor's modules, so each part is a stored function: bind it to a local, then call it.

PartWhat it does
put_bar(S, host, name, controls)Puts a row of controls on head, side or foot
put_rows(S, host, name, specs)Puts rows on the body; a spec is #{ label, controls }
area_node(host, name)The node itself, to add children to or read a rect from
tabs(S, host, id, names)A row of tabs across the head, and answers the open one
pages(S, host, id, groups)A sidebar of pages, and answers the open one
tree_rows(S, id, value, opts)A map or list as rows; edit makes leaves fields
field_row(S, label, value, opts)A labelled value; mask hides it, copy adds a copy button
heading_row(title), empty_row(text)A heading, and what an empty panel says
file_rows(S, id, opts)A folder's files; TOML and JSON open as editable trees
user_data_directory(S), save_editor_settings(S)The game's user data folder, and a write of the editor's settings
short_path(path)A path with the home folder written as ~
pub fn draw(S, k, host) {
let field = S.kit.field_row;
let rows = S.kit.put_rows;
rows(S, host, "body", [
field(S, "token", "abcd1234efgh5678", #{ mask: true, copy: true }),
]);
}

A control is the widget table the editor's own rows are built from, plus an on the editor calls with what the reader changed. The controls are nodes: they take theme roles, and the same layout places them.

A floating window still draws. windows takes a draw(S, k) and the ui verbs, because a window has no node of its own yet.

The editor's User data dock is files over the open game's user data folder. Saves and prefs open there as structures you can edit. A game played in the editor writes to that same folder.

A plugin runs unsandboxed in the editor's host, with the editor's capabilities.