Skip to content

Lua API: Modules

The global modules scripts call: Scene, Input, Save, Tween, Pool, Camera…

Animates node properties over time with easing.

Tween.to(node, property, value, duration, ease, options)

Section titled “Tween.to(node, property, value, duration, ease, options)”

Tweens a property to a value: “position”, “scale”, “rotation”, any numeric or colour property (“tint”, “emitter.amount”, …; colours as {r, g, b[, a]} 0..1), or a material parameter (“shader:amount”). Returns the tween’s id.

local id = Tween.to(self.node, "scale", {1.2, 1.2}, 0.15, "out_quad", {loops = -1, yoyo = true})
  • node Node
  • property string
  • value number|number[]
  • duration number Seconds
  • ease? EaseName Default “out_quad”
  • options? TweenOptions

Returns integer id

Stops a tween (it stays where it is).

if self.fade_id then Tween.cancel(self.fade_id) end
  • id integer

Returns boolean found

Stops a node’s tweens (all, or one property’s).

local stopped = Tween.cancel_all(self.node, "position")
  • node Node
  • property? string

Returns integer count

Whether the tween with this id is still playing.

if not Tween.is_running(self.bounce_id) then self.bounce_id = nil end
  • id integer

Returns boolean

Maps progress 0..1 through an easing curve (for your own animation).

local k = Tween.ease("in_out_sine", self.elapsed / self.duration)
  • ease EaseName
  • t number

Returns number

Reuses instances of a component instead of creating and freeing them (bullets, sparks, coins). A pooled node is parked disabled (no update, no drawing, bodies off) and comes back on take; node:destroy() / queue_free() on one returns it to its pool. Its root’s script gets on_spawn() before its first update after every take (after ready() the first time) and on_despawn() when returned. Pools last while the scene is loaded.

Takes an instance of the component: a parked one, else a new one (nil at the pool’s max). Added under parent (else the scene’s root) at x, y local to it.

local b = Pool.take("components/bullet.component.json", self.node:get_parent(), self.node.position)
  • component ComponentPath|string
  • parent? Node
  • x? number|Vec2
  • y? number

Returns Node?

Returns a taken instance to its pool (the same as node:destroy() on it).

Pool.release(self.node)
  • node Node

Returns boolean ok false when it isn’t a taken pooled node

Makes instances up front (parked) so the first takes don’t create them mid-game.

Pool.prewarm("components/bullet.component.json", 64)
  • component ComponentPath|string
  • count integer Instances in total

Caps how many instances the pool makes (0 = no cap); take returns nil at the cap.

Pool.set_max("components/spark.component.json", 200)
  • component ComponentPath|string
  • max integer

How many instances are taken, parked and made in total.

local active, parked, made = Pool.stats("components/bullet.component.json")
  • component ComponentPath|string

Returns integer active, integer parked, integer created

The player’s accessibility settings: applied at once, saved with Accessibility.save() (accessibility.json next to the save files) and loaded when the game starts over the project’s defaults (Project Settings > Accessibility). See the Accessibility Menu script template.

A setting’s value.

local s = Accessibility.get("ui_scale")
  • key AccessibilityKey

Returns any

Changes a setting (false for an unknown key or value).

Accessibility.set("colorblind", "deuteranopia")
  • key AccessibilityKey
  • value number|boolean|string

Returns boolean ok

Every setting.

for k, v in pairs(Accessibility.get_all()) do print(k, v) end

Returns AccessibilitySettings

Saves the player’s settings.

Accessibility.save()

Returns boolean ok

Back to the project’s defaults (save to keep).

Accessibility.reset()

The subtitle track: lines at the bottom centre over everything, each for a time (up to 3 at once). Spoken lines show when the player has subtitles on, captions when captions are on.

Shows a line; returns its id (0 when the player turned that kind off).

Subtitles.show("Over here!", {speaker = "Ada"})
  • text string Rich text
  • options? SubtitleOptions

Returns integer id

Takes a line down early.

Subtitles.hide(id)
  • id integer

Takes every line down.

Subtitles.clear()

Lines on screen now.

if Subtitles.count() == 0 then self:next_bark() end

Returns integer

Persistent save data: plain values (booleans, numbers, strings, tables of those) stored as JSON in the player’s user-data folder (macOS ~/Library/Application Support/, Windows %APPDATA%<game>, Linux ~/.local/share/). Read on first use; written by Save.flush() and when the game quits.

