log in
substrata logo

Luau Scripting in Substrata

You can create scripts in Substrata using the Luau programming language, which is Roblox's version of the Lua programming language.

Creating an object script

To create a script for your object, you will need to use the native Substrata client (i.e. not the web client).

Currently (as of 8th July 2024) you will need the latest test Substrata client for Luau scripting, available on the Substrata discord channel.

In the native client, double click on the object you want to script. Then click on the 'Edit' button in the script section:

This will pop up the script editor window (shown here with an existing script):

To create a Luau script, you must start your script with

--lua

This tells Substrata to expect a Luau script, as opposed to a Winter or XML script.

To apply changes in your script to the object, just close the script editor window.

Script execution environment

Your script will run on both the client and the server. For example, that means that every client that connects to substrata.info will run your script, and your script will also run on the substrata.info server.

Calling some functions or assigning some attributes will generally only have any immediate effect on either the client or server, depending on what attribute or function it is.

Attribute updates that affect physics behaviour will generally run client-side, because physics in Substrata is computed client side.

Other attribute updates will only run on the server, and then the updated object information will be immediately and automatically sent to all connected clients.

Debugging your script

You can print output from your script with the print function, for example
print("hello")

Debugging output, including print output is available in the log window (Tools > Show Log).

If an error occurs while running your script, the error while be shown in the log window (or the server log) as well, and the script will be stopped. To restart your script you will need to edit it, thereby reloading the script.

Here is an example of a message printed from print():

As well as the actual message, the UID of the script's object is also printed.

Since scripts run on the server as well, you can view debugging output from the server at https://substrata.info/script_log.

This page will show the last 1000 or so messages and errors, from all scripts created with your user account. You must be logged in to view this page.

Event Listeners

These functions can be defined in your script to listen for certain events. If you define one of these functions, they will be called when the event triggers for the object whose script it is.

You can also listen for these events on other objects with addEventListener.

onUserTouchedObject(avatar : Avatar, object : Object)
avatar : Avatar The avatar that touched the object.
object : Object The object that was touched.
Called when a user's avatar touches an object. If an avatar continues to touch an object, for example the avatar is standing on an object, this event will continue to be fired at a regular period of something like 0.5 s.
onUserUsedObject(avatar : Avatar, object : Object)
avatar : Avatar The avatar of the player that used the object.
object : Object The object that was used.
Called when a user uses an object. Currently users use objects by moving the mouse cursor over an object and pressing the E key.
onUserMovedNearToObject(avatar : Avatar, object : Object)
avatar : Avatar The avatar that moved near the object.
object : Object The object that the player moved near to.
Called when a user avatar moves near (within a certain distance) to an object. The distance is currently 20 metres.
onUserMovedAwayFromObject(avatar : Avatar, object : Object)
avatar : Avatar The avatar that moved near the object.
object : Object The object that the player moved away from.
Called when a user avatar moves away from (further than a certain distance) an object. The distance is currently 20 metres.
onUserEnteredParcel(avatar : Avatar, object : Object, parcel : Parcel)
avatar : Avatar The avatar that entered the parcel.
object : Object The object in the parcel.
parcel : Parcel The parcel.
Called when a user avatar enters the land parcel that the script object is inside.
onUserExitedParcel(avatar : Avatar, object : Object, parcel : Parcel)
avatar : Avatar The avatar that exited the parcel.
object : Object The object in the parcel.
parcel : Parcel The parcel.
Called when a user avatar exits the land parcel that the script object is inside.
onUserEnteredVehicle(avatar : Avatar, vehicle_ob : Object)
avatar : Avatar The avatar that entered the vehicle.
vehicle_ob : Object The vehicle object
Called when a user avatar enters (or sits on in the case of a bike) a vehicle.
onUserExitedVehicle(avatar : Avatar, vehicle_ob : Object)
avatar : Avatar The avatar that exited the vehicle.
vehicle_ob : Object The vehicle object
Called when a user avatar exits a vehicle.

Global functions

getObjectForUID(uid : number) : Object
uid : number The UID (unique identifier) of the object.
Returns the object that has the given UID. Throws exception if no such object exists.
showMessageToUser(msg : String, av : Avatar)
msg : StringThe message to send to the avatar's user.
av : AvatarThe avatar of the user to send the message to.
Shows a string message on the screen of the user of the given avatar. The message will be displayed for a few seconds.
createTimer(onTimerEvent : function, interval_time_s : number, repeating : bool) : number
onTimerEvent : functionThe timer event handler to be called when the timer fires.
interval_time_s : numberThe time interval, in seconds, until the timer fires.
repeating : boolShould the timer keep firing every interval_time_s seconds, or should it just fire once.

Creates a timer, when the timer interval is complete, onTimerEvent is called. onTimerEvent should be a function with the following signature:

onTimerEvent(object : Object)

Throws an exception if more than 4 timer events are created without being destroyed.

