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})nodeNodepropertystringvaluenumber|number[]durationnumberSecondsease?EaseNameDefault “out_quad”options?TweenOptions
Returns integer id
Tween.cancel(id)
Section titled “Tween.cancel(id)”Stops a tween (it stays where it is).
if self.fade_id then Tween.cancel(self.fade_id) endidinteger
Returns boolean found
Tween.cancel_all(node, property)
Section titled “Tween.cancel_all(node, property)”Stops a node’s tweens (all, or one property’s).
local stopped = Tween.cancel_all(self.node, "position")nodeNodeproperty?string
Returns integer count
Tween.is_running(id)
Section titled “Tween.is_running(id)”Whether the tween with this id is still playing.
if not Tween.is_running(self.bounce_id) then self.bounce_id = nil endidinteger
Returns boolean
Tween.ease(ease, t)
Section titled “Tween.ease(ease, t)”Maps progress 0..1 through an easing curve (for your own animation).
local k = Tween.ease("in_out_sine", self.elapsed / self.duration)easeEaseNametnumber
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.
Pool.take(component, parent, x, y)
Section titled “Pool.take(component, parent, x, y)”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)componentComponentPath|stringparent?Nodex?number|Vec2y?number
Returns Node?
Pool.release(node)
Section titled “Pool.release(node)”Returns a taken instance to its pool (the same as node:destroy() on it).
Pool.release(self.node)nodeNode
Returns boolean ok false when it isn’t a taken pooled node
Pool.prewarm(component, count)
Section titled “Pool.prewarm(component, count)”Makes instances up front (parked) so the first takes don’t create them mid-game.
Pool.prewarm("components/bullet.component.json", 64)componentComponentPath|stringcountintegerInstances in total
Pool.set_max(component, max)
Section titled “Pool.set_max(component, max)”Caps how many instances the pool makes (0 = no cap); take returns nil at the cap.
Pool.set_max("components/spark.component.json", 200)componentComponentPath|stringmaxinteger
Pool.stats(component)
Section titled “Pool.stats(component)”How many instances are taken, parked and made in total.
local active, parked, made = Pool.stats("components/bullet.component.json")componentComponentPath|string
Returns integer active, integer parked, integer created
Accessibility
Section titled “Accessibility”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.
Accessibility.get(key)
Section titled “Accessibility.get(key)”A setting’s value.
local s = Accessibility.get("ui_scale")keyAccessibilityKey
Returns any
Accessibility.set(key, value)
Section titled “Accessibility.set(key, value)”Changes a setting (false for an unknown key or value).
Accessibility.set("colorblind", "deuteranopia")keyAccessibilityKeyvaluenumber|boolean|string
Returns boolean ok
Accessibility.get_all()
Section titled “Accessibility.get_all()”Every setting.
for k, v in pairs(Accessibility.get_all()) do print(k, v) endReturns AccessibilitySettings
Accessibility.save()
Section titled “Accessibility.save()”Saves the player’s settings.
Accessibility.save()Returns boolean ok
Accessibility.reset()
Section titled “Accessibility.reset()”Back to the project’s defaults (save to keep).
Accessibility.reset()Subtitles
Section titled “Subtitles”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.
Subtitles.show(text, options)
Section titled “Subtitles.show(text, options)”Shows a line; returns its id (0 when the player turned that kind off).
Subtitles.show("Over here!", {speaker = "Ada"})textstringRich textoptions?SubtitleOptions
Returns integer id
Subtitles.hide(id)
Section titled “Subtitles.hide(id)”Takes a line down early.
Subtitles.hide(id)idinteger
Subtitles.clear()
Section titled “Subtitles.clear()”Takes every line down.
Subtitles.clear()Subtitles.count()
Section titled “Subtitles.count()”Lines on screen now.
if Subtitles.count() == 0 then self:next_bark() endReturns 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/
Save.set(key, value)
Section titled “Save.set(key, value)”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) endkeySaveKey|stringvalueSaveValue
Save.get(key, default)
Section titled “Save.get(key, default)”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})keySaveKey|stringdefault?SaveValueReturned when the key isn’t set
Returns SaveValue
Save.has(key)
Section titled “Save.has(key)”Whether a value is stored under the key.
if not Save.has("tutorial_done") then Scene.change_scene("tutorial") endkeySaveKey|string
Returns boolean
Save.delete(key)
Section titled “Save.delete(key)”Removes a key from the save data.
Save.delete("checkpoint")keySaveKey|string
Save.keys()
Section titled “Save.keys()”Returns every stored key, sorted.
for _, key in ipairs(Save.keys()) do Log.info(key, Save.get(key)) endReturns string[] keys Sorted
Save.clear()
Section titled “Save.clear()”Removes every key (the file is rewritten on the next flush).
Save.clear(); Save.flush()Save.flush()
Section titled “Save.flush()”Writes the save file now.
if not Save.flush() then Log.warn("could not write save file") endReturns boolean ok
Save.slot(name)
Section titled “Save.slot(name)”Switches to another save file (
local previous = Save.slot("slot_2")name?string
Returns string slot
Save.path()
Section titled “Save.path()”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).
Crash.pending()
Section titled “Crash.pending()”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) endReturns CrashReport[]
Crash.dismiss(path)
Section titled “Crash.dismiss(path)”Stops offering a report (it stays in the folder as .seen).
Crash.dismiss(report.path)pathstringA path from Crash.pending()
Returns boolean ok
Crash.note(text)
Section titled “Crash.note(text)”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)textstring
Crash.dir()
Section titled “Crash.dir()”The crash folder.
print("crash reports in " .. Crash.dir())Returns string
Engine
Section titled “Engine”Core engine statistics, frame timing, and display information.
Engine.get_fps()
Section titled “Engine.get_fps()”Gets current display refresh frames per second.
Log.info("FPS: " .. Engine.get_fps())Returns integer
Engine.get_delta_time()
Section titled “Engine.get_delta_time()”Gets the last frame duration in seconds.
local dt = Engine.get_delta_time(); self.elapsed = self.elapsed + dtReturns number
Engine.get_time()
Section titled “Engine.get_time()”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
Engine.get_window_size()
Section titled “Engine.get_window_size()”Gets current physical window width and height in screen pixels.
local w, h = Engine.get_window_size()Returns integer width, integer height
Engine.get_virtual_size()
Section titled “Engine.get_virtual_size()”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
Engine.set_paused(paused)
Section titled “Engine.set_paused(paused)”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()) endpausedboolean
Engine.is_paused()
Section titled “Engine.is_paused()”Returns whether the game is currently paused.
if Engine.is_paused() then return endReturns boolean
Engine.set_fullscreen(on)
Section titled “Engine.set_fullscreen(on)”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()) endonboolean
Engine.is_fullscreen()
Section titled “Engine.is_fullscreen()”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).
Input.is_action_pressed(action, player)
Section titled “Input.is_action_pressed(action, player)”Checks if an action was pressed this frame (just triggered).
if Input.is_action_pressed("jump") then self.vy = -self.jump_speed endactionInputActionAction name from the project’s Input settings (e.g. “move_up”, “fire”, “jump”, “dash”)player?integer1-4 (default 1)
Returns boolean
Input.is_action_down(action, player)
Section titled “Input.is_action_down(action, player)”Checks if an action is currently held down.
if Input.is_action_down("fire", self.player) then self:shoot() endactionInputActionplayer?integer1-4 (default 1)
Returns boolean
Input.is_action_released(action, player)
Section titled “Input.is_action_released(action, player)”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 endactionInputActionplayer?integer1-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.speedneg_actionInputActionAction name representing negative direction (e.g. “move_left”)pos_actionInputActionAction name representing positive direction (e.g. “move_right”)player?integer1-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|integerThe action, or the player (the move_* actions)right?InputActionup?InputActiondown?InputActionplayer?integer1-4 (default 1)
Returns number x Normalized X axis, number y Normalized Y axis
Input.is_key_pressed(key)
Section titled “Input.is_key_pressed(key)”Queries raw keyboard key press state for a single frame.
if Input.is_key_pressed("F5") then Scene.reload() endkeyKeyName|stringKey name like “SPACE”, “W”, “Z”, “F5”, “LEFT_ALT”
Returns boolean
Input.is_key_down(key)
Section titled “Input.is_key_down(key)”Queries raw keyboard key held state.
if Input.is_key_down("LEFT_SHIFT") then speed = speed * 2 endkeyKeyName|stringKey name
Returns boolean
Input.is_key_released(key)
Section titled “Input.is_key_released(key)”Queries raw keyboard key release state for a single frame.
if Input.is_key_released("SPACE") then self.charging = false endkeyKeyName|stringKey name
Returns boolean
Input.is_mouse_button_pressed(button)
Section titled “Input.is_mouse_button_pressed(button)”Queries mouse button pressed state for a single frame.
if Input.is_mouse_button_pressed("LEFT") then self:shoot() endbuttonMouseButton|string
Returns boolean
Input.is_mouse_button_down(button)
Section titled “Input.is_mouse_button_down(button)”Queries mouse button held state.
if Input.is_mouse_button_down("RIGHT") then self.aiming = true endbuttonMouseButton|string
Returns boolean
Input.is_mouse_button_released(button)
Section titled “Input.is_mouse_button_released(button)”Queries mouse button release state for a single frame.
if Input.is_mouse_button_released("LEFT") then self.dragging = false endbuttonMouseButton|string
Returns boolean
Input.get_mouse_position()
Section titled “Input.get_mouse_position()”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
Input.get_mouse_wheel_move()
Section titled “Input.get_mouse_wheel_move()”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
Input.get_bindings(action)
Section titled “Input.get_bindings(action)”An action’s bindings, each with a player-facing text.
for i, b in ipairs(Input.get_bindings("jump")) do print(i, b.text) endactionInputAction
Returns InputBindingInfo[]
Input.binding_text(action, slot)
Section titled “Input.binding_text(action, slot)”What to show for an action’s bindings: “Space / A” (or one slot’s).
label.text = "Jump: " .. Input.binding_text("jump")actionInputActionslot?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"})actionInputActionslotintegerbindingInputBindingSpec|nilconflicts?"clear"|"swap"|"allow"
Returns boolean ok false for an unknown action
Input.listen(action, options)
Section titled “Input.listen(action, options)”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})actionInputActionoptions?InputListenOptions
Returns boolean ok false for an unknown action
Input.is_listening()
Section titled “Input.is_listening()”Whether Input.listen is waiting for a press.
if Input.is_listening() then return end -- the menu ignores input meanwhileReturns boolean
Input.cancel_listen()
Section titled “Input.cancel_listen()”Stops waiting (on_done gets “cancelled”).
Input.cancel_listen()Input.reset_bindings(action)
Section titled “Input.reset_bindings(action)”Back to the project’s bindings: one action, or all of them. Saved for the player.
Input.reset_bindings()action?InputAction
Input.save_bindings()
Section titled “Input.save_bindings()”Saves the player’s bindings now (set_binding, listen and reset_bindings already do).
Input.save_bindings()Returns boolean ok
Input.find_conflicts(binding)
Section titled “Input.find_conflicts(binding)”The actions that already use a binding.
local used = Input.find_conflicts({key = "SPACE"})bindingInputBindingSpec
Returns string[]
Input.last_device()
Section titled “Input.last_device()”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"
Input.get_actions()
Section titled “Input.get_actions()”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)) endReturns string[]
Input.vibrate(low, high, seconds, player)
Section titled “Input.vibrate(low, high, seconds, player)”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)lownumber0-1high?number0-1 (default: low)seconds?numberDefault 0.2player?integer1-4 (default 1)
Returns boolean
Input.stop_vibration(player)
Section titled “Input.stop_vibration(player)”Stops a player’s rumble.
Input.stop_vibration(2)player?integer1-4 (default 1)
Input.get_vibration(player)
Section titled “Input.get_vibration(player)”The rumble a player’s gamepad has now (0, 0: none).
local low, high = Input.get_vibration()player?integer1-4 (default 1)
Returns number low, number high
Input.glyph(action, player, device)
Section titled “Input.glyph(action, player, device)”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”
actionInputActionplayer?integer1-4 (default 1)device?"keyboard"|"xbox"|"playstation"|"switch"|"steamdeck"Instead of the player’s device
Returns string?
Input.device(player)
Section titled “Input.device(player)”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" endplayer?integer1-4 (default 1)
Returns "keyboard"|"xbox"|"playstation"|"switch"|"steamdeck"|"generic"|"none"
Input.is_gamepad_connected(player)
Section titled “Input.is_gamepad_connected(player)”Does the player have a gamepad connected?
if not Input.is_gamepad_connected(2) then label.text = "P2: connect a controller" endplayer?integer1-4 (default 1)
Returns boolean
Input.gamepad_name(player)
Section titled “Input.gamepad_name(player)”The name of a player’s gamepad (nil: none).
Example: print(Input.gamepad_name(1)) - “Xbox Wireless Controller”
player?integer1-4 (default 1)
Returns string?
Input.get_player_gamepad(player)
Section titled “Input.get_player_gamepad(player)”The gamepad (1-4) a player has (nil: none).
local pad = Input.get_player_gamepad(2)playerinteger1-4
Returns integer?
Input.set_player_gamepad(player, pad)
Section titled “Input.set_player_gamepad(player, pad)”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 endplayerinteger1-4pad?integer1-4
Input.gamepads()
Section titled “Input.gamepads()”The connected gamepads.
for _, p in ipairs(Input.gamepads()) do print(p.pad, p.name, p.device, p.player) endReturns {pad: integer, name: string, device: string, player: integer?}[]
Input.player_count()
Section titled “Input.player_count()”How many players: player 1 plus every other player with a gamepad.
for p = 1, Input.player_count() do self:spawn_player(p) endReturns 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)pathAudioPath|stringFile path, e.g. “assets/audio/laser.wav”volume?number0..1 (default 1), before the bus faderpitch?numberDefault 1bus?stringThe mixer bus (default “SFX”)
Audio.play_sound_at(path, x, y, volume)
Section titled “Audio.play_sound_at(path, x, y, volume)”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)pathAudioPath|stringxnumberynumbervolume?number
Audio.set_bus_volume(bus, volume)
Section titled “Audio.set_bus_volume(bus, volume)”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)busAudioBusA bus name (case doesn’t matter)volumenumber
Audio.get_bus_volume(bus)
Section titled “Audio.get_bus_volume(bus)”Returns a bus’s fader (linear).
local vol = Audio.get_bus_volume("sfx"); Audio.set_bus_volume("sfx", vol * 0.5)busAudioBus
Returns number volume
Audio.set_bus_mute(bus, mute)
Section titled “Audio.set_bus_mute(bus, mute)”Mutes / unmutes a bus.
Audio.set_bus_mute("Voice", true)busAudioBusmuteboolean
Audio.set_bus_solo(bus, solo)
Section titled “Audio.set_bus_solo(bus, solo)”Solos a bus (only soloed buses - and what they feed - are heard).
Audio.set_bus_solo("Music", true)busAudioBussoloboolean
Audio.set_bus_pan(bus, pan)
Section titled “Audio.set_bus_pan(bus, pan)”Pans a bus: -1 left, 0 centre, 1 right.
Audio.set_bus_pan("Ambience", -0.3)busAudioBuspannumber
Audio.get_buses()
Section titled “Audio.get_buses()”The mixer’s buses, Master first.
for _, name in ipairs(Audio.get_buses()) do print(name, Audio.get_bus_volume(name)) endReturns string[]
Audio.bus_peak(bus)
Section titled “Audio.bus_peak(bus)”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}busAudioBus
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 underwaterbusAudioBuseffectstring|integerparamstringvaluenumber
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)busAudioBuseffectstring|integerenabledboolean
Audio.set_lowpass(hz)
Section titled “Audio.set_lowpass(hz)”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 openhznumberCutoff in Hz; 0 turns it off
Audio.set_reverb(mix, size)
Section titled “Audio.set_reverb(mix, size)”Reverb on the whole mix - a Reverb effect on the Master bus.
Audio.set_reverb(0.4, 0.8)mixnumber0..1 (0 turns it off)size?numberRoom size 0..1 (default 0.5)
Audio.set_listener(node)
Section titled “Audio.set_listener(node)”Where positional sounds are heard from (nil: the current camera).
Audio.set_listener(Scene.find_node("Player"))nodeNode?
Audio.crossfade(from, to, seconds)
Section titled “Audio.crossfade(from, to, seconds)”Fades one MusicNode out (stopping it) while another fades in.
Audio.crossfade(Scene.find_node("ExploreMusic"), Scene.find_node("BattleMusic"), 2.0)fromNode?MusicNode playing nowtoNodeMusicNode to fade in (started if needed)secondsnumber
Audio.play_music(path, loop, bus)
Section titled “Audio.play_music(path, loop, bus)”Streams a music track from the given file path.
Audio.play_music("music/theme.ogg", true)pathAudioPath|stringFile path, e.g. “assets/audio/theme.ogg”loop?booleanWhether to loop playback (default true)bus?stringThe mixer bus (default “Music”)
Audio.stop_music(path)
Section titled “Audio.stop_music(path)”Stops playing the specified music stream.
Audio.stop_music("music/theme.ogg")pathAudioPath|string
Audio.pause_music(path)
Section titled “Audio.pause_music(path)”Pauses playback of the specified music stream.
Audio.pause_music("music/theme.ogg")pathAudioPath|string
Audio.resume_music(path)
Section titled “Audio.resume_music(path)”Resumes playback of the specified paused music stream.
Audio.resume_music("music/theme.ogg")pathAudioPath|string
Audio.is_music_playing(path)
Section titled “Audio.is_music_playing(path)”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") endpathAudioPath|string
Returns boolean
Audio.set_music_volume(path, volume)
Section titled “Audio.set_music_volume(path, volume)”Sets volume for a specific music stream.
Audio.set_music_volume("music/theme.ogg", 0.6)pathAudioPath|stringvolumenumberVolume between 0.0 and 1.0
Audio.set_master_volume(volume)
Section titled “Audio.set_master_volume(volume)”Sets the global master audio volume.
Audio.set_master_volume(0.75)volumenumberVolume between 0.0 (silent) and 1.0 (full)
Camera
Section titled “Camera”Global camera control operating on the active scene’s primary CameraNode.
Camera.get_target_position()
Section titled “Camera.get_target_position()”Gets current target world position of the camera.
local cx, cy = Camera.get_target_position()Returns number x, number y
Camera.set_target_position(x, y)
Section titled “Camera.set_target_position(x, y)”Sets target world position for the camera.
local x, y = self.node:get_position(); Camera.set_target_position(x, y)xnumberynumber
Camera.set_target(target)
Section titled “Camera.set_target(target)”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"))targetNode?
Camera.add_target(target, options)
Section titled “Camera.add_target(target, options)”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})targetNodeoptions?CameraTargetOptions
Camera.remove_target(target)
Section titled “Camera.remove_target(target)”Takes a node out of the group.
Camera.remove_target(Scene.find_node("Boss"))targetNode
Camera.clear_targets()
Section titled “Camera.clear_targets()”Stops following: clears the group and the follow target.
Camera.clear_targets()Camera.get_target()
Section titled “Camera.get_target()”The node the camera follows (single target), or nil.
local target = Camera.get_target(); if target then Log.info(target.name) endReturns Node?
Camera.get_targets()
Section titled “Camera.get_targets()”Every node the camera follows (the group, or the single target).
for _, node in ipairs(Camera.get_targets()) do Log.info(node.name) endReturns Node[]
Camera.get_zoom()
Section titled “Camera.get_zoom()”Gets configured target zoom level.
local zoom = Camera.get_zoom(); Camera.set_zoom(zoom * 1.1)Returns number
Camera.set_zoom(zoom)
Section titled “Camera.set_zoom(zoom)”Sets camera target zoom.
Camera.set_zoom(2.0)zoomnumber
Camera.get_offset()
Section titled “Camera.get_offset()”Gets screen offset vector.
local ox, oy = Camera.get_offset()Returns number ox, number oy
Camera.set_offset(ox, oy)
Section titled “Camera.set_offset(ox, oy)”Sets screen offset vector.
Camera.set_offset(0, -40)oxnumberoynumber
Camera.get_rotation()
Section titled “Camera.get_rotation()”Gets camera rotation in degrees.
local deg = Camera.get_rotation(); Camera.set_rotation(deg + 90 * dt)Returns number
Camera.set_rotation(degrees)
Section titled “Camera.set_rotation(degrees)”Sets camera rotation in degrees.
Camera.set_rotation(15)degreesnumber
Camera.shake(strength, duration)
Section titled “Camera.shake(strength, duration)”Triggers screen shake trauma.
Camera.shake(0.6, 0.3)strengthnumberShake intensity (0.0 to 1.0)duration?numberDuration in seconds
Camera.stop_shake()
Section titled “Camera.stop_shake()”Immediately stops active screen shake.
Camera.stop_shake()Camera.is_shaking()
Section titled “Camera.is_shaking()”Checks if screen shake is active.
if not Camera.is_shaking() then Camera.shake(0.3, 0.2) endReturns boolean
Camera.add_trauma(amount)
Section titled “Camera.add_trauma(amount)”Adds shake trauma to the active advanced camera (stacks up to 1; see AdvancedCameraNode).
Camera.add_trauma(0.3)amountnumber0..1
Camera.get_active()
Section titled “Camera.get_active()”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)leftnumberrightnumbertopnumberbottomnumber
Camera.clear_limits()
Section titled “Camera.clear_limits()”Clears all bounding rectangle limits.
Camera.clear_limits()Camera.screen_to_world(sx, sy)
Section titled “Camera.screen_to_world(sx, sy)”Transforms screen coordinates to world coordinates.
local wx, wy = Camera.screen_to_world(Input.get_mouse_position())sxnumberScreen XsynumberScreen Y
Returns number wx World X, number wy World Y
Camera.world_to_screen(wx, wy)
Section titled “Camera.world_to_screen(wx, wy)”Transforms world coordinates to screen pixel coordinates.
local sx, sy = Camera.world_to_screen(self.node:get_position())wxnumberWorld XwynumberWorld Y
Returns number sx Screen X, number sy Screen Y
Camera.get_visible_world_bounds()
Section titled “Camera.get_visible_world_bounds()”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
Physics
Section titled “Physics”Global physics world queries, gravity, and simulation control.
Physics.get_gravity()
Section titled “Physics.get_gravity()”Gets world gravity vector in pixels per second squared.
local gx, gy = Physics.get_gravity()Returns number gx, number gy
Physics.set_gravity(gx, gy)
Section titled “Physics.set_gravity(gx, gy)”Sets world gravity vector.
Physics.set_gravity(0, 980)gxnumbergynumber
Physics.set_debug_draw(on)
Section titled “Physics.set_debug_draw(on)”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()) endonboolean
Physics.get_debug_draw()
Section titled “Physics.get_debug_draw()”Whether colliders are drawn while the game runs.
local showing = Physics.get_debug_draw()Returns boolean
Physics.is_paused()
Section titled “Physics.is_paused()”Checks if physics simulation is paused.
if Physics.is_paused() then Physics.set_paused(false) endReturns boolean
Physics.set_paused(paused)
Section titled “Physics.set_paused(paused)”Pauses or unpauses physics simulation.
Physics.set_paused(true)pausedboolean
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) endstartXnumberstartYnumberendXnumberendYnumber
Returns RaycastHit? hit Raycast hit details or nil if no obstacle was struck
Physics.layer_name(n)
Section titled “Physics.layer_name(n)”The project’s name for collision layer n (Project Settings > Physics), or “”.
local name = Physics.layer_name(3); Log.info("Layer 3: " .. name)ninteger1-16
Returns string name
Active scene queries, node discovery, switching, and dynamic instantiation.
Scene.find_node(name)
Section titled “Scene.find_node(name)”Finds any node in the active scene hierarchy by name.
local player = Scene.find_node("Player"); if player then player:queue_free() endnameNodeName|stringName of node to find
Returns Node? node Found node or nil
Scene.find_by_tag(tag)
Section titled “Scene.find_by_tag(tag)”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() endtagNodeTag|string
Returns Node[]
Scene.set_ambient(r, g, b, a)
Section titled “Scene.set_ambient(r, g, b, a)”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)rnumbergnumberbnumbera?number
Scene.get_ambient()
Section titled “Scene.get_ambient()”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
Scene.get_name()
Section titled “Scene.get_name()”Gets the name of the currently active scene.
Log.info("Entered scene: " .. Scene.get_name())Returns string
Scene.change_scene(name, transition)
Section titled “Scene.change_scene(name, transition)”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"})nameSceneName|ScenePathA scene’s name, or its filetransition?SceneTransition
Scene.preload(name)
Section titled “Scene.preload(name)”Starts loading a scene’s assets in the background (a little each frame), so switching to it later is quick.
Scene.preload("level_2")nameSceneName
Scene.preload_progress(name)
Section titled “Scene.preload_progress(name)”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) .. "%")nameSceneName
Returns number progress 0..1
Scene.is_transitioning()
Section titled “Scene.is_transitioning()”Returns whether a scene transition is currently in progress.
if Scene.is_transitioning() then return endReturns boolean
Scene.loading_progress()
Section titled “Scene.loading_progress()”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() * 100Returns number
Scene.loading_target()
Section titled “Scene.loading_target()”The name of the scene the loading screen is loading, or nil.
local next = Scene.loading_target()Returns string?
Scene.reload()
Section titled “Scene.reload()”Reloads the currently active scene.
if Input.is_key_pressed("R") then Scene.reload() endScene.get_nodes()
Section titled “Scene.get_nodes()”Returns an array of all active nodes in the current scene.
Log.info("Node count: " .. #Scene.get_nodes())Returns Node[]
Scene.create_node(nodeType, name, parent)
Section titled “Scene.create_node(nodeType, name, parent)”Dynamically instantiates a new scene node and adds it to the active scene graph.
local timer = Scene.create_node("TimerNode", "Cooldown", self.node)nodeTypestringAny node type: “Node”, “TextureNode”, “UIButtonNode”, “UILabelNode”, “PathNode”, … (or a short name from Add Node’s search: “sprite”, “timer”, “path”)name?stringOptional node nameparent?NodeOptional 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)componentComponentPath|stringparent?Nodex?number|Vec2y?number
Returns Node?
Scene.get_draw_bounds()
Section titled “Scene.get_draw_bounds()”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
Scene.set_draw_bounds(draw)
Section titled “Scene.set_draw_bounds(draw)”Enables or disables debug bounds rendering on the scene.
Scene.set_draw_bounds(true)drawboolean
Engine logging subsystem routed to Odin’s logging channels.
Log.info(…)
Section titled “Log.info(…)”Logs an informational message.
Log.info("player spawned at", x, y)...any
Log.warn(…)
Section titled “Log.warn(…)”Logs a warning message.
Log.warn("missing texture:", "textures/coin.png")...any
Log.error(…)
Section titled “Log.error(…)”Logs an error message.
Log.error("could not load level", "level_2")...any
Sampling curve script properties ({ type = “curve” }: points {t, v}).
Curve.sample(points, t)
Section titled “Curve.sample(points, t)”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)pointsnumber[][]The property’s value ({{t, v}, …})tnumber0..1 (any value works)
Returns number
Gradient
Section titled “Gradient”Sampling gradient script properties ({ type = “gradient” }: stops {t, r, g, b, a}).
Gradient.sample(stops, t)
Section titled “Gradient.sample(stops, t)”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)stopsnumber[][]The property’s value ({{t, r, g, b, a}, …})tnumber0..1
Returns number r, number g, number b, number a
Navigation
Section titled “Navigation”Navigation.find_path(map, x1, y1, x2, y2, opts)
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})mapNodeTileMapNode or LdtkLevelNodex1numbery1numberx2numbery2numberopts?NavOptions
Returns number[][]? path
Locale
Section titled “Locale”Locale.set(language)
Section titled “Locale.set(language)”Switches the language texts are shown in (a column of the locale tables).
Locale.set("fr")languageLanguage|stringe.g. “fr” (the project’s locale tables’ languages)
Locale.get()
Section titled “Locale.get()”Returns the current language.
if Locale.get() == "fr" then self.flag = "textures/flag_fr.png" endReturns string language
Locale.languages()
Section titled “Locale.languages()”Returns the languages available in the project’s locale tables.
for _, lang in ipairs(Locale.languages()) do Log.info(lang) endReturns string[] languages The languages the tables have
Project
Section titled “Project”Engine project management, data-driven scene serialization, and .rayzr package access.
Project.load(pathOrDir)
Section titled “Project.load(pathOrDir)”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")pathOrDirstringPath 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")projectDirstringSource directory pathoutRayzrPathstringTarget 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")projectDirstringoutRayzrPathstring
Returns boolean success
Project.serialize_scene()
Section titled “Project.serialize_scene()”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
Project.read_file(relPath)
Section titled “Project.read_file(relPath)”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) endrelPathstringProject-relative file path
Returns string? content File contents as a string, or nil if not found
Project.has_file(relPath)
Section titled “Project.has_file(relPath)”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") endrelPathstringProject-relative file path
Returns boolean exists