Stores a value (nil removes the key). Tables are stored by reference: changes to a table you got from Save.get are saved too.

if score > Save.get("best_score", 0) then Save.set("best_score", score) end
  • key SaveKey|string
  • value SaveValue

Returns the stored value for a key, or the default when the key is not set.

local settings = Save.get("settings", {music = 0.8, fullscreen = false})
  • key SaveKey|string
  • default? SaveValue Returned when the key isn’t set

Returns SaveValue

Whether a value is stored under the key.

if not Save.has("tutorial_done") then Scene.change_scene("tutorial") end
  • key SaveKey|string

Returns boolean

Removes a key from the save data.

Save.delete("checkpoint")
  • key SaveKey|string

Returns every stored key, sorted.

for _, key in ipairs(Save.keys()) do Log.info(key, Save.get(key)) end

Returns string[] keys Sorted

Removes every key (the file is rewritten on the next flush).

Save.clear(); Save.flush()

Writes the save file now.

if not Save.flush() then Log.warn("could not write save file") end

Returns boolean ok

Switches to another save file (.json; default “save”), writing the current one first. Returns the current slot.

local previous = Save.slot("slot_2")
  • name? string

Returns string slot

Returns the file path of the current save slot.

Log.info("saving to", Save.path())

Returns string path The current save file

Crash reports from earlier runs of this game. A crash (an engine error, a failed check, invalid memory access) writes a report to the player’s user-data folder (<user data>/crashes). Read them at startup and send them where you like (Editor-free: your own server, a “send report?” menu).

The reports from earlier runs not yet dismissed (oldest first).

for _, r in ipairs(Crash.pending()) do show_report_dialog(r.text); Crash.dismiss(r.path) end

Returns CrashReport[]

Stops offering a report (it stays in the folder as .seen).

Crash.dismiss(report.path)
  • path string A path from Crash.pending()

Returns boolean ok

Adds a line to the next crash report’s “recent” list (what the game was doing: “entered level 3”, “boss fight”). The last 48 lines are kept.

Crash.note("loaded " .. level_name)
  • text string

The crash folder.

print("crash reports in " .. Crash.dir())

Returns string

Core engine statistics, frame timing, and display information.

Gets current display refresh frames per second.

Log.info("FPS: " .. Engine.get_fps())

Returns integer

Gets the last frame duration in seconds.

local dt = Engine.get_delta_time(); self.elapsed = self.elapsed + dt

Returns number

Gets running time in seconds since the engine launched.

local t = Engine.get_time(); self.node:set_position(self.base_x, self.base_y + math.sin(t * 2) * 8)

Returns number

Gets current physical window width and height in screen pixels.

local w, h = Engine.get_window_size()

Returns integer width, integer height

Gets virtual canvas reference resolution (e.g. 1280x720).

local vw, vh = Engine.get_virtual_size(); self.node:set_position(vw / 2, vh / 2)

Returns number width, number height

Pauses the game: nodes with pause mode “pausable” (the default) stop updating and physics stops; “always” nodes keep running (pause menus).

if Input.is_action_pressed("pause") then Engine.set_paused(not Engine.is_paused()) end
  • paused boolean

Returns whether the game is currently paused.

if Engine.is_paused() then return end

Returns boolean

Fullscreen on or off: the window covers the screen at its resolution (the game is scaled to it) or goes back to the project’s window size.

if Input.is_key_pressed("F11") then Engine.set_fullscreen(not Engine.is_fullscreen()) end
  • on boolean

Whether the game is fullscreen.

local full = Engine.is_fullscreen(); Log.info("Fullscreen: " .. tostring(full))

Returns boolean

Engine input subsystem for keyboard, mouse, and mapped action queries. Actions are named in Settings > Project > Input (project.json “input”): built-in names (move_up, move_down, move_left, move_right, jump, fire, secondary, boost, interact, pause, confirm, cancel, custom1..4) plus any the project adds, each bound to keys, mouse buttons or gamepad input.

Players: up to 4. Player 1 is the keyboard + mouse and the first gamepad; players 2-4 each have a gamepad (the 2nd, 3rd, 4th plugged in). Action queries take an optional player (default 1). Scripts get on_gamepad_connected(player, name) / on_gamepad_disconnected(player).

