Lua ScriptingAdvanced

gameObject API reference

Signatures for transforms, physics, lookup, audio, GUI, hierarchy, vehicles, and constraints.

Identity, lookup, and variables

SignatureResult
gameObject.idCurrent object UUID
destroy()Removes this object from the running simulation and returns true; false when the simulation is stopped or the object is already being removed (see Cross-object interactions)
getVar(name)number, string, boolean, object API, or nil
setVar(name, value)Sets a number, string, or boolean variable. It cannot set an object reference, and it does nothing on an object that has no variables at all, so define at least one on the object in the inspector first
getObject(uuid)Object API or nil
getObjectByName(name)Exact-name match or nil
getObjectsByNamePrefix(prefix)Array of object APIs
getObjectsByType(type)Array of object APIs

Transforms and hierarchy

getPosition(), setPosition(x,y,z), getRotation(), setRotation(x,y,z,w), rotate(x,y,z), getParent(), getChildren(), setParent(parentIdOrNil), getWorldPosition(), getWorldRotation(), getWorldScale(), getLocalPosition(), getLocalRotation(), getLocalScale(), getForward(), getRight(), isRoot().

getForward() and getRight()

Both return world-space unit vectors. For an ordinary object they are derived from its world rotation, where local +Z is forward and local +X is right — the same convention characters and vehicles face along.

A camera is the exception. On a camera, getForward() returns the direction it is actually rendering and getRight() its screen-right. Three.js cameras look down their own local -Z while the rest of Airogel faces +Z, so deriving a camera's forward from its rotation the ordinary way would hand you the opposite of what the player sees; these two calls reconcile that for you.

That is what makes camera-relative movement work: read the active camera's forward and right, flatten them to the XZ plane if you do not want pitch, and use those as your movement axes instead of fixed world X/Z.

local camera = scene.getActiveCamera()
local forward = camera.getForward()
local right = camera.getRight()

getPosition() and getRotation() on a camera likewise return the pose it is rendering from, including a first_person or third_person camera that the engine is moving to follow its target, so a script reading the camera during update() sees what the player sees.

Physics and queries

addForce(x,y,z), addImpulse(x,y,z), addTorque(x,y,z), getLinearVelocity(), setLinearVelocity(x,y,z), getAngularVelocity(), setAngularVelocity(x,y,z), resetPhysicsState(x,y,z,qx?,qy?,qz?,qw?), registerForCollisions(), deregisterForCollisions(), raycast(direction,maxDistance,options?), sphereQuery(radius), boxQuery(halfExtents).

setPosition() deliberately preserves momentum. Use resetPhysicsState() for respawn or recovery: it teleports, applies the optional rotation, clears linear and angular velocity and vehicle driver input, then wakes the body.

On a soft body, push rather than assign. addForce, addImpulse and addTorque all work on one, but setLinearVelocity and setAngularVelocity do not: a soft body's velocity is recomputed from its vertices every step, so an assigned value is discarded by the next one and the object simply carries on as it was. The matching getters are fine — they read the body's real aggregate motion. Soft bodies also need far larger numbers than their size suggests, because their mass is their vertex count; see Physics, collisions, and triggers.

raycast(direction, maxDistance, options)

Returns an array of hits sorted nearest first. The ray stops at the nearest object in its way: the array can hold more than one hit, but never anything behind that object. On a miss it returns an empty array, never nil or false, so #hits == 0 is the miss check and if hits then is always true. A zero-length direction, a non-finite maxDistance, or a maxDistance of zero or less also return empty rather than raising.

Each hit is a table with exactly these four fields:

FieldValue
shapeUUID of the object that was hit. This is the id field — there is no id, objectId or bodyId on a hit.
pointWorld-space contact point, as {x, y, z}
distanceMetres from the ray origin
fractionPosition along the ray, 0 to 1 of maxDistance

There is no surface normal on a hit.

Behaviour worth knowing before you rely on it:

  • The ray originates at the calling object's world position. To cast from a character's eye rather than its origin, resolve the camera and call raycast on that handle instead. The camera sits inside the character's capsule, so pass the character in ignore.
  • direction is world-space and is not rotated by the calling object's transform. Passing {x=0, y=0, z=1} casts along world +Z, which is only "forward" for an unrotated object. For object-relative aiming, rotate the direction yourself, or pass getForward() — on a camera that is the direction the player is looking.
  • direction does not need to be normalized; it is normalized for you.
  • options is an optional table. ignore lists objects to pass through, as object handles or UUIDs, in a list or a single one. ignoreSensors = true passes through trigger volumes, which are hit by default. ignoreSelf = false includes the calling object, which is ignored by default because a ray that starts inside it hits it at distance 0. These filter inside the query; skipping a hit in the script cannot reveal what is behind it, because nothing behind it was found. Example: gameObject.raycast(gameObject.getForward(), 100, { ignore = { player }, ignoreSensors = true }).
  • Before 9 October 2026 there were no options, and the results included the calling object.

sphereQuery(radius) and boxQuery(halfExtents) are centred on the calling object and return arrays of object UUIDs rather than hit tables. They also include the calling object when it has a collider.

Characters and animation

isOnGround(), getGroundInfo(), getCharacterVelocity(), setCharacterVelocity(x,y,z), jump(), setCrouched(crouched), isCrouched(), isInWater(), getWaterSubmersion(), getWalkSpeed(), getRunSpeed(), plus the animation API.

getGroundInfo() bundles the common ground-state fields into one call:

