Working with the Scene Graph
Most gameplay scripts spend their time manipulating scene objects — the live nodes in the running scene. This guide explains how Items (project assets) relate to SceneObjects (runtime instances), and how to create, find, transform, and organize objects from Lua.
Items vs SceneObjects
| Concept | Role |
|---|---|
| Item | A persistent project asset (mesh, material, prefab, …) stored in the resource tree. Loaded via Asset Loading. |
| SceneObject | A runtime node in the active scene graph. May be placed in the editor or created from a script. Has transform, tags, layers, physics, and attached behaviours. |
Every Behaviour script has an implicit self.node property (Properties) that references the SceneObject the script is attached to.
Accessing the SceneObject module
local SceneObject = require 'engine/sceneobject'
local Transform = require 'engine/math/transform'
local Vector3 = require 'engine/math/vector3'
Finding objects
Use static search methods to locate objects by name, tag, or ID. All find functions support an optional root argument to limit the search to a subtree.
By name — returns the first match, or nil:
All by name — returns a table (may be empty):
local enemies = SceneObject.findAllByName("Enemy")
for _, enemy in ipairs(enemies) do
enemy:addTag("Hostile")
end
By tag:
local pickups = SceneObject.findByTag("Pickup")
for _, pickup in ipairs(pickups) do
pickup:delete()
end
By ID — useful when you stored an object's ID earlier:
Scoped search — search within a parent's hierarchy:
local level = SceneObject.findByName("Level1")
local door = level:findByName("Door") -- instance call: root defaults to level
local chest = SceneObject.findByName("Chest", level) -- explicit root
- Use
Property.Nodelinks in the inspector when references are known at design time. See Properties. Use find methods for dynamic lookup.
Creating objects
Creation is asynchronous — typed helpers such as SgSphere.create return a Promise. Use await() during setup or :next() at runtime.
Generic creation via SceneObject.create:
SceneObject.create(SceneObject.Type.Box, {
name = "Crate",
transform = Transform.new(Vector3.new(0, 1, 0)),
width = 1,
height = 1,
depth = 1,
}):next(function(crate)
crate:setActive(true)
end)
Typed helpers are clearer and expose type-specific options. See the Scene Objects API for the full list (SgSphere, SgMesh, SgPrefab, SgTerrain, …).
During init(), blocking setup with await is fine:
function Spawner:init()
self.crate = await(SgBox.create({
name = "Crate",
width = 1, height = 1, depth = 1,
transform = Transform.new(Vector3.new(0, 0.5, 0)),
}))
end
Transforms
Read the local or world transform:
Modify via withTransform (recommended — reads, mutates, writes back):
Shortcuts for common operations:
self.node:translate(1, 0, 0)
self.node:rotate(0, 90, 0)
self.node:scale(2, 2, 2)
self.node:setPosition(0, 5, 0)
Direction helpers (world space):
local forward = self.node:getForward()
local right = self.node:getRight()
local up = self.node:getUp()
See Math and Transforms for Vector3, Quaternion, and Transform in depth.
Hierarchy and parenting
local parent = self.node:getParent()
local children = self.node:getChildren()
for _, child in ipairs(children) do
print(child:getDisplayName())
end
-- Reparent at runtime
child:moveToParent(newParent)
Pass a parent SceneObject as the last argument to create functions to spawn directly under that node.
Tags and layers
Tags are string labels for grouping and search. Layers control rendering and physics filtering.
Active state and cleanup
self.node:setActive(false) -- hide and disable updates on this subtree
self.node:enable()
self.node:disable()
self.node:delete() -- remove from scene (cannot be undone)
Clone an existing object (optionally offset and reparent):
Physics on SceneObjects
Physics-enabled objects expose impulse, force, and velocity methods directly on SceneObject. See Physics Scripting for raycasts and collision events; the snippet below shows object-level forces:
local Vector3 = require 'engine/math/vector3'
self.node:applyCentralImpulse(Vector3.new(0, 5, 0))
self.node:setLinearVelocity(Vector3.new(2, 0, 0))
Fields, materials, and links
Scene objects expose datatype fields (the same fields you see in the inspector):
Link fields resolve to loaded Item handles:
Swap materials at runtime:
Cross-script access
To call methods on another object's behaviour script:
See Cross-Behaviour Calls for details and naming rules.
See also Properties for linking objects in the inspector.
Snapshots
Save and restore object state (useful for undo-style mechanics or checkpoints):
Choosing an approach
| Goal | Approach |
|---|---|
| Designer-placed reference | Property.Node on the behaviour |
| Runtime lookup | findByName / findByTag |
| Spawn from asset | SgPrefab.create or Loader.loadPrefab — see Prefabs |
| Procedural primitive | SgBox.create, SgSphere.create, … |
| Talk to another script | :behaviour("ScriptItemName") |
| Global game state | Variable Sets |
Further reading
- SceneObject — full API reference
- Asset Loading — loading meshes, materials, and prefab items
- Prefabs — instantiate prefab assets at runtime
- Properties — inspector-exposed script fields