Checks if an action was pressed this frame (just triggered).

if Input.is_action_pressed("jump") then self.vy = -self.jump_speed end
  • action InputAction Action name from the project’s Input settings (e.g. “move_up”, “fire”, “jump”, “dash”)
  • player? integer 1-4 (default 1)

Returns boolean

Checks if an action is currently held down.

if Input.is_action_down("fire", self.player) then self:shoot() end
  • action InputAction
  • player? integer 1-4 (default 1)

Returns boolean

Checks if an action was released this frame.

if Input.is_action_released("jump") and self.vy < 0 then self.vy = self.vy * 0.5 end
  • action InputAction
  • player? integer 1-4 (default 1)

Returns boolean

Input.get_axis(neg_action, pos_action, player)

Section titled “Input.get_axis(neg_action, pos_action, player)”

Calculates a 1D axis value between -1.0 and 1.0 from negative and positive action names.

local dir = Input.get_axis("move_left", "move_right"); self.vx = dir * self.speed
  • neg_action InputAction Action name representing negative direction (e.g. “move_left”)
  • pos_action InputAction Action name representing positive direction (e.g. “move_right”)
  • player? integer 1-4 (default 1)

Returns number axis Value from -1.0 to 1.0

Input.get_vector(left, right, up, down, player)

Section titled “Input.get_vector(left, right, up, down, player)”

Calculates a normalized 2D direction vector from 4 directional action inputs.

Example: local dx, dy = Input.get_vector("move_left", "move_right", "move_up", "move_down") - or Input.get_vector(2): player 2’s move_* actions.

  • left? InputAction|integer The action, or the player (the move_* actions)
  • right? InputAction
  • up? InputAction
  • down? InputAction
  • player? integer 1-4 (default 1)

Returns number x Normalized X axis, number y Normalized Y axis

Queries raw keyboard key press state for a single frame.

if Input.is_key_pressed("F5") then Scene.reload() end
  • key KeyName|string Key name like “SPACE”, “W”, “Z”, “F5”, “LEFT_ALT”

Returns boolean

Queries raw keyboard key held state.

if Input.is_key_down("LEFT_SHIFT") then speed = speed * 2 end
  • key KeyName|string Key name

Returns boolean

Queries raw keyboard key release state for a single frame.

if Input.is_key_released("SPACE") then self.charging = false end
  • key KeyName|string Key name

Returns boolean

Queries mouse button pressed state for a single frame.

if Input.is_mouse_button_pressed("LEFT") then self:shoot() end
  • button MouseButton|string

Returns boolean

Queries mouse button held state.

if Input.is_mouse_button_down("RIGHT") then self.aiming = true end
  • button MouseButton|string

Returns boolean

Queries mouse button release state for a single frame.

if Input.is_mouse_button_released("LEFT") then self.dragging = false end
  • button MouseButton|string

Returns boolean

Gets the current mouse cursor position in screen pixels.

local mx, my = Input.get_mouse_position(); local wx, wy = Camera.screen_to_world(mx, my)

Returns number x Mouse X, number y Mouse Y

Gets mouse wheel scroll delta for this frame.

local wheel = Input.get_mouse_wheel_move(); Camera.set_zoom(Camera.get_zoom() + wheel * 0.1)

Returns number

An action’s bindings, each with a player-facing text.

for i, b in ipairs(Input.get_bindings("jump")) do print(i, b.text) end
  • action InputAction

Returns InputBindingInfo[]

What to show for an action’s bindings: “Space / A” (or one slot’s).

label.text = "Jump: " .. Input.binding_text("jump")
  • action InputAction
  • slot? integer

Returns string

Input.set_binding(action, slot, binding, conflicts)

Section titled “Input.set_binding(action, slot, binding, conflicts)”

Sets one of an action’s bindings (nil removes that slot); slot 0 replaces the first binding of the same device kind. Saved for the player.

Input.set_binding("jump", 1, {key = "W"})
  • action InputAction
  • slot integer
  • binding InputBindingSpec|nil
  • conflicts? "clear"|"swap"|"allow"

Returns boolean ok false for an unknown action

Waits for the player to press the key / button / stick to bind to an action (a “press a key…” prompt). Everything must be let go first, so the button that opened the menu isn’t taken. Escape cancels. The result is saved for the player.

Input.listen("jump", {on_done = function(result, text) self.label.text = text end})
  • action InputAction
  • options? InputListenOptions

Returns boolean ok false for an unknown action

Whether Input.listen is waiting for a press.

if Input.is_listening() then return end -- the menu ignores input meanwhile

Returns boolean

Stops waiting (on_done gets “cancelled”).

Input.cancel_listen()

Back to the project’s bindings: one action, or all of them. Saved for the player.

Input.reset_bindings()
  • action? InputAction

Saves the player’s bindings now (set_binding, listen and reset_bindings already do).

Input.save_bindings()

Returns boolean ok

The actions that already use a binding.

local used = Input.find_conflicts({key = "SPACE"})
  • binding InputBindingSpec

Returns string[]

The kind of device the player used last - show keyboard or gamepad prompts.

hint.text = Input.last_device() == "gamepad" and "Press A" or "Press Space"

Returns "keyboard"|"gamepad"

The project’s actions, in the order of Project Settings > Input (for building a controls menu).

for _, name in ipairs(Input.get_actions()) do print(name, Input.binding_text(name)) end

Returns string[]

Rumbles a player’s gamepad. low: the heavy (left) motor, high: the light (right) one, 0-1 each. False when the player has no gamepad. (Windows: XInput pads; Linux: pads with force feedback; macOS: controllers with haptics; the web: where the browser has it.)

Input.vibrate(0.4, 0.8, 0.25, self.player)
  • low number 0-1
  • high? number 0-1 (default: low)
  • seconds? number Default 0.2
  • player? integer 1-4 (default 1)

Returns boolean

Stops a player’s rumble.

Input.stop_vibration(2)
  • player? integer 1-4 (default 1)

The rumble a player’s gamepad has now (0, 0: none).

local low, high = Input.get_vibration()
  • player? integer 1-4 (default 1)

Returns number low, number high

The button prompt image for an action: its first binding on the device the player uses now (a key on the keyboard; A / Cross / B… on a pad). Use it as any image path (set_texture, UI images). nil when the action has nothing on that device.

Example: self.node:get_node("Prompt"):set_texture(Input.glyph("jump")) - “prompts:xbox/xbox_button_a”

  • action InputAction
  • player? integer 1-4 (default 1)
  • device? "keyboard"|"xbox"|"playstation"|"switch"|"steamdeck" Instead of the player’s device

Returns string?

The device a player uses now: “keyboard” (player 1, last used the keyboard / mouse or has no pad), the gamepad’s kind (from its name), or “none”.

if Input.device() == "playstation" then hint.text = "Press Cross" end
  • player? integer 1-4 (default 1)

Returns "keyboard"|"xbox"|"playstation"|"switch"|"steamdeck"|"generic"|"none"

Does the player have a gamepad connected?

if not Input.is_gamepad_connected(2) then label.text = "P2: connect a controller" end
  • player? integer 1-4 (default 1)

Returns boolean

The name of a player’s gamepad (nil: none).

Example: print(Input.gamepad_name(1)) - “Xbox Wireless Controller”

  • player? integer 1-4 (default 1)

Returns string?

The gamepad (1-4) a player has (nil: none).

local pad = Input.get_player_gamepad(2)
  • player integer 1-4

Returns integer?

Gives a player a gamepad (nil: none). A player that had it gets this player’s old one, so two players never share a pad - a “press A to join” screen.

for _, p in ipairs(Input.gamepads()) do if not p.player then Input.set_player_gamepad(next_free, p.pad) end end
  • player integer 1-4
  • pad? integer 1-4

The connected gamepads.

for _, p in ipairs(Input.gamepads()) do print(p.pad, p.name, p.device, p.player) end

Returns {pad: integer, name: string, device: string, player: integer?}[]

How many players: player 1 plus every other player with a gamepad.

for p = 1, Input.player_count() do self:spawn_player(p) end

Returns integer

Engine audio playback and the mixer: every sound and music plays into a bus (Master, Music, SFX and the project’s own - the Mixer tab), each with a fader, pan, mute / solo and effects.

Audio.play_sound(path, volume, pitch, bus)

Section titled “Audio.play_sound(path, volume, pitch, bus)”

Plays a sound effect from the given file path. Calls in quick succession overlap instead of restarting it.

