Venus Lua API › Core

GameObject

Scene node — hierarchy, transforms, lifecycle.

authored lua/venus_lua/src/lua_gameobject.rs

GameObject

A node in the 3D scene graph: a transform (position / rotation / scale), a place in the parent
→ child hierarchy, and a lifecycle (active / inactive / destroyed). Components — renderers,
colliders, lights, controllers, scripts — attach to a GameObject and read its transform.

Static calls use the type table (GameObject.Create()); instance methods use colon syntax on a
handle (gameObject:SetLocalPosition(...)).

Types

GameObject — an opaque handle (userdata) to a scene node. A handle may be null or stale
(its object destroyed); it is safe to keep and pass around. Calling a method on a null/stale handle
never raises — it degrades to a documented default. Most getters and every setter warn
(returning nil or a no-op); the hierarchy-navigation getters instead degrade silently to an
empty value (see Functions). tostring(gameObject) yields GameObject{name}, or
GameObject{NULL} if dead or unnamed.

Creation & Destruction

GameObject.Create( ) : GameObject
Creates a new, active GameObject at the origin. Returns nil and logs an error if the scene
object pool is exhausted.

GameObject.CreateInactive( ) : GameObject
Same as Create, but the object starts disabled (its components do not run until activated).

GameObject.Instantiate( GameObject source ) : GameObject
Recursively clones source and its children. Returns nil and logs a warning if source is
null.

GameObject.Get( string name ) : GameObject
Finds a GameObject by its unique name. Returns nil if no match exists (no warning — a miss is a valid result).

GameObject.Exists( GameObject object ) : boolean
Returns true if the handle refers to a live object. Safe on null/stale handles (returns
false) — this is the one call that never warns.

gameObject:Destroy( ) : void
Destroys the object (and its children) if it exists; logs a warning otherwise. The object stops
existing immediatelyExists() is false the moment this returns, and neither it nor its
children receive any further callbacks. Called from outside a callback the teardown is
synchronous; called from inside one (e.g. destroying self from Update) the teardown proper —
its components’ OnDestroy, and slot reclaim — is deferred to the end of the update cycle, so the
calling component’s own data stays valid until its callback returns. Either way, calling it on self
is safe.

Zone ownership & lifetime. A GameObject you create from Lua — Create, CreateInactive, or a
parentless Instantiate — is recorded as a spawned root of the zone that is loading when the
call runs
. When that zone unloads, the engine destroys its spawned roots, so a Lua-created object
lives only as long as its owning zone. There is no DontDestroyOnLoad binding. The unload
sweep destroys only handles that are still roots, so to keep an object across an unload,
SetParent it under longer-lived content (e.g. a persistent object from an earlier/session zone)
before that zone unloads — reparenting lifts it out of this zone’s sweep. Two traps: reparenting
under another object in the same zone does not save it (the whole subtree is destroyed
together), and MakeRoot() does not help — a root stays on the destroy list.

Functions

Read-only queries. Their null/stale-handle behavior falls in three groups:

gameObject:Active( ) : boolean
The object’s own enabled flag, ignoring ancestors.

gameObject:ActiveInHierarchy( ) : boolean
true only if this object and every ancestor are active.

gameObject:Parent( ) : GameObject
The parent, or nil if the object is a scene root (a root is not an error — no warning).

gameObject:Root( ) : GameObject
The topmost ancestor (itself if already a root).

gameObject:Children( ) : table
An array (1-based) of the direct children.

gameObject:ChildCount( ) : integer
Number of direct children.

gameObject:GetChild( integer index ) : GameObject
The child at index (1-based); nil if out of range.

gameObject:FindChild( string name ) : GameObject
The direct child named name; nil if none. Direct children only — use FindChildRecursive to search the whole subtree.

gameObject:FindChildRecursive( string name ) : GameObject
Depth-first (pre-order) search of all descendants for the first one named name; nil if none. Does not consider the object itself.

gameObject:IsChildOf( GameObject parent ) : boolean
true if parent is this object’s ancestor.

gameObject:IsRoot( ) : boolean
true if the object has no parent.

gameObject:FirstChild( ) : GameObject
First direct child, or nil.

gameObject:NextSibling( ) : GameObject
Next sibling under the shared parent, or nil.