Returns a timer handle that can be passed to destroyTimer.

destroyTimer(timer_handle : number) : number
timer_handle : number)A timer handle that was previously returned from createTimer.

Destroys a timer. Has no effect if the timer has already completed (for non-repeating timers), or has already been destroyed.

addEventListener(event_name : string, ob_uid : number, handler : Function) : number
event_name : stringThe name of the event. Valid event names are "onUserUsedObject", "onUserTouchedObject", "onUserMovedNearToObject", "onUserMovedAwayFromObject", "onUserEnteredParcel", "onUserExitedParcel", "onUserEnteredVehicle", "onUserExitedVehicle"
ob_uid : numberUID of the object that the event will be listened on. For example, for onUserTouchedObject, this is the UID of the object that will have to be touched to trigger the touch event.
handler : FunctionA function that will be called when the event occurs. You can use your existing event handling functions such as onUserTouchedObject.

Adds a listener for an event on another object.

For example, this code listens for the touch event on the object with UID 583:

addEventListener("onUserTouchedObject", 583, onUserTouchedObject)

Classes

Object

Attributes:

model_url : stringThe Substrata URL of the 3d model for the object
pos : Vec3dThe position of the object
axis : Vec3fThe axis of rotation of the object
angle : numberThe counter-clockwise angle of rotation of the object around axis, in radians.
scale : Vec3fThe scale factor in x, y, and z directions.
collidable : booleanIs this object physically collidable with? If false, avatars and physics objects will pass through it without colliding with it.
dynamic : booleanIs this object a dynamic physics object?
sensor : booleanSensor objects still emit touch events, but objects can pass through them.
content : stringContent field. For Text and Hypercard object types, this is the text that is displayed.
video_autoplay : booleanIf true, video is automatically played for Video object types.
video_loop : booleanIf true, video automatically loops back to the beginning and starts playing again. If false, just plays once.
video_muted : booleanIf true, video is muted when playing.
mass : numberObject mass in kg for physics engine. Only relevant when dynamic is true.
friction : numberObject friction. Friction of the physics body (dimensionless number, usually between 0 and 1, 0 = no friction, 1 = friction force equals force that presses the two bodies together).
restitution : numberRestitution of body (dimensionless number, usually between 0 and 1, 0 = completely inelastic collision response, 1 = completely elastic collision response).
centre_of_mass_offset_os : Vec3fOffset of centre of mass of object, in object coordinates. Can be used to e.g. give a vehicle a lower centre of mass than by default.
audio_source_url : string Substrata URL of the audio source for this object. Can be a mp3 or wav URL
audio_volume : number Volume factor for audio source. Default is 1.

Methods:

getNumMaterials() : number Returns the number of materials of the object.
getMaterial(mat_index : number) : Material Returns the material of the object with the given index.
Material

Attributes:

colour : Vec3fThe reflective colour of the object. Vec3f(1,1,1) is a white object. Non-linear sRGB.
colour_texture_url : stringThe URL of the colour texture.
emission_rgb : Vec3fThe colour of the emitted light from the object. Non-linear sRGB.
emission_texture_url : stringThe URL of the emission texture.
normal_map_url : stringThe URL of the normal map.
roughness_val : numberThe roughness of the material. 0 = perfectly smooth, 1 = very rough.
roughness_texture_url : string URL of the metallic-roughness texture. The metallic value is read from the blue channel, the roughness value is read from the green channel.
metallic_fraction_val : numberThe metallic fraction of the material. 0 = not metal. 1 = completely metal.
opacity_val : number How opaque (non-transparent) the material is. Currently this value just determines if the transparent shader is used - it's used in the case where opacity_val < 1.
tex_matrix : Matrix2fThe texture coordinate matrix. Default value is {1, 0, 0, 1}.
emission_lum_flux_or_lum : number For the spotlight object materials, this is luminous flux. For generic model materials, luminance.
hologram : booleanIs this a hologram material? Hologram materials don't block light, but just emit it.
double_sided : booleanShould we render back-faces of the triangles with this material? Rendering is faster when not rendering back-faces, but if your object triangle mesh is not fully sealed, it may result in visible gaps.
Avatar

Attributes:

pos : Vec3dThe position of the avatar. This is approximately the position of the eyes of the avatar for a standard avatar height, and will be 1.67 m above the surface the avatar is standing on.
name : stringThe name of the user who is controlling the avatar.
linear_velocity : Vec3fThe linear velocity of the avatar, in metres per second.
vehicle_inside : ObjectThe vehicle object the avatar is inside-of/riding, or nil if none.

Examples

Here is a very simple script that shows a message to a user when they touch an object:

--lua

function onUserTouchedObject(av : Avatar, ob : Object)
	showMessageToUser("Hi " .. av.name .. ", you touched the red cube!", av)
end

Which has this result in-world when you walk into the red cube:



< Home