Audio.play_sound("sounds/jump.wav", 0.8, 1.1)
  • path AudioPath|string File path, e.g. “assets/audio/laser.wav”
  • volume? number 0..1 (default 1), before the bus fader
  • pitch? number Default 1
  • bus? string The mixer bus (default “SFX”)

Plays a one-shot heard from a world position: quieter with distance from the listener (the camera, or Audio.set_listener) and panned to its side.

local x, y = self.node:get_position(); Audio.play_sound_at("sounds/explosion.wav", x, y)
  • path AudioPath|string
  • x number
  • y number
  • volume? number

Sets a mixer bus’s fader (linear: 1 = 0 dB, 0.5 = -6 dB). “master”, “music” and “sfx” also update the project’s volumes.

Audio.set_bus_volume("music", 0.5)
  • bus AudioBus A bus name (case doesn’t matter)
  • volume number

Returns a bus’s fader (linear).

local vol = Audio.get_bus_volume("sfx"); Audio.set_bus_volume("sfx", vol * 0.5)
  • bus AudioBus

Returns number volume

Mutes / unmutes a bus.

Audio.set_bus_mute("Voice", true)
  • bus AudioBus
  • mute boolean

Solos a bus (only soloed buses - and what they feed - are heard).

Audio.set_bus_solo("Music", true)
  • bus AudioBus
  • solo boolean

Pans a bus: -1 left, 0 centre, 1 right.

Audio.set_bus_pan("Ambience", -0.3)
  • bus AudioBus
  • pan number

The mixer’s buses, Master first.

for _, name in ipairs(Audio.get_buses()) do print(name, Audio.get_bus_volume(name)) end

Returns string[]

A bus’s level right now (post-fader peaks, falling off smoothly) - for in-game meters or music-reactive effects.

local l, r = Audio.bus_peak("Music"); self.node.scale = {1 + l * 0.2, 1 + l * 0.2}
  • bus AudioBus

Returns number left, number right

Audio.set_effect(bus, effect, param, value)

Section titled “Audio.set_effect(bus, effect, param, value)”

Sets an effect parameter on a bus: the effect by type name (the first of it) or 1-based position. Types and parameters: eq (low_gain, low_freq, mid_gain, mid_freq, high_gain, high_freq), lowpass / highpass (cutoff, resonance), compressor (threshold, ratio, attack, release, makeup), limiter (ceiling, release), delay (time, feedback, mix), reverb (size, damping, mix), distortion (drive, mix, output).

Audio.set_effect("Music", "lowpass", "cutoff", 600) -- muffle the music underwater
  • bus AudioBus
  • effect string|integer
  • param string
  • value number

Returns boolean ok false when there’s no such bus / effect / parameter

Audio.set_effect_enabled(bus, effect, enabled)

Section titled “Audio.set_effect_enabled(bus, effect, enabled)”

Enables or bypasses an effect on a bus.

Audio.set_effect_enabled("Music", "lowpass", in_water)
  • bus AudioBus
  • effect string|integer
  • enabled boolean

A low-pass filter on the whole mix (e.g. underwater, a pause menu) - a Low-pass effect on the Master bus.

Audio.set_lowpass(800) -- muffled while the pause menu is open
  • hz number Cutoff in Hz; 0 turns it off

Reverb on the whole mix - a Reverb effect on the Master bus.

Audio.set_reverb(0.4, 0.8)
  • mix number 0..1 (0 turns it off)
  • size? number Room size 0..1 (default 0.5)

Where positional sounds are heard from (nil: the current camera).

Audio.set_listener(Scene.find_node("Player"))
  • node Node?

Fades one MusicNode out (stopping it) while another fades in.

Audio.crossfade(Scene.find_node("ExploreMusic"), Scene.find_node("BattleMusic"), 2.0)
  • from Node? MusicNode playing now
  • to Node MusicNode to fade in (started if needed)
  • seconds number

Streams a music track from the given file path.

Audio.play_music("music/theme.ogg", true)
  • path AudioPath|string File path, e.g. “assets/audio/theme.ogg”
  • loop? boolean Whether to loop playback (default true)
  • bus? string The mixer bus (default “Music”)

Stops playing the specified music stream.

Audio.stop_music("music/theme.ogg")
  • path AudioPath|string