gameObject:PreviousSibling( ) : GameObject
Previous sibling, or nil.

gameObject:Forward( ) : Vector3
World-space forward axis (-Z rotated by world rotation).

gameObject:Right( ) : Vector3
World-space right axis.

gameObject:Up( ) : Vector3
World-space up axis.

gameObject:TransformPoint( Vector3 point ) : Vector3
Transforms point from local to world space (scale, rotation, translation). Accepts a Vector3
or three numbers.

gameObject:TransformVector( Vector3 vector ) : Vector3
Local → world using scale and rotation (no translation).

gameObject:TransformDirection( Vector3 direction ) : Vector3
Local → world using rotation only.

gameObject:InverseTransformPoint( Vector3 point ) : Vector3
World → local (scale, rotation, translation).

gameObject:InverseTransformVector( Vector3 vector ) : Vector3
World → local using scale and rotation.

gameObject:InverseTransformDirection( Vector3 direction ) : Vector3
World → local using rotation only.

Properties (get / set)

Setters return void and no-op with a warning on a null/stale handle.

ID

gameObject:ID( ) : string — the object’s unique name. A freshly Create()d object has no
name
until SetID: until then ID() logs a warning (NotFound) and returns nil (and
tostring yields GameObject{NULL}).
gameObject:SetID( string name ) : void — sets the object’s unique name. The name must be
unique — if another live object already holds name, the rename is a silent no-op (the
object keeps its old name) and a warning is logged (reported as NotFound, though it is really a
name collision). An empty name is accepted and leaves the object nameless.

LocalPosition

gameObject:LocalPosition( ) : Vector3 — position relative to the parent.
gameObject:SetLocalPosition( Vector3 position ) : void — accepts a Vector3 or three
numbers; three numbers preserve f64 precision through the floating-origin split.

LocalRotation

gameObject:LocalRotation( ) : Quaternion — rotation relative to the parent.
gameObject:SetLocalRotation( Quaternion rotation ) : void

LocalScale

gameObject:LocalScale( ) : number — uniform local scale.
gameObject:SetLocalScale( number scale ) : void

LocalEulerAngles

gameObject:LocalEulerAngles( ) : Vector3 — local rotation as YXZ Euler angles, in
degrees.
gameObject:SetLocalEulerAngles( Vector3 degrees ) : void

WorldPosition

gameObject:WorldPosition( ) : number, number, number — the precise world position as three
f64 numbers x, y, z (not a Vector3, which would be f32-lossy at range):
local x, y, z = go:WorldPosition().
gameObject:SetWorldPosition( Vector3 position ) : void — accepts a Vector3 or three f64
numbers.

WorldRotation

gameObject:WorldRotation( ) : Quaternion
gameObject:SetWorldRotation( Quaternion rotation ) : void

WorldScale

gameObject:WorldScale( ) : number — accumulated world scale.
gameObject:SetWorldScale( number scale ) : void

EulerAngles

gameObject:EulerAngles( ) : Vector3 — world rotation as YXZ Euler angles, in degrees.
gameObject:SetEulerAngles( Vector3 degrees ) : void

Layer

gameObject:Layer( ) : integer — the render/collision layer mask: a 32-bit bitmask (fresh
objects default to 0xFFFFFFFF = all layers), not an index. A renderable is visible to a camera
only when objectMask & cameraMask ≠ 0.
gameObject:SetLayer( integer mask ) : void — sets the 32-bit layer mask.

Euler angle order variants

For each of the six rotation orders there are four accessors: world/local getter (returns a
Vector3 of degrees) and world/local setter (takes a Vector3 or three numbers of degrees).
EulerAngles / SetEulerAngles above are the default (YXZ) forms.

Order Get world Get local Set world Set local
XYZ EulerAnglesXYZ() LocalEulerAnglesXYZ() SetEulerAnglesXYZ(v) SetLocalEulerAnglesXYZ(v)
XZY EulerAnglesXZY() LocalEulerAnglesXZY() SetEulerAnglesXZY(v) SetLocalEulerAnglesXZY(v)
YXZ EulerAnglesYXZ() LocalEulerAnglesYXZ() SetEulerAnglesYXZ(v) SetLocalEulerAnglesYXZ(v)
YZX EulerAnglesYZX() LocalEulerAnglesYZX() SetEulerAnglesYZX(v) SetLocalEulerAnglesYZX(v)
ZXY EulerAnglesZXY() LocalEulerAnglesZXY() SetEulerAnglesZXY(v) SetLocalEulerAnglesZXY(v)
ZYX EulerAnglesZYX() LocalEulerAnglesZYX() SetEulerAnglesZYX(v) SetLocalEulerAnglesZYX(v)