FieldValue
objectIdUUID of the object the character is standing on, or nil when it is not on ground it can stand on
objectNameThat object's name, or nil
slopeAngleAngle of the surface it is touching, in degrees
isTooSteepTrue when that surface is steeper than the character can stand on
velocityVelocity of the ground, as {x, y, z}, so a character can ride a moving platform
normalSurface normal as {x, y, z}, or nil when nothing is touched

The same data is also available piecemeal via getGroundNormal(), getGroundVelocity(), getGroundObjectId(), getGroundObject(), getSlopeAngle(), and isSlopeTooSteep(). getUp() returns the character's current up vector (usually {x=0, y=1, z=0}).

getWaterSubmersion() returns how much of the character is under water, from 0 (dry) to 1 (fully submerged); isInWater() is true whenever it is above 0. setCrouched(crouched) returns false when the collider could not change — most often when standing up under something too low — and the character stays as it was, so call it again on a later frame.

getWalkSpeed() and getRunSpeed() return the character's Walk Speed and Run Speed inspector fields in metres per second, or nil on anything but a character. The engine never moves a character by them; they are there for a controller script to read, so speed can be tuned per character without editing the script. jump() does nothing unless the character is on ground it can stand on, except in water, where it swims upward at half the character's Jump Speed.

Rendering, GUI, and audio

getLightColor(), setLightColor(color), getLightIntensity(), setLightIntensity(value), getLightGroundColor(), setLightGroundColor(color), setText(text), setTextColor(r,g,b), setBackgroundColor(r,g,b,a), setFontSize(size), setVisible(visible), setImageAsset(assetId), setSize(width,height), setWidth(width), setHeight(height), setOpacity(opacity), setGuiPosition(x,y), setAnchor(anchor), setOrder(order), playSound(assetId, options), stopSound(soundId?, fadeOutSeconds?), isSoundPlaying(soundId).

GUI width and height are pixels. setGuiPosition is normalized 0–1 inside the container; it is deliberately separate from the 3D setPosition(x,y,z). setAnchor accepts topLeft, top, topRight, left, center, right, bottomLeft, bottom, or bottomRight. setOrder affects children of row/column panels. See Editor basics for the full authoring layout model.

Colour components run from 0 to 1, not 0 to 255. The light calls take a table — setLightColor({r=1, g=0.5, b=0}) — while the GUI calls take separate numbers — setTextColor(1, 0.5, 0). setText keeps the newlines and runs of spaces you write, so "Health: 3\nAmmo: 12" is two lines. setVisible works by setting opacity to 1 or 0, so setVisible(true) replaces a partial setOpacity; setOpacity clamps to 0–1.

playSound(assetId, options)

Plays a sound effect positioned at this object, which follows the object as it moves, and returns a sound id. Every call starts a new, independent instance, so calling it again while a looped sound is still playing layers a second copy rather than restarting the first. Keep the id if you will need to stop that instance. options is optional:

OptionMeaning
volume0–1, relative to the sound-effects volume. Default 1
loopRepeat until stopped. Default false
playbackRateSpeed multiplier; also shifts pitch. Default 1
fadeInSeconds to ramp up from silence
refDistance, rolloffFactor, maxDistanceHow the volume falls off with the listener's distance
distanceModel"linear", "inverse", or "exponential"

stopSound(soundId, fadeOutSeconds?) stops one instance; stopSound() with no id stops every sound playing on this object. isSoundPlaying(soundId) reports whether an instance is still playing. For music and non-positional sound, see Scene scripts, variables, and storage.

Vehicles, constraints, navigation, and animation

These are also on every object API. Their behaviour and parameters are documented on the pages linked below.

AreaMembers
Wheeled vehicles (Vehicles and navigation)setDriverInput(forward,right,brake,handBrake), setForwardInput(forward), setRightInput(right), setBrakeInput(brake), setHandBrakeInput(handBrake), getVehicleForwardSpeed(), getVehicleRPM(), getVehicleGear(), isWheelInContact(index), getWheelContactNormal(index)
Tracked vehiclessetTrackDriverInput(forward,leftRatio,rightRatio,brake), setLeftRatio(ratio), setRightRatio(ratio), plus the shared forward, brake, speed, RPM, gear and wheel-contact calls above
Hinge and slider motorssetConstraintMotorState("off"|"velocity"|"position"), setConstraintTargetVelocity(value), setConstraintTargetPosition(value), getConstraintCurrentValue()
Navigation agentsregisterForPathfinding(speed?), moveTo(x,y,z), followPath(waypoints), stopMovement(), isMoving(), getPathToTarget(x,y,z), getRandomNavPoint(radius)
Generated tracksgetTrackPath() — the centreline of a generated track mesh, or nil
Animation (Animation playback API)playAnimation(name,loop?,blendTime?), stopAnimation(name?,fadeOutTime?), setAnimationSpeed(speed,name?), getAnimationNames(), isAnimationPlaying(name?), enableAutoAnimation(config), disableAutoAnimation(), updateAutoAnimation(), getAnimationState(), preloadAnimations(names?), isAnimationReady(name), areAnimationsLoaded(), seekAnimation(name,time), getAnimationTime(name?), getAnimationDuration(name), getAnimationDiagnostics()
Bones and sockets (Animation playback API)getBoneNames(), getBoneWorldPosition(name), getBoneWorldRotation(name), attachToBone(object,boneName,offset?), detachFromBone(object?), getBoneAttachments(). For characters and meshes with a skeleton; presentation-only in a multiplayer room