Pauses playback of the specified music stream.

Audio.pause_music("music/theme.ogg")
  • path AudioPath|string

Resumes playback of the specified paused music stream.

Audio.resume_music("music/theme.ogg")
  • path AudioPath|string

Checks if the specified music stream is currently playing.

if not Audio.is_music_playing("music/theme.ogg") then Audio.play_music("music/theme.ogg") end
  • path AudioPath|string

Returns boolean

Sets volume for a specific music stream.

Audio.set_music_volume("music/theme.ogg", 0.6)
  • path AudioPath|string
  • volume number Volume between 0.0 and 1.0

Sets the global master audio volume.

Audio.set_master_volume(0.75)
  • volume number Volume between 0.0 (silent) and 1.0 (full)

Global camera control operating on the active scene’s primary CameraNode.

Gets current target world position of the camera.

local cx, cy = Camera.get_target_position()

Returns number x, number y

Sets target world position for the camera.

local x, y = self.node:get_position(); Camera.set_target_position(x, y)
  • x number
  • y number

Sets a node for the camera to smoothly follow (nil stops following). A follow target picked in the Inspector is set automatically when the scene loads.

Camera.set_target(Scene.find_node("Player"))
  • target Node?

Adds a node to the group the camera frames: it centres on their weighted average and, with Auto Zoom on, zooms to keep them all in view. A single follow target joins the group first. Adding a node again updates its options.

Camera.add_target(Scene.find_node("Boss"), {weight = 2, radius = 120})
  • target Node
  • options? CameraTargetOptions

Takes a node out of the group.

Camera.remove_target(Scene.find_node("Boss"))
  • target Node

Stops following: clears the group and the follow target.

Camera.clear_targets()

The node the camera follows (single target), or nil.

local target = Camera.get_target(); if target then Log.info(target.name) end

Returns Node?

Every node the camera follows (the group, or the single target).

for _, node in ipairs(Camera.get_targets()) do Log.info(node.name) end

Returns Node[]

Gets configured target zoom level.

local zoom = Camera.get_zoom(); Camera.set_zoom(zoom * 1.1)

Returns number

Sets camera target zoom.

Camera.set_zoom(2.0)
  • zoom number

Gets screen offset vector.

local ox, oy = Camera.get_offset()

Returns number ox, number oy

Sets screen offset vector.

Camera.set_offset(0, -40)
  • ox number
  • oy number

Gets camera rotation in degrees.

local deg = Camera.get_rotation(); Camera.set_rotation(deg + 90 * dt)

Returns number

Sets camera rotation in degrees.

Camera.set_rotation(15)
  • degrees number

Triggers screen shake trauma.

Camera.shake(0.6, 0.3)
  • strength number Shake intensity (0.0 to 1.0)
  • duration? number Duration in seconds

Immediately stops active screen shake.

Camera.stop_shake()

Checks if screen shake is active.

if not Camera.is_shaking() then Camera.shake(0.3, 0.2) end

Returns boolean

Adds shake trauma to the active advanced camera (stacks up to 1; see AdvancedCameraNode).

Camera.add_trauma(0.3)
  • amount number 0..1

The advanced camera the scene’s camera shows (nil if there are none).

local cam = Camera.get_active()

Returns AdvancedCameraNode?

Camera.set_limits(left, right, top, bottom)

Section titled “Camera.set_limits(left, right, top, bottom)”

Constrains camera movement within bounding rectangle limits.

Camera.set_limits(0, 3200, 0, 1800)
  • left number
  • right number
  • top number
  • bottom number

Clears all bounding rectangle limits.

Camera.clear_limits()

Transforms screen coordinates to world coordinates.

local wx, wy = Camera.screen_to_world(Input.get_mouse_position())
  • sx number Screen X
  • sy number Screen Y

Returns number wx World X, number wy World Y

Transforms world coordinates to screen pixel coordinates.

local sx, sy = Camera.world_to_screen(self.node:get_position())
  • wx number World X
  • wy number World Y

Returns number sx Screen X, number sy Screen Y

Gets visible world bounds rectangle currently seen by camera.

local x, y, w, h = Camera.get_visible_world_bounds()

Returns number x, number y, number width, number height

Global physics world queries, gravity, and simulation control.

Gets world gravity vector in pixels per second squared.

local gx, gy = Physics.get_gravity()

Returns number gx, number gy

Sets world gravity vector.

Physics.set_gravity(0, 980)
  • gx number
  • gy number

Draws collision shapes, joints and areas while the game runs (debugging); the project’s Show Colliders setting (physics.debug_draw) sets the start.

if Input.is_key_pressed("F3") then Physics.set_debug_draw(not Physics.get_debug_draw()) end
  • on boolean

Whether colliders are drawn while the game runs.

local showing = Physics.get_debug_draw()

Returns boolean

Checks if physics simulation is paused.

if Physics.is_paused() then Physics.set_paused(false) end

Returns boolean

Pauses or unpauses physics simulation.

Physics.set_paused(true)
  • paused boolean

Physics.raycast(startX, startY, endX, endY)

Section titled “Physics.raycast(startX, startY, endX, endY)”

Casts a ray between start and end world points against non-sensor colliders.

local hit = Physics.raycast(x, y, x, y + 64); if hit and hit.node then Log.info(hit.node.name) end
  • startX number
  • startY number
  • endX number
  • endY number

Returns RaycastHit? hit Raycast hit details or nil if no obstacle was struck

The project’s name for collision layer n (Project Settings > Physics), or “”.

local name = Physics.layer_name(3); Log.info("Layer 3: " .. name)
  • n integer 1-16

Returns string name

Active scene queries, node discovery, switching, and dynamic instantiation.

Finds any node in the active scene hierarchy by name.

local player = Scene.find_node("Player"); if player then player:queue_free() end
  • name NodeName|string Name of node to find

Returns Node? node Found node or nil

Every node in the active scene with a tag, in tree order.

for _, enemy in ipairs(Scene.find_by_tag("enemy")) do enemy:queue_free() end
  • tag NodeTag|string

Returns Node[]

Sets the scene’s ambient light (0..1): the colour where no light reaches. White = an unlit scene. With LightNodes, the scene is lit by them on top.

Scene.set_ambient(0.2, 0.2, 0.35)
  • r number
  • g number
  • b number
  • a? number

Gets the scene’s ambient light (0..1).

local r, g, b, a = Scene.get_ambient()

Returns number r, number g, number b, number a

Gets the name of the currently active scene.

Log.info("Entered scene: " .. Scene.get_name())

Returns string

Switches to another scene by name (or its file, e.g. a scene property) - at once, or behind a transition (the screen is covered, the scene switches once its assets are loaded, then it’s uncovered).

With loading, it goes through a loading screen: a transition into that scene, the target loading in the background (textures decoded on worker threads), then a transition into the target once it’s loaded.

Scene.change_scene("level_2", {transition = "fade", duration = 0.5, loading = "loading"})
  • name SceneName|ScenePath A scene’s name, or its file
  • transition? SceneTransition

Starts loading a scene’s assets in the background (a little each frame), so switching to it later is quick.

Scene.preload("level_2")
  • name SceneName

Returns how far a scene started with Scene.preload has loaded, from 0 to 1.

local p = Scene.preload_progress("level_2"); Log.info("Loading: " .. math.floor(p * 100) .. "%")
  • name SceneName

Returns number progress 0..1

Returns whether a scene transition is currently in progress.

if Scene.is_transitioning() then return end

Returns boolean

How far the scene behind the loading screen has loaded, 0 to 1 (1 when nothing is loading) - for a loading screen’s progress bar.

self.node:get_node("Bar").value = Scene.loading_progress() * 100

Returns number

The name of the scene the loading screen is loading, or nil.

local next = Scene.loading_target()

Returns string?

Reloads the currently active scene.

if Input.is_key_pressed("R") then Scene.reload() end

Returns an array of all active nodes in the current scene.

Log.info("Node count: " .. #Scene.get_nodes())

Returns Node[]

Dynamically instantiates a new scene node and adds it to the active scene graph.

local timer = Scene.create_node("TimerNode", "Cooldown", self.node)
  • nodeType string Any node type: “Node”, “TextureNode”, “UIButtonNode”, “UILabelNode”, “PathNode”, … (or a short name from Add Node’s search: “sprite”, “timer”, “path”)
  • name? string Optional node name
  • parent? Node Optional parent node (defaults to scene root)

Returns Node? node Newly spawned node

Scene.instantiate(component, parent, x, y)

Section titled “Scene.instantiate(component, parent, x, y)”

Instances a component at runtime - Scene.instantiate("components/Bullet.component.json", self.node:get_parent(), self.node.position). Added under parent (else the scene’s root), at x, y local to it (two numbers or a Vec2). Returns its root node (typed per component), or nil.

local b = Scene.instantiate("components/Bullet.component.json", self.node:get_parent(), self.node.position)
  • component ComponentPath|string
  • parent? Node
  • x? number|Vec2
  • y? number

Returns Node?

Checks if debug collider and bounds rendering is enabled on this scene.

local on = Scene.get_draw_bounds(); Scene.set_draw_bounds(not on)

Returns boolean

Enables or disables debug bounds rendering on the scene.

Scene.set_draw_bounds(true)
  • draw boolean

Engine logging subsystem routed to Odin’s logging channels.

Logs an informational message.

Log.info("player spawned at", x, y)
  • ... any

Logs a warning message.

Log.warn("missing texture:", "textures/coin.png")
  • ... any

Logs an error message.

Log.error("could not load level", "level_2")
  • ... any

Sampling curve script properties ({ type = “curve” }: points {t, v}).

The curve’s value at t (linear between points; clamped at the ends).

local speed = self.max_speed * Curve.sample(self.speed_curve, self.elapsed / 2.0)
  • points number[][] The property’s value ({{t, v}, …})
  • t number 0..1 (any value works)

Returns number

Sampling gradient script properties ({ type = “gradient” }: stops {t, r, g, b, a}).

The gradient’s colour at t (linear between stops), 0..1 channels.

local r, g, b, a = Gradient.sample(self.tint, self.health / self.max_health)
  • stops number[][] The property’s value ({{t, r, g, b, a}, …})
  • t number 0..1

Returns number r, number g, number b, number a

Section titled “Navigation.find_path(map, x1, y1, x2, y2, opts)”

Same as map:nav_find_path(x1, y1, x2, y2, opts).

local path = Navigation.find_path(Scene.find_node("Level"), x, y, tx, ty, {smooth = false})
  • map Node TileMapNode or LdtkLevelNode
  • x1 number
  • y1 number
  • x2 number
  • y2 number
  • opts? NavOptions

Returns number[][]? path

Switches the language texts are shown in (a column of the locale tables).

Locale.set("fr")
  • language Language|string e.g. “fr” (the project’s locale tables’ languages)

Returns the current language.

if Locale.get() == "fr" then self.flag = "textures/flag_fr.png" end

Returns string language

Returns the languages available in the project’s locale tables.

for _, lang in ipairs(Locale.languages()) do Log.info(lang) end

Returns string[] languages The languages the tables have

Engine project management, data-driven scene serialization, and .rayzr package access.

Loads a complete project either from a directory containing project.json or from a .rayzr package archive.

local ok = Project.load("assets/demo_project.rayzr")
  • pathOrDir string Path to directory or .rayzr file

Returns boolean success

Project.export_rayzr(projectDir, outRayzrPath)

Section titled “Project.export_rayzr(projectDir, outRayzrPath)”

Packages a loose project directory into a portable .rayzr container archive with zlib compression.

local ok = Project.export_rayzr("assets/demo_project", "bin/demo_project.rayzr")
  • projectDir string Source directory path
  • outRayzrPath string Target output .rayzr file path

Returns boolean success

Project.export_ore(projectDir, outRayzrPath)

Section titled “Project.export_ore(projectDir, outRayzrPath)”

The old name of export_rayzr (before the engine was renamed from Ore).

Project.export_ore("assets/demo_project", "bin/demo_project.rayzr")
  • projectDir string
  • outRayzrPath string

Returns boolean success

Serializes the currently active scene graph and node states to a JSON formatted string.

local json = Project.serialize_scene(); Log.info(#json, "bytes")

Returns string json

Reads a text file either from the mounted .rayzr archive package or the loose project directory.

local text = Project.read_file("data/items.json"); if text then Log.info(text) end
  • relPath string Project-relative file path

Returns string? content File contents as a string, or nil if not found

Checks if a file exists in the active project (.rayzr archive or disk).

if Project.has_file("levels/level_2.json") then Scene.change_scene("level_2") end
  • relPath string Project-relative file path

Returns boolean exists