Methods

Actions that mutate the object. All no-op with a warning on a null/stale handle.

gameObject:Activate( ) : void
Enables the object and recursively updates children.

gameObject:Deactivate( ) : void
Disables the object and recursively updates children.

gameObject:SetParent( GameObject parent ) : void
Reparents the object, keeping it in the scene. parent may be a GameObject handle or a
string name. No-op with a warning if either object is invalid or the reparent would form a cycle.

Keeps the LOCAL transform — the object moves in world space. Local position, rotation, and
scale are preserved verbatim; because they are now measured against the new parent, the
object’s world pose shifts. This is the opposite of Unity, whose SetParent(parent)
defaults to worldPositionStays = true (world pose kept, local recomputed). There is no
world-preserving overload
. To hold the world pose across a reparent, capture it first and
restore it after:

local x, y, z = go:WorldPosition()
local rot = go:WorldRotation()
go:SetParent(newParent)
go:SetWorldPosition(x, y, z)
go:SetWorldRotation(rot)

If the new parent has non-unit scale, also capture go:WorldScale() beforehand and restore it
with go:SetWorldScale(s).

gameObject:MakeRoot( ) : void
Detaches from the current parent, making the object a scene root.

gameObject:DetachChildren( ) : void
Unparents every direct child, making each a root.

gameObject:Translate( Vector3 delta ) : void
Adds delta to the object’s local position — movement in parent space (the parent’s local
frame), not along the object’s own facing. Accepts a Vector3 or three numbers.

Parent-space, not self-space. delta is added to the local position unchanged — it is not
rotated by the object’s own rotation, so this is not Unity’s default Translate (Space.Self,
which moves along the object’s own axes). For a root object (no parent) local space is world
space, so Translate there moves along world axes. To move along the object’s own facing at any
depth, drive the world position with the world-space axis vectors instead:

local x, y, z = go:WorldPosition()
local f = go:Forward()              -- world-space unit axis (also Right() / Up())
go:SetWorldPosition(x + f.x * d, y + f.y * d, z + f.z * d)

gameObject:Rotate( Quaternion rotation ) : void
Applies rotation on top of the current rotation.

gameObject:RotateAround( Vector3 point, Vector3 axis, number angle ) : void
Rotates the object angle radians about the world-space axis through point. point and
axis each accept a Vector3 or three numbers. (Note: unlike the SetEulerAngles* setters,
which take degrees, this angle is radians.)

gameObject:SetPositionAndRotation( Vector3 position, Quaternion rotation ) : void
Sets world position and rotation in one call. position accepts a Vector3 or three f64
numbers (three numbers preserve precision).

gameObject:LookAt( Vector3 target ) : void
gameObject:LookAt( Vector3 target, Vector3 up ) : void
Orients the object’s forward axis toward target. The optional up biases roll (defaults to
world up). target / up accept a Vector3 or three numbers.

Example

A ScriptBehavior file returns a table of callbacks; the engine attaches it to a
GameObject and invokes those callbacks, passing the component’s self (whose
self.object is this GameObject). The local Script = {} / return Script
scaffolding is required — a bare function Script.Update(...) is not a runnable file.

-- Orbit a child marker around its parent and keep it facing the parent's origin.
-- (Adapted from data/scripts/camera_orbit.lua.)
local Script = {}

function Script.Update(self)
    local go = self.object
    local t  = Time.Time()
    local r  = 5.0

    -- Position relative to the parent, on a circle in the XZ plane.
    go:SetLocalPosition(math.cos(t) * r, 1.5, math.sin(t) * r)

    -- Face the parent's world origin (three f64 numbers, precise at range).
    local px, py, pz = go:Parent():WorldPosition()
    go:LookAt(px, py, pz)
end

return Script