ScriptAPI reference

API Reference

A compact reference for the public and protected ScriptAPI surface available to scripts derived from ScriptBase.

ScriptAPI / Reference

Base Class

Scripts inherit from ScriptBase. Most callable helpers are protected, so they are used from inside your script class.

public virtual string ScriptName

Defaults to the class name. Used in logs and script event sender info.

public virtual string ScriptDescription

Optional description text for tools that surface script metadata.

protected GameObject gameObject

The object that owns this script instance.

protected Transform transform

The transform of the owning object.

protected Dictionary<string, GameObject> slots

Named object slots assigned by the runtime when available.

protected bool IsRuntimeMode

True when running in the runtime world rather than editor bootstrap mode.

protected virtual bool SyncTransformStateByDefault

Defaults to true. Override to disable automatic transform state sync at startup.

protected bool TransformStateSyncEnabled

Runtime switch for automatic transform state synchronization.

ScriptAPI / Types

Core Types

SceneObject

Serialized scene reference with ObjectId, Id, IsNull, Exists, gameObject, transform, GetComponent<T>, GetComponents<T>, child lookup, and parent lookup helpers.

Sitpoint : SceneObject

Scene object reference with SitpointId resolution for sitpoint components and settings.

AvatarClone : SceneObject

Server-spawned avatar clone reference with SourceUsername, DisplayName, CloneId, and helper methods for destroy, transform, emote, sitpoint, and stand commands.

SceneLight : SceneObject

Scene object reference with a light property that resolves a Light on the object or in children.

InventoryItem

Inventory reference with InventoryId, Kind, Id, IsNull, IsEmpty, Exists, and normalization helpers.

TextureItem : InventoryItem

A texture inventory item (a picture, such as a backpack icon). Kind is Texture. Inspector fields of this type take only textures.

Emote : InventoryItem

An emote. In the scene editor, a public Emote field (or List<Emote>) shows a dropdown of the scene author's own emotes, uploaded or bought in the Avatar Editor; pick one there. Emotes are not typed in by id. Kind is Emote; EmoteId mirrors InventoryId; check IsNull before using it.

UserInfo

A player: Username, ObjectName, ObjectId, PlayerObject, and where they are and how they move, updated about 10 times a second: Position (their feet), Forward (the way the body faces, level), Yaw (that way in degrees, 0 = +z, 90 = +x), Velocity, Speed (over the ground, metres a second), IsRunning, IsCrouching, IsJumping (in the air), IsSeated, IsGrounded, and PoseTime (when it was last updated, 0 = not yet). GetUsers() returns every player in the world.

SitpointInfo

Sit or stand snapshot with Username, SitpointId, ObjectName, ObjectId, Sitpoint, and PlayerObject.

InteractionInfo

Interactable callback data with Username, ObjectName, ObjectId, ScriptNetworkId, InteractionType, and Target.

ChatCommandInfo

Chat command callback data with Username, UserId, player object fields, room ids, FullText, Command, ParameterText, and split Args.

SitpointInputInfo

Sitpoint input data with MoveX, MoveY, LookX, LookY, Jump, Sprint, Username, SitpointId, and Sitpoint.

TriggerInfo

Trigger callback data with EventType, IsPlayer, username, object name/id, trigger object id, OtherObject, and TriggerObject.

CollisionInfo

Collision callback data with IsPlayer, username, collided object name/id, collision object id, OtherObject, and CollisionObject.

BackpackSelection

A player's pick in the world's backpack: Username, UserId, Index, Item, DisplayName, PlayerPosition, Forward, Yaw, SpawnPosition(distance, height). See Backpack.

TriggerEventType

Enum values: Enter, Exit.

CharacterPhysicsMode

Enum values: Standing, Falling, Sleep.

CharacterImpactPart

Enum values: All, Core, LeftArm, RightArm, LeftLeg, RightLeg, Head.

CharacterRagdollSettings

Fields: Strength (0.12), GravityScale (1), Damping (0.02), Friction (0.6), Bounciness (0.08). Used with SetCharacterRagdollSettings.

Script Event Data

ScriptEventData.Set(name, value)

Adds or replaces a named event value and returns the same data object.

Has(name)

Checks whether the data contains a key. Keys are case-insensitive.

Get(name, defaultValue)

Gets a raw value or default.

Get<T>(name, defaultValue)

Gets and converts a value to the requested type when possible.

TryGet<T>(name, out value)

Gets a typed value and reports whether the key existed.

GetString / GetInt / GetFloat / GetDouble / GetBool

Convenience typed accessors for common event payload values.

Copy()

Returns a shallow copy of the event data.

ScriptEventData.From(params object[] pairs)

Builds event data from alternating name/value pairs.

Script Event Info

ScriptEventInfo

Contains EventName, Group, FullName, SenderScriptName, SenderObjectId, SenderObject, and Data.

ScriptAPI / Lifecycle

Lifecycle And Mode Helpers

public virtual void Start()

Called when the script starts. Call base.Start() before custom setup.

public virtual void OnDestroy()

Called as the script is destroyed. Use for cleanup such as despawning objects or stopping state.

public virtual void OnEnable()

Called when the script is enabled.

public virtual void OnDisable()

Called when the script is disabled.

public virtual void OnEvent(string eventName, object data)

Generic event hook for runtime-dispatched events.

SetTransformStateSync / DisableTransformStateSync / EnableTransformStateSync

Control automatic transform state synchronization for this script.

protected void SetStateApplyPhase(int phase)

Advanced control for when state is applied. The base uses the update phase.

ScriptAPI / Users And Events

Users

OnUserJoin(UserInfo user)

Override to run logic when a user enters.

OnUserPart(UserInfo user)

Override to run logic when a user leaves.

OnUserSit(SitpointInfo sitpoint)

Override to run logic when a user sits.

OnUserStand(SitpointInfo sitpoint)

Override to run logic when a user stands.

GetUsers()

Returns a list of active user snapshots.

GetUsernames()

Returns the active usernames known by the runtime.

ForEachUser(Action<UserInfo> callback)

Runs the callback for each active user and logs callback errors.

Script Events

Script events are local runtime messages. Use eventName for global listeners, or eventName@group for a grouped event key. Posting name@group notifies listeners of name and listeners of name@group.

CreateScriptEventData(params object[] nameValuePairs)

Builds ScriptEventData from alternating name and value arguments.

NewScriptEventData(params object[] nameValuePairs)

Alias for CreateScriptEventData.

ListenForScriptEvent(string eventName, Action<ScriptEventInfo> callback)

Registers a callback with full sender and payload details.

ListenForScriptEvent(string eventName, Action<ScriptEventData> callback)

Registers a callback that receives only the payload data.

StopListeningForScriptEvent(string eventName = null)

Stops this script's event subscriptions. With no name, removes all subscriptions owned by this script.

PostScriptEvent(string eventName)

Posts an event with empty data.

PostScriptEvent(string eventName, ScriptEventData data)

Posts an event with structured data.

PostScriptEvent(string eventName, params object[] nameValuePairs)

Posts an event from alternating name and value arguments.

ScriptAPI / Dialogs

Dialogs

ShowDialog queues a modal dialog client command. Provide targetUsername for a single user, or leave it empty for everyone. The dialog opens in the middle of the player’s screen in the platform’s own UI style (desktop, mobile and VR), with the icon (Info, Warning, Error, Success) and the buttons you choose (OK, OKCancel, YesNo, YesNoCancel); the button pressed comes back to onResult. Closing it counts as Cancel (or No when there is no Cancel).

ShowDialog(title, message, targetUsername = null, icon = Info, buttons = OK)

Shows a simple dialog with optional target, icon, and button set.

ShowDialog(title, message, ModalDialogIcon icon)

Shows an icon dialog with an OK button.

ShowDialog(title, message, icon, buttons, targetUsername = null)

Shows a dialog with explicit icon and button configuration.

ShowDialog(title, message, icon, buttons, onResult, targetUsername = null)

Shows a dialog and invokes the callback with ModalDialogResult.

ShowDialog(title, message, icon, onResult, targetUsername = null)

Shows an OK dialog and invokes the callback.

Dialog Enums

ModalDialogIcon

None, Info, Warning, Error, Success.

ModalDialogButtons

OK, OKCancel, YesNo, YesNoCancel, Custom.

ModalDialogResult

None, OK, Cancel, Yes, No, Custom1, Custom2, Custom3.

ScriptAPI / Store Offers And Links

Store Offers And Links

OfferStoreItem sells one store item right in the world, without the store or the inventory: the player gets a buy panel in the platform’s own UI (desktop, mobile and VR) with the item’s picture, name, type, creator, price and their balance, and Buy / Not now. Buying is a normal store purchase (the player’s coins go to the seller, it shows in both people’s transactions) and the item goes straight into the player’s inventory: Purchased Items in the Scene Editor, or the Avatar Editor inventory. The player then sees that it was delivered, or why the purchase did not go through. A player who already has it is told so; one who bought it before and removed it from their inventory can have it put back for free. OfferLink asks a player whether to open a web page in a new browser tab; they see the address first and nothing opens unless they choose Open Link. See the StoreOffer and LinkOffer examples.

OfferStoreItem(string username, StoreKind store, string item, Action<StoreOfferResult> onResult = null, string message = null)

Shows the buy panel to one player, or to everybody in the world now with "*". store: the store the item is sold in, StoreKind.Scene (Scene Editor store) or StoreKind.Avatar (Avatar Editor store). item: the item’s store id, or its store link (https://…/store/scene/ITEM_ID). A private item can only be offered with its private link (Edit Store Details → Copy Private Link), which carries its key; everybody you offer it to can then buy it. message: an optional line shown above the item. onResult runs here once for each player who answers. A wrong id or link shows the player nothing; onResult gets Failed with the reason. There is also OfferStoreItem(UserInfo user, …).

OfferStoreItemToEveryone(StoreKind store, string item, Action<StoreOfferResult> onResult = null, string message = null)

The same, for every player in the world now.

StoreOfferResult

Username, ItemId, Store, Status, Message (why it failed), Purchased (bought just now) and Owned (the player has the item now: bought now, put back, or already had it). Purchases are confirmed with the store before onResult is called, so a reward given for Purchased is safe.

StoreOfferStatus

Purchased, Redelivered (bought before, put back in their inventory for free), AlreadyOwned (already in their inventory, or it is their own listing), Declined (Not now, or closed), Failed (not enough coins, not for sale, a private item without its link…).

StoreKind

Scene (objects, scripts, materials, sounds… from the Scene Editor store), Avatar (bodies, clothes, hair, accessories and emotes from the Avatar Editor store).

OfferLink(string username, string url, string message = null, string title = null, Action<bool> onAnswer = null)

Asks one player (or everybody with "*") whether to open url in a new browser tab. The question shows the site’s address and the full link; Open Link opens it, Cancel does nothing. Only full http:// and https:// addresses. In VR the page opens in the browser, outside VR (the player is told first). onAnswer runs here with true when they opened it. There is also OfferLink(UserInfo user, …).

ScriptAPI / Notifications

Notifications

A toast is a business-card sized notification at the top right of the screen, the same card players get for a new direct message or friend request. It works on desktop, mobile and in VR: the icon on the left, the title and description beside it. It shows for 10 seconds (the player can close it sooner); text that does not fit the card is cut and ends with ... A new toast from the same script replaces the one it showed before.

ShowToast(string username, TextureItem icon, string title, string description = null)

Shows one player (or everybody in the world now with "*") a toast. icon: a TextureItem field (drop a texture from your inventory on it in the Inspector), or null for none. Give a title, a description, or both (up to 200 and 1000 characters). Also ShowToast(UserInfo user, ...).

ShowToastToEveryone(TextureItem icon, string title, string description = null)

The same toast for everybody in the world right now.

ScriptAPI / Weather And Time Of Day

Weather And Time Of Day

For scenes whose Sky is Dynamic sky (Scene Editor → Windows → Scene Settings). The change is for everybody in this world at once, timed by the server so everyone sees the same, and players who join later see it too. It lasts until the script changes it again or the world's scene changes; the scene's own settings are not changed.

SetWeather(WeatherPreset preset, float blendSeconds = 10)

Blends the sky to a built-in weather over blendSeconds (0 to 600): clouds, rain or snow, lightning, rainbow and mist. WeatherPreset.Scene goes back to the scene's own weather (like ClearWeather).

ClearWeather(float blendSeconds = 10)

Back to the scene's own weather (with its weather cycling, if it has it).

WeatherPreset

Scene, Clear, PartlyCloudy, Cloudy, Overcast, Rain, Thunderstorm, Snow, Hail, DustStorm, Foggy. A public WeatherPreset field is a dropdown in the script's Inspector.

SetTimeOfDay(float hour)

The sky's clock jumps to hour (0 to 24, e.g. 18.5 = 6:30 pm). When the scene's time of day moves, it goes on from there at the scene's speed.

ResetTimeOfDay()

Back to the scene's own clock.

TriggerLightning()

A lightning strike now, with its flash and thunder, whatever the weather.

SetWind(float direction, float strength, float blendSeconds = 3)

Wind for the whole world. Every material in the scene with Foliage Wind turned on — trees, bushes, grass, terrain trees, and sails, flags or banners you gave Foliage Wind in the Material Editor — sways toward direction (degrees; the same angle as SetWaveDirection, so you can turn the waves with it). strength multiplies each material’s own sway: 0 still, 1 as authored, 2 twice as strong, up to 4. It eases over blendSeconds, the same for everyone, and players who join later get the current wind. Materials without Foliage Wind are not affected.

SetWind(Vector3 direction, float strength, float blendSeconds = 3)

The same with a direction vector in script space (only X and Z are used): SetWind(Vector3.right, 2f).

ClearWind(float blendSeconds = 3)

Eases every foliage material back to its own Foliage Wind settings.

ScriptAPI / Transforms

Coroutines And Self Transform Helpers

StartCoroutine(IEnumerator routine)

Starts a coroutine using the script runtime runner.

yield return new WaitForSeconds(seconds)

Waits inside a coroutine. For example, yield return new WaitForSeconds(0.5f); waits half a second.

StopCoroutine(Coroutine routine)

Stops a coroutine started through the runtime runner.

StopAllCoroutines()

Stops all coroutines on the runner.

Move(Vector3 offset)

Adds the offset to this script object's world position.

Rotate(Vector3 eulerAngles)

Rotates this script object in world space.

RotateAround(Vector3 axis, float angle)

Rotates this script object around a world-space axis.

SetPosition / SetRotation / SetScale

Sets this script object's world position, world rotation, or local scale. Inside a group the world values are kept (the object is placed where you say in the world, whatever its parent's place).

GetPosition / GetRotation / GetScale

Reads this script object's world position, world euler rotation, or local scale.

Coordinates and the Hierarchy.

Scripts use Unity-style coordinates: y is up and Vector3.forward is +z. The scene editor's Inspector shows Position and Rotation in the same numbers (local to the object's parent), so a value you read in the Inspector is what transform.localPosition and transform.localEulerAngles return. The editor's Hierarchy is known to scripts too: transform.parent is the object's parent there (null at the top), and the Local… calls are relative to that parent (for a top-level object, local and world are the same).

Transform

position / eulerAngles / rotation / lossyScale

World values (setting position or eulerAngles moves the object for everybody).

localPosition / localEulerAngles / localRotation / localScale

Relative to the parent in the Hierarchy, as the Inspector shows them. Setting one moves the object for everybody.

parent / root

The parent in the Hierarchy (null for a top-level object) and the top-level object above this one.

childCount / GetChild(int index) / GetSiblingIndex()

The object's direct children, in Hierarchy order.

Find(string name)

A direct child by name, or a path of names such as "Pins/Pin 3". null when there is none.

TransformPoint / InverseTransformPoint

A point from this object's own space (as its children's localPosition) to world space, and back.

TransformDirection / InverseTransformDirection

A direction from this object's space to world space, and back (rotation only).

IsChildOf(Transform other)

True when other is this object or one of its ancestors.

Vector3 And Quaternion

Vector3.Angle(from, to) / Vector3.SignedAngle(from, to, axis)

The angle between two directions in degrees (0 to 180), or signed around an axis.

a == b / a != b

Two vectors are equal when they are closer than 0.00001 (as in Unity), so rounding does not make equal positions differ.

Vector3.Scale / Min / Max / MoveTowards / ClampMagnitude / Project / Reflect

As in Unity.

Quaternion.Inverse / Quaternion.Angle / Quaternion.Dot

As in Unity.

ScriptAPI / Object Commands

Self Object Client Commands

These helpers queue commands for the object that owns the script. Most include optional targetUsername, component index, and includeChildren parameters.

SetMaterialFloat(int materialIndex, string propertyName, float value, string targetUsername = null, int rendererIndex = 0, bool includeChildren = true)

Sets a material float property.

SetMaterialColor(int materialIndex, string propertyName, Color value, string targetUsername = null, int rendererIndex = 0, bool includeChildren = true)

Sets a material color property.

SetMaterialKeyword(int materialIndex, string keyword, bool enabled, string targetUsername = null, int rendererIndex = 0, bool includeChildren = true)

Enables or disables a material keyword.

SetRendererVisible(bool visible, string targetUsername = null, int rendererIndex = 0, bool includeChildren = true)

Sets renderer visibility.

SetRendererVisibleForUser(string targetUsername, bool visible, int rendererIndex = 0, bool includeChildren = true)

Changes this object's renderer visibility only for the named user. The private state is not replayed after the user leaves and rejoins. An empty username is rejected rather than becoming a broadcast.

SetObjectVisible(bool visible, string targetUsername = null) / SetObjectVisible(SceneObject target, bool visible, string targetUsername = null)

Shows or hides a whole object (its meshes, particles, lights and everything inside it) for everybody, or only for the named player. Players who join later see it the same way. It does not change the object's collider.

SetLightEnabled / SetLightIntensity / SetLightColor

Controls a light component by index.

SetAudioVolume / SetAudioPitch / PlayAudio / StopAudio

Controls an audio source by index.

PlayAudioFrom(float startSeconds, string targetUsername = null, int audioSourceIndex = 0, bool includeChildren = true)

Plays this object's audio source starting startSeconds into its clip, restarting it there if it is already playing. Use it to put a player who joins late in step with a track everyone else is hearing: send it to that player's username with how far the track has played. If the clip is still loading for that player, it starts once loaded, at the matching later point. Past the end of a non-looping clip nothing plays; a looping clip wraps around. A normal PlayAudio afterwards starts from the beginning again.

PlayAudioFrom(SceneObject target, float startSeconds, string targetUsername = null, int audioSourceIndex = 0, bool includeChildren = true)

Same as above, for the audio source on another object.

PlayAudioLoop(string targetUsername = null, int audioSourceIndex = 0, bool includeChildren = true)

Plays this object's audio source over and over, whatever its own Loop setting, until StopAudio. Players who join while it is playing hear it too. A later PlayAudio or PlayAudioFrom uses the source's own Loop setting again. Good for engines, motors and machinery that run for a while.

PlayAudioLoop(SceneObject target, string targetUsername = null, int audioSourceIndex = 0, bool includeChildren = true)

Same as above, for the audio source on another object.

SetAnimatorTrigger / SetAnimatorBool

Controls animator parameters by index.

EnableAnimatorSync / DisableAnimatorSync

Periodically sends current Animator state to clients.

EnableLegacyAnimationSync / DisableLegacyAnimationSync

Periodically sends current legacy Animation state to clients.

SyncAnimatorState / SyncLegacyAnimationState

Sends current animation state once.

SetLocalPosition / SetLocalRotation / SetLocalScale

Moves, turns or scales this object relative to its parent in the Hierarchy (the values the Inspector shows).

LerpLocalPosition(Vector3 targetPosition, float lerpTime, string targetUsername = null)

Moves this object smoothly from its current position to the target (relative to its parent) over lerpTime seconds. With a collider, it pushes and carries what it meets on the way.

LerpLocalRotation(Vector3 targetEulerAngles, float lerpTime, string targetUsername = null)

Spherically interpolates this object's rotation to the target euler angles.

LerpLocalScale(Vector3 targetScale, float lerpTime, string targetUsername = null)

Interpolates this object's local scale to the target.

LerpLocalTransform(Vector3 targetPosition, Vector3 targetEulerAngles, Vector3 targetScale, float lerpTime, string targetUsername = null)

Interpolates position, rotation, and scale together using one command.

SetColliderEnabled / SetRigidbodyKinematic

Controls collider or rigidbody state by index. SetRigidbodyKinematic(true) (or rb.isKinematic = true) freezes a simulated object where it is, with no speed or gravity, still solid; false lets it fall and roll again.

Target Object Client Commands

These overloads take SceneObject targetObject first and queue the command for that object id.

SetMaterialFloat(SceneObject targetObject, int materialIndex, string propertyName, float value, string targetUsername = null, int rendererIndex = 0, bool includeChildren = true)

Sets a target object's material float property.

SetMaterialColor(SceneObject targetObject, int materialIndex, string propertyName, Color value, string targetUsername = null, int rendererIndex = 0, bool includeChildren = true)

Sets a target object's material color property.

SetObjectGlow(SceneObject targetObject, Color color, float strength, float fadeSeconds = 0f, string targetUsername = null)

Makes the whole object glow, children included: every material of every mesh gets the glow color at strength (2 is a clear glow), faded smoothly over fadeSeconds. Only this object glows, even when its materials are shared with others. Players who join later see it glowing.

ClearObjectGlow(SceneObject targetObject, float fadeSeconds = 0f, string targetUsername = null)

Fades the object back to its own materials' glow (as authored).

SetMaterialKeyword(SceneObject targetObject, int materialIndex, string keyword, bool enabled, string targetUsername = null, int rendererIndex = 0, bool includeChildren = true)

Enables or disables a target object's material keyword.

SetRendererVisible(SceneObject targetObject, bool visible, string targetUsername = null, int rendererIndex = 0, bool includeChildren = true)

Sets target renderer visibility.

SetRendererVisibleForUser(string targetUsername, SceneObject targetObject, bool visible, int rendererIndex = 0, bool includeChildren = true)

Changes a target object's renderer visibility only for the named user. The setting is temporary and a later public SetRendererVisible command replaces it normally.

SetLightEnabled / SetLightIntensity / SetLightColor

Controls a target light by index.

SetAudioVolume / SetAudioPitch / PlayAudio / StopAudio

Controls a target audio source by index.

SetAudioPitchByMotion(audioObject, minPitch, maxPitch, fullSpeed, volumeAtRest, motionObject, enabled)

Pitches and fades an audio source by how fast it moves in the world, measured by every viewer each frame: minPitch when still, maxPitch at fullSpeed metres per second. Works for parts moved by a model’s animation (no Rigidbody needed); a looping sound starts by itself when it first moves. motionObject measures another object instead; enabled: false puts the sound back to normal.

SetParticleEmission(particleObject, rateMultiplier, targetUsername)

Scales how many particles the Particle System(s) on an object or under it emit: 0 none, 1 as set in its Inspector, 2 twice as many (up to 20). Each viewer eases to the new rate, and players who join later get it too. Example: exhaust smoke that grows with a vehicle’s speed.

SetParticlesPlaying(particleObject, playing, targetUsername = null, restart = false)

Starts or stops emitting (particles already out finish their life as usual). Works on systems with Play On Awake off, so a script can keep them off until needed. Starting a stopped system starts it over, like Unity's Play() after Stop(): Start Delay, bursts and Duration count from the beginning again, so a burst fires once and a system that does not loop plays again. restart: true starts it over even while it is playing (fire a one-shot burst again without stopping it first).

SetAnimatorTrigger / SetAnimatorBool

Controls a target animator by index.

SyncAnimatorState / SyncLegacyAnimationState

Sends a target object's current animation state once.

SetLocalPosition / SetLocalRotation / SetLocalScale

Moves, turns or scales a target object relative to its parent in the Hierarchy (the values the Inspector shows).

LerpLocalPosition(SceneObject targetObject, Vector3 targetPosition, float lerpTime, string targetUsername = null)

Moves a target object smoothly from its current position to the destination.

LerpLocalRotation(SceneObject targetObject, Vector3 targetEulerAngles, float lerpTime, string targetUsername = null)

Spherically interpolates a target object's rotation.

LerpLocalScale(SceneObject targetObject, Vector3 targetScale, float lerpTime, string targetUsername = null)

Interpolates a target object's local scale.

LerpLocalTransform(SceneObject targetObject, Vector3 targetPosition, Vector3 targetEulerAngles, Vector3 targetScale, float lerpTime, string targetUsername = null)

Interpolates all three transform channels on a target object using one command.

SetColliderEnabled / SetRigidbodyKinematic

Controls a target collider or rigidbody by index.

Timed transform behavior.

Leave targetUsername empty (or use *, all, or everyone) to run the interpolation on every client and on the shared instance-server object. A specific username runs only on that user's client and does not move the server copy. A newer lerp replaces the affected transform channel; an immediate SetLocalPosition, SetLocalRotation, or SetLocalScale cancels the matching lerp. For continuous motion, send future waypoints at a modest interval and make lerpTime equal to or slightly longer than that interval.

Position and rotation lerps of an object with a collider run on the server too, at their speed: moving equipment (a sweep bar, a lift, a door) pushes loose objects and carries what rests on it instead of passing through. Its children in the Hierarchy move with it. On a simulated (non-kinematic) body a lerp places it at the end; freeze it with SetRigidbodyKinematic(true) (before or after the lerp, in the same tick is fine) to move it along the way.

ScriptAPI / Material Emission

Material Emission At Runtime

SV Standard Lit materials expose _EmissionIntensity for glow strength and _EmissionColor for glow color. Set intensity above 0 to make the material emissive, and set intensity back to 0 with a black emission color to turn it off.

SetMaterialFloat(target, materialIndex, "_EmissionIntensity", level)

Sets the emission level. 0 is off; values like 1 to 3 are useful for visible glow.

SetMaterialColor(target, materialIndex, "_EmissionColor", color)

Sets the emitted color. Use a bright color while lit and Color.black when resetting.

_SVEmissionUseMap

Leave this off for normal SV Standard Lit glow through the base texture. Turn it on only when the material has a dedicated emission mask map.

SetMaterialFloat(target, 0, "_EmissionIntensity", 1.5f);
SetMaterialColor(target, 0, "_EmissionColor", Color.white);

SetMaterialFloat(target, 0, "_EmissionIntensity", 0f);
SetMaterialColor(target, 0, "_EmissionColor", Color.black);

ScriptAPI / Spritesheets

Spritesheet Material Helpers

Spritesheet helpers are target-object commands. They expect materials with the spritesheet properties used by the runtime animator.

SetSpritesheetFrame(SceneObject targetObject, int frame, string targetUsername = null, int rendererIndex = 0, int materialIndex = 0, bool includeChildren = true)

Sets the target material frame.

SetSpritesheetPlayback(SceneObject targetObject, bool animate, float autoAdvanceSeconds, bool loop, string targetUsername = null, int rendererIndex = 0, int materialIndex = 0, bool includeChildren = true)

Sets animation, frame advance interval, and looping.

SetSpritesheetMaterial(SceneObject targetObject, int? frame = null, bool? animate = null, float? autoAdvanceSeconds = null, bool? loop = null, bool? affectMaps = null, string targetUsername = null, int rendererIndex = 0, int materialIndex = 0, bool includeChildren = true)

Applies any combination of frame, playback, loop, and map-affecting settings. Commands are persistent for late-joining clients.

Without a targetUsername the change goes to the instance server's shared state of that material: every player sees the same frame at the same moment, including players who join later, and the material's own settings (Start Frame, Animate, Seconds / Frame, Loop) are only the starting point. Commands sent before anybody has loaded the scene are kept and applied when the first player arrives. With a targetUsername the change is shown to that user only and is not kept after they leave.

ScriptAPI / Animated Meshes

Animated Meshes

A model with animation takes has a legacy Animation component (and an Animator for the trigger / bool helpers). Its default take, whether it plays when the world opens, its speed and repeat mode are set in the scene editor's Inspector. Script calls change the instance server's shared state of the model, so every player sees the same take at the same time, also players who join later.

Animation anim = target.GetComponentInChildren<Animation>(true)

The model's animation. foreach (AnimationState s in anim) lists the takes with s.name and s.length (known once a player has loaded the scene).

anim.Play() / anim.Play("take") / anim.CrossFade("take")

Plays a take (empty = the default take). Playing the take that is already set continues from its current time; another take starts at 0.

anim.Stop() / anim.Stop("take") / anim.Rewind()

Stops (back to the rest pose), or jumps back to 0.

anim["take"].speed / .time / .normalizedTime / .wrapMode

Speed (0 freezes, negative plays backwards), jump to a time, and repeat mode: WrapMode.Loop, PingPong, Once (stops at the end), ClampForever (holds the last frame). anim.wrapMode sets it for the current take.

SetAnimatorTrigger(target, "name") / SetAnimatorBool(target, "name", bool)

Plays the take whose name contains name (a bool of false stops it). Animator.Play("take"), Animator.speed and Animator.enabled (pause / resume) also work.

SyncAnimatorState / SyncLegacyAnimationState / Enable…Sync

Still accepted; the state is always shared, so these only re-send it.

ScriptAPI / Blend Shapes

Blend Shapes

Blend shapes (morph targets, shape keys) built into a scene model: the scene editor's Inspector lists them under Blend Shapes, where each one gets a resting value, Auto play, Loop and Seconds. Scripts change them by the name the Inspector shows (right-click a name there to copy it). Capital letters do not matter, the part after the last dot is enough (Maya names a shape blendShapeNode.target, so "toSphere" finds pyramidSphere_blendShape.toSphere), and so is any part of the name; "*" means every blend shape of that object. Without a targetUsername the change goes to the instance server's shared state, so every player sees the same weight at the same moment, players who join later too, and calls made before anybody has loaded the scene are applied when the first player does. With a targetUsername only that player sees it, until they leave.

While a shape plays, its weight goes from its resting value to the far end (1, or 0 when the value is above 0.5) and back, smoothly, once per cycle of seconds. With loop on it keeps going; with loop off it does one cycle and rests at its value again. Every call changes only what you pass: leave out loop or seconds and the shape keeps what it had (its Inspector settings or an earlier call).

SetBlendShape(string name, float value, string targetUsername = null)

Sets the resting weight (0..1) of a blend shape of this object. A playing shape keeps playing around its new value.

SetBlendShape(SceneObject target, string name, float value, string targetUsername = null)

The same, on another object.

PlayBlendShape(string name, bool? loop = null, float seconds = 0, string targetUsername = null)

Starts it playing from the start of a cycle (again from the start if it was already playing). loop and seconds (above 0) change those settings at the same time.

PlayBlendShape(SceneObject target, string name, bool? loop = null, float seconds = 0, string targetUsername = null)

The same, on another object.

StopBlendShape(string name, string targetUsername = null) / StopBlendShape(SceneObject target, string name, string targetUsername = null)

Stops it; the weight goes back to its resting value.

SetBlendShapeLoop(string name, bool loop, string targetUsername = null) / SetBlendShapeLoop(SceneObject target, …)

Turns looping on or off. Turned off while it plays, the cycle it is in finishes, then it stops.

SetBlendShapeSeconds(string name, float seconds, string targetUsername = null) / SetBlendShapeSeconds(SceneObject target, …)

Seconds for one cycle (at least 0.05). While it plays it stays where it is in its cycle.

// Smile once, slowly, for everybody
PlayBlendShape(Face, "Smile", loop: false, seconds: 2f);

// Breathing chest that keeps going
PlayBlendShape(Body, "Breathe", loop: true, seconds: 3.5f);

// Eyes half closed, just for one player
SetBlendShape(Face, "Blink", 0.5f, info.Username);

// Everything back to rest
StopBlendShape(Face, "*");
SetBlendShape(Face, "*", 0f);

See the BlendShapeControl example for buttons that play, stop, loop and speed up a shape.

ScriptAPI / Script Canvases

Script Canvases (Painting Textures)

A script canvas is a picture the script paints, MS Paint style and then some, shown as the texture of one material of an object. Everybody in the world sees exactly the same pixels, also players who join later (they get the picture as it is, then the updates). Coordinates are pixels with (0, 0) at the top left of the picture as the mesh's own texture coordinates place it (for models made in Blender and other tools that is the usual way up, with no need for a negative scale); colours can be see-through.

CreateCanvas(int materialIndex = 0, int width = 256, int height = 256, Color? background = null, int rendererIndex = 0, bool includeChildren = true, bool transparentBackground = false)

Creates a canvas on this object's material number materialIndex (0 = the first) and returns it. Width and height are pixels, 8 to 512. The picture replaces the material's texture for everybody; the material's colour is set to white so the picture shows its own colours.

CreateCanvas(SceneObject target, int materialIndex = 0, int width = 256, int height = 256, Color? background = null, int rendererIndex = 0, bool includeChildren = true, bool transparentBackground = false)

The same, on another object (a slot).

transparentBackground

With true the background is see-through: only what is painted shows, and the surface's material is drawn see-through so whatever is behind it shows elsewhere (paint on glass, a decal on a wall, a sticker layer). The eraser makes it see-through again. A background colour with alpha below 1 (for example Color.clear) does the same.

canvas.SetTransparentBackground(bool transparent = true) / canvas.TransparentBackground

Makes the background see-through (or solid again) and clears the picture; TransparentBackground tells whether it is see-through.

canvas.Clear() / Clear(Color color) / SetBackground(Color color)

Fills the picture with the background colour. With a colour, that becomes the new background (what Clear() and the eraser paint with).

canvas.PSet(int x, int y, Color color)

One pixel.

canvas.Dot(int x, int y, int size, Color color, CanvasBrush brush = CanvasBrush.Round)

One dab of a brush: a dot, a blob of ink.

canvas.Line(int x1, int y1, int x2, int y2, Color color, int size = 1, CanvasBrush brush = CanvasBrush.Round)

A straight line drawn with a brush of that size.

canvas.Stroke(Color color, int size, CanvasBrush brush, params int[] xy)

One brush stroke through all the points (x1, y1, x2, y2, …). A see-through colour stays even where the stroke crosses itself.

canvas.Rect(int x, int y, int width, int height, Color color, bool filled = true, int thickness = 1)

A rectangle, filled or as an outline.

canvas.Ellipse(int cx, int cy, int radiusX, int radiusY, Color color, bool filled = true, int thickness = 1) / Circle(int cx, int cy, int radius, …)

An ellipse or circle around (cx, cy), filled or as an outline.

canvas.Polygon(Color color, bool filled, int thickness, params int[] xy)

A polygon through the points, filled or outlined.

canvas.Fill(int x, int y, Color color, int tolerance = 0)

Paint bucket: fills the area of the same colour around (x, y). Tolerance 0 to 255 also fills colours that are close.

canvas.Spray(int x, int y, int radius, int dots, Color color, int seed = 0)

Spray paint: dots scattered within the radius. The same seed gives the same pattern.

canvas.Erase(int size, params int[] xy)

The eraser: paints the background colour through the points.

canvas.Text(int x, int y, string text, Color color, CanvasFont font = CanvasFont.Sans, int size = 14)

Text with its top left at (x, y); size is the letter height in pixels; \n starts a new line. Fonts: Sans, Bold, Serif, Mono, Pixel. Up to 200 characters per call.

canvas.Border(int thickness, Color color)

A frame round the edge of the picture.

canvas.Gradient(int x, int y, int width, int height, Color from, Color to, bool vertical = true)

A rectangle shaded from one colour to another.

canvas.Blit(TextureItem picture, int x, int y, int width, int height, float angle = 0, Color? tint = null, bool flipX = false, bool flipY = false)

Draws a picture (a texture from your inventory, usually a public TextureItem field the scene author fills in) with its centre at (x, y), width × height pixels, turned angle degrees clockwise. tint multiplies its colours (white or null = as it is) and its alpha makes it fainter; flipX / flipY mirror it. The picture's own see-through parts stay see-through. Each picture is loaded once per scene (made at most 128 pixels a side; up to 8 different pictures per scene); drawing that uses it waits until it has loaded. Stamps, footprints, decals, stickers, icons. There is also Blit(string inventoryId, …).

canvas.BlitFrame(TextureItem sheet, int columns, int rows, int frame, int x, int y, int width, int height, float angle = 0, Color? tint = null, bool flipX = false, bool flipY = false)

Draws one frame of a sprite sheet, like Blit. A sprite sheet is a texture whose frames are all the same size, laid out in columns × rows and numbered left to right, top to bottom from 0. It is loaded once per scene (up to 1024 frames; each made at most 48 pixels a side) and counts as one of the scene's 8 pictures.

canvas.CreateTileGrid(TextureItem sheet, int sheetColumns, int sheetRows, int columns, int rows, int tileWidth, int tileHeight, int x = 0, int y = 0)

A grid of columns × rows tiles, each tileWidth × tileHeight pixels, its top left at (x, y), showing frames of a sprite sheet: a tile map, a character display, an LED board. Returns a CanvasTileGrid. Only tiles that really change are sent, so a big grid stays cheap; the sheet's frames are made exactly tile-sized (when 48 pixels or less), so they stay sharp.

grid.Set(int column, int row, int frame, Color? tint = null) / grid.Fill(int frame, Color? tint = null) / grid.GetFrame(int column, int row) / grid.Redraw()

Shows a frame of the sheet in one tile (or every tile), its colours multiplied by tint (white or null = as it is; darker = dimmer). Frame -1 = an empty tile (the canvas's background colour). Redraw() draws every tile again, for example after canvas.Clear(). Columns, Rows, TileWidth, TileHeight, X and Y describe the grid.

canvas.SetUpdateRate(float perSecond)

How many times a second the drawing is sent: 1 to 20 (4 when not set). Updates that change nothing are not sent, so a still picture costs nothing. Faster suits animation such as an LED board; slower is lighter for everybody.

canvas.SetEmission(float level)

Makes the picture glow: it is also used as the material's emission map, at this strength (0 = no glow, the material's own emission again; 1 = the picture's colours at full brightness; up to 10). Dark parts of the picture stay dark. Players who join later get it too.

canvas.Present()

Drawing is sent automatically, up to 4 times a second. Present() sends it now if the last update was at least a quarter of a second ago (returns true), otherwise it goes with the next update.

canvas.Release()

Stops the canvas: the material shows its own texture again, for everybody, and the scene can make another canvas.

canvas.Width / Height / BackgroundColor / Queued / Released

The picture's size, its background colour, the drawing commands waiting to be sent, and whether it was released.

CanvasBrush: Round, Square, Soft, Spray, Calligraphy

Brush shapes for Dot, Line and Stroke. Soft fades towards its edge; Spray scatters dots.

Limits, so a world stays smooth for everybody: a scene can have 10 canvases at once, each up to 1024 × 1024 pixels; each one updates 4 times a second, or up to 20 with SetUpdateRate; an update that changes no pixels is not sent (faster drawing is gathered into the next update); one update carries at most 4000 drawing commands and a fixed amount of drawing work (commands past it are skipped, the same for everybody). The picture is kept in the instance server's memory only, never saved: it starts blank again when the scene restarts. Each limit that is hit is written to the script log once.

ScriptCanvas canvas;
public override void Start()
{
    canvas = CreateCanvas(0, 256, 256, Color.white);            // this object's first material
    canvas.Border(4, Color.black)
          .Text(10, 10, "Hello!", Color.blue, CanvasFont.Bold, 24)
          .Circle(128, 140, 50, new Color(1f, 0.5f, 0f), true)
          .Line(20, 230, 236, 230, Color.red, 6, CanvasBrush.Soft);
}

public TextureItem Sticker;                                    // a picture from your inventory
void Stamp(int x, int y) => canvas.Blit(Sticker, x, y, 48, 48, Random.Range(0f, 360f));

public TextureItem Letters;                                    // 26 letters A-Z in one row
void Sign()
{
    CanvasTileGrid grid = canvas.SetUpdateRate(10).CreateTileGrid(Letters, 26, 1, 10, 1, 24, 32);
    string word = "HELLO";
    for (int i = 0; i < word.Length; i++) grid.Set(i, 0, word[i] - 'A', Color.green);
}

ScriptAPI / Spawning

Inventory Spawning And Player Controls

SpawnInventoryItem(InventoryItem item, Vector3 position, Vector3 eulerAngles, Vector3 scale)

Spawns an inventory item for everyone and returns it as a SceneObject; players who join later see it too. The item can be a mesh, a prefab or a system object (light, sitpoint, water, particles, media screen…), whichever was dropped on the script's InventoryItem field. A mesh spawns solid (a collider shaped like it). A prefab (Hierarchy → Save to Inventory) spawns with everything saved in it: rigidbodies fall and can be grabbed and thrown (simulated on the server), and scripts on it run on the server, one copy per spawned prefab. Each player downloads an item's model, textures and scripts once per visit, however often it is spawned.

DespawnSpawnedObject(SceneObject spawnedObject)

Removes a spawned object for everyone, with all its parts, their physics and the scripts that ran on them.

TeleportPlayer(string username, Vector3 position, Vector3 eulerAngles)

Queues a teleport command for one username.

SetPlayerCamera(string username, float yaw, float pitch = NaN, float blendSeconds = 0.4f) / SetPlayerCamera(UserInfo user, …)

Turns that player's camera (the 3rd-person orbit camera, and the 1st-person view) to look along yaw, in degrees, the same as TeleportPlayer's facing (0 = toward −Z, 90 = +X), blended over blendSeconds (0 = at once). pitch: degrees, + looks down; leave it out (NaN) to keep the current pitch. Only that player's screen changes. Nothing happens in VR (the head is the view) or in the free / static camera. Seated, the yaw is still a world heading. E.g. after TeleportPlayer(user, spot, new Vector3(0, 90, 0)), SetPlayerCamera(user, 90) puts the camera behind them.

SetPlayerCameraMode(string username, CameraMode mode, bool lockMode = false) · ReleasePlayerCameraMode(string username) (also with UserInfo)

Puts that player's view in CameraMode.FirstPerson or CameraMode.ThirdPerson (CameraMode.Default = release). With lockMode their own zoom / view change is held off until you release it. ReleasePlayerCameraMode puts back exactly what they had (1st person, or their own zoom distance). Nothing happens in VR. For anything that poses the avatar (lining up a putt, a bowling run-up, sitting at a piano): SetPlayerCameraMode(user, CameraMode.ThirdPerson, true) when it starts, ReleasePlayerCameraMode(user) when it ends.

UserInfo.CameraMode

The view the player is in now: FirstPerson, ThirdPerson, VR, Free (F4) or Static (F2); Default until the first camera update. Kept up to date with CameraYaw / CameraForward. In 1st person an emote no longer moves the view: the eye stays where the head was when it started.

TeleportPlayer(string username, Vector3 position, Vector3 eulerAngles, bool keepCamera)

keepCamera = true: the player's camera keeps exactly the same world view (yaw and pitch) while the avatar moves and turns: turn a player around a spot every time their aim changes without the camera and the aim fighting. false: the camera also turns to look the way the new facing (eulerAngles.y) does. Without it (the 3-argument version) the camera is left as it is.

UserInfo GetUser(string username) · UserInfo.CameraYaw, CameraPitch, CameraForward, CameraPosition, CameraIsVR, HasCamera

The player's live camera, kept up to date about 10 times a second while it moves (read it in your own loop; the same UserInfo objects as GetUsers() and OnUserJoin). CameraYaw: degrees, as TeleportPlayer / SetPlayerCamera (0 = looking −Z, 90 = +X). CameraPitch: degrees, + looks down. CameraForward: unit vector where the camera looks (world). CameraPosition: where the camera is. CameraIsVR: it is a headset (the head is the view). HasCamera: false until the first update. CameraAge: seconds since the server sent these values (normally under 0.2 s).

PlayPlayerEmote(string username, Emote emote)

Plays the emote chosen in an Emote field on one user (everyone in the instance sees it). Nothing happens, with a warning in the log, if no emote was chosen.

PlayEmoteForAll(Emote emote)

Plays the emote on every user in the instance.

StopPlayerEmote(string username) / StopAllPlayerEmotes()

Stops the emote one user, or everyone, is playing.

SetPlayerScale(string username, float scale)

Sets uniform scale for one user.

SetPlayerScale(string username, Vector3 scale)

Sets non-uniform scale for one user.

SetAllPlayerScales(float scale) / SetAllPlayerScales(Vector3 scale)

Sets player scale for everyone.

SetPlayerNameTag(string username, bool show)

Shows or hides a player's name tag — the floating username that appears when you hover, tap or point at them — for everybody in the world. "*" = every player in the world now. This overrides the scene's Show player name tags setting for the current world; it is cleared when the player leaves or the scene changes.

SetAllPlayerNameTags(bool show)

Shows or hides every player's name tag in the world now (same as SetPlayerNameTag("*", show)).

TintPlayer(string username, Color color, float strength = 0.75f, float emission = 0f)

Colours a player's whole avatar (body, clothes, hair, everything they wear) for everybody in the world, also players who join later. strength: 0 = not at all, 1 = solid colour, as if dipped in paint (light and shade still show). emission: 0 = no glow; above 0 the avatar glows in that colour (1 = full brightness, up to 10). Calling it again changes the colour. It lasts until ResetPlayerTint, or until the player leaves the world (they come back looking normal) or the scene changes. "*" = every player in the world now. There is also TintPlayer(UserInfo user, …).

ResetPlayerTint(string username) / ResetPlayerTint(UserInfo user)

The player's avatar looks as normal again, for everybody. "*" = every player.

SetPlayerVisible(string username, bool visible, bool hideFromSelf = true)

Hides (false) or shows (true) a player's whole avatar (body, everything they wear, their name tag and voice meter) for everybody in the world, also players who join later. hideFromSelf: false = the player still sees themselves (faded, like a ghost, so they know the others cannot see them), only the others do not. It stays when they change outfits, until the script shows them again, or until they leave the world (they come back visible) or the scene changes. A hidden player cannot be pointed at or clicked; objects they hold stay visible. "*" = every player in the world now. There is also SetPlayerVisible(UserInfo user, …).

HidePlayer(string username, bool hideFromSelf = true) / ShowPlayer(string username) / SetAllPlayersVisible(bool visible, bool hideFromSelf = true)

The same, shorter (also with a UserInfo).

bool IsPlayerVisible(string username) / IsPlayerVisible(UserInfo user)

False while a script keeps that player hidden.

SetMediaScreenUrl(string url)

Sets all media screens to a URL.

SetMediaScreenUrl(string mediaScreenId, string url)

Sets one media screen id, or * for all media screens.

SetMediaScreenUrlForUser(string targetUsername, string url)

Sets all media screens to a URL only for the named user. Stored database rows are ignored for that user until a live public media update arrives. The override is not restored after rejoining.

SetMediaScreenUrlForUser(string targetUsername, string mediaScreenId, string url)

Sets one media screen, or * for all screens, only for the named user. A public SetMediaScreenUrl or /media update releases the matching private override.

GetCurrentSceneIdentifier()

Returns the current scene instance EventID for the server script. This is useful for logs and instance-specific script behavior.

ScriptAPI / Backpack

Backpack

A world can give every player a backpack: a Backpack button on the control bar (desktop, mobile and VR) that opens a panel of item icons with their names, five across, scrolling when there are more. Worlds that do not set one up have no button and no panel. The panel takes the platform's theme like every other panel. See the Backpack example.

SetupBackpack(List<InventoryItem> items, List<TextureItem> icons, List<string> names, Action<BackpackSelection> onItemSelected)

Sets up the world's backpack for every player (and players who join later). Entry i of each list belongs together: items[i] is the item, icons[i] a texture for its picture and names[i] the name under it. An empty icon uses the item's own thumbnail; an empty name, the item's own name. onItemSelected runs on the server when a player picks an entry: your script decides what happens, usually SpawnInventoryItem in front of that player. One backpack per world: the latest call replaces it. Up to 200 entries.

RemoveBackpack()

Takes the backpack away: the button leaves every player's control bar and an open panel closes.

BackpackSelection

What a player picked: Username, UserId, Index (the entry), Item, DisplayName, PlayerPosition (their feet), Forward (the flat direction their avatar faces), Yaw (that direction in degrees, for a spawn rotation of new Vector3(0, Yaw, 0)) and PlayerObject.

BackpackSelection.SpawnPosition(float distance = 1.5f, float height = 1f)

A point distance metres in front of the player and height metres above their feet.

Inspector: InventoryItem, TextureItem and List<…> fields

Drag items from the Inventory panel onto the field. InventoryItem fields take things that can be spawned (prefabs, meshes and system objects); TextureItem fields take textures; other kinds are refused. In a list, drop on an entry to replace it or on the box at the end to add one. Lists are numbered from #0 so entries of different lists line up. List<string> fields get one text box per entry.

ScriptAPI / Avatar Clones

Avatar Clone Spawning

Avatar clones are spawned and animated on the instance server, then serialized to clients through player-style packets. Keep the returned AvatarClone reference if you want to move it, force emotes, slot it into a sitpoint, or destroy it later.

SpawnAvatarClone(string username, Vector3 position)

Spawns a clone of the named user's avatar at a world position and returns an AvatarClone reference.

SpawnAvatarClone(string username, Vector3 position, Vector3 eulerAngles)

Spawns a clone with a world position and rotation.

SpawnAvatarClone(string username, Vector3 position, Vector3 eulerAngles, Vector3 scale, string outfitName = null)

Spawns a clone with an explicit rotation and scale. The clone wears the user's outfit named outfitName (the name they gave it in the Avatar Editor; capitals do not matter), or the outfit they are wearing when outfitName is blank or they have no outfit by that name. The clone's OutfitName keeps the name asked for.

DestroyAvatarClone(AvatarClone clone)

Destroys a spawned clone. Clients remove it when the server stops sending clone packets.

SetAvatarClonePosition(AvatarClone clone, Vector3 position)

Moves a clone to a world position.

SetAvatarCloneRotation(AvatarClone clone, Vector3 eulerAngles)

Sets clone world rotation in Euler angles.

SetAvatarCloneScale(AvatarClone clone, Vector3 scale)

Sets clone scale.

SetAvatarCloneTransform(AvatarClone clone, Vector3 position, Vector3 eulerAngles, Vector3 scale)

Sets clone position, rotation, and scale in one command.

PlayAvatarCloneEmote(AvatarClone clone, Emote emote)

Makes the clone play the emote chosen in an Emote field. The resulting bones are serialized to clients like player bones.

StopAvatarCloneEmote(AvatarClone clone)

Stops the clone's forced emote.

SeatAvatarClone(AvatarClone clone, Sitpoint sitpoint)

Slots the clone into a slotted Sitpoint.

SeatPlayer(string username, Sitpoint sitpoint, bool force = false)

Sits a real player down on a slotted Sitpoint, as if they had clicked it (their OnUserSit follows). If they are already sitting somewhere else they move over. Without force, nothing happens when someone else is in the seat; with force, that player is stood up first (their OnUserStand runs) and the seat goes to username.

ForceSeatPlayer(string username, Sitpoint sitpoint)

The same as SeatPlayer(username, sitpoint, true): always seats them, standing up whoever was there.

StandPlayer(string username)

Makes that player leave their sitpoint: they step out at the seat’s exit point and OnUserStand runs. Pass "*" to stand up everyone who is sitting. Players who aren’t sitting are not affected.

StandAllPlayers()

The same as StandPlayer("*").

StandPlayer(Sitpoint sitpoint)

Stands up whoever is sitting in that sitpoint (nothing happens if it is free). Handy for vehicles: empty a seat without knowing who is in it.

StandAvatarClone(AvatarClone clone)

Removes the clone from its current sitpoint.

AvatarClone Reference Methods

clone.Destroy()

Same as DestroyAvatarClone(clone).

clone.SetPosition(position) / SetRotation(eulerAngles) / SetScale(scale) / SetTransform(position, eulerAngles, scale)

Convenience methods for controlling the cached clone reference.

clone.PlayEmote(emote) / clone.StopEmote()

Convenience methods for forcing or stopping server-side clone emotes.

clone.Ragdoll(CharacterRagdollSettings settings = null, float autoStandAfterSeconds = 0f) / clone.StandUp(float transitionSeconds = 0.8f)

Turns the clone into a ragdoll (it falls onto whatever is below, like a player) and gets it back up where the body lies. Also SetAvatarCloneRagdoll(clone, true / false). Sitting, moving or destroying the clone ends the ragdoll.

clone.ApplyImpact(Vector3 direction, float power = 8f, float durationSeconds = 0.15f, float autoStandAfterSeconds = 0f, CharacterImpactPart part = CharacterImpactPart.All)

Hits the clone (it becomes a ragdoll if it is not one yet): power metres per second in direction. Also ApplyAvatarCloneImpact(clone, …).

clone.Sit(sitpoint) / clone.Stand()

Convenience methods for sitpoint slotting.

public Emote selectedEmote;
public Sitpoint cloneSeat;

AvatarClone clone;

void SpawnOne(string username)
{
    clone = SpawnAvatarClone(username, transform.position + Vector3.forward * 2f);
}

void ToggleEmote()
{
    if (clone != null && !clone.IsNull)
        clone.PlayEmote(selectedEmote);
}

void SitClone()
{
    if (clone != null && !clone.IsNull)
        clone.Sit(cloneSeat);
}

void RemoveClone()
{
    if (clone != null && !clone.IsNull)
        clone.Destroy();
}

ScriptAPI / Character Physics

Character Physics

Ragdoll physics for players. Falling, Sleep and impacts with fall = true turn the player's body into a ragdoll: it falls with gravity onto floors, terrain and furniture, gets pushed around by moving physics objects, and stays down until it is stood up again (by StopCharacterPhysicsMode or an auto-stand delay). The player cannot walk while ragdolled, and gets up where the body lies. A body lying on a surface uses that surface's Collider Friction and Bounciness (with its Combine modes) together with the ragdoll's own settings, and comes to rest once it stops moving; an impact, a player walking into it or a moving physics object wakes it again. Avatar clones can be ragdolled too (clone.Ragdoll, below). The player's own viewer simulates the body and everyone in the instance sees the same fall. Use "*" as the username for everyone.

SetCharacterPhysicsMode(string username, CharacterPhysicsMode mode, float transitionSeconds = 0.8f, float autoStandAfterSeconds = 0f)

Falling: ragdoll with the current ragdoll settings (use it for fall trigger zones). Sleep: a completely limp ragdoll. Standing: get up, blending back to normal animation over transitionSeconds. Set autoStandAfterSeconds above 0 to get up automatically.

StopCharacterPhysicsMode(string username, float transitionSeconds = 0.8f)

Gets the player up from a ragdoll: they stand where the body lies, and the pose blends back to normal over transitionSeconds (0 = at once).

SetCharacterRagdollSettings(string username, CharacterRagdollSettings settings)

How that player's ragdoll behaves from now on (also while already ragdolled): Strength (muscles: 0 = limp, 1 = stiff as a statue; about 0.1–0.2 looks like a person falling), GravityScale (1 = normal), Damping (air drag, 0–0.95), Friction (0 = slides, 1 = stops where it lands), Bounciness (0–1).

ApplyCharacterImpact(string username, Vector3 direction, float power = 8f, float durationSeconds = 0.15f, bool fall = true, float autoStandAfterSeconds = 0f, CharacterImpactPart part = CharacterImpactPart.All)

Hits the player: power is the speed (metres per second) added in direction, spread over durationSeconds, to the body part (or the whole body). With fall = true the player becomes a ragdoll (or is pushed further if already one); with fall = false a standing player is only pushed. direction = Vector3.zero, power = 0, fall = true simply collapses the player.

// Start falling for the player who entered a trigger.
SetCharacterPhysicsMode(info.Username, CharacterPhysicsMode.Falling, 0.15f);

// Stop when the player reaches the landing trigger.
StopCharacterPhysicsMode(info.Username, 0.8f);

// Drop in place and recover after 3 seconds.
ApplyCharacterImpact(info.Username, Vector3.zero, 0f, 0f, true, 3f);

// A floppy, low-gravity ragdoll, then knock the player backwards off their feet.
CharacterRagdollSettings floppy = new CharacterRagdollSettings();
floppy.Strength = 0.05f;
floppy.GravityScale = 0.6f;
SetCharacterRagdollSettings(info.Username, floppy);
ApplyCharacterImpact(info.Username, new Vector3(0f, 0.5f, -1f), 6f, 0.15f, true, 4f);

ScriptAPI / Chat

Chat Commands And Room Messages

RegisterChatCommand(Action<ChatCommandInfo> callback)

Listens for every non-system slash command that was not handled by the game client command list.

RegisterChatCommand(string command, Action<ChatCommandInfo> callback)

Listens for one custom slash command such as /roll. The leading slash is optional. Built-in commands (/yt, /media, /help, /alert, /kick, /ban and the other admin and recording commands) are handled by the viewer and cannot be registered: the script log says so and the command is skipped.

StopListeningForChatCommand(string command = null)

Removes this script's chat command listener for one command, or all chat command listeners when omitted.

SendScriptChat(string message)

Inserts a room chat history entry from the fake username [Script].

ScriptAPI / Interaction

Interactables, Triggers, Collisions, And Sitpoint Input

MakeInteractable(Action<InteractionInfo> onInteract, bool highlight = true, string targetUsername = null, bool includeChildren = true, float raycastDistance = 10f, string hintText = null)

Makes the script object interactable and calls the callback when activated.

MakeInteractable(SceneObject targetObject, Action<InteractionInfo> onInteract, bool highlight = true, string targetUsername = null, bool includeChildren = true, float raycastDistance = 10f, string hintText = null)

Makes a target object interactable. highlight = false keeps it clickable without the hover highlight; raycastDistance is how close (in metres) a player must be to select it. With targetUsername only that player can use it; players who join later get it too.

SetInteractable(bool enabled, string targetUsername = null)

Switches the script object's interaction off or on again, for everyone or only for targetUsername. Off: it cannot be selected or clicked (no highlight, no hint, no callback) by those players. On: back as MakeInteractable set it up, or a plain interactable if it never was. Players who join later get the current state.

SetInteractable(SceneObject targetObject, bool enabled, string targetUsername = null)

The same for another object. Example: SetInteractable(door, false); SetInteractable(door, true, "alice"); lets only alice use the door.

RegisterTrigger(Action<TriggerInfo> onEnter, Action<TriggerInfo> onExit = null, bool includeChildren = true)

Registers trigger callbacks on the script object.

RegisterTrigger(SceneObject targetObject, Action<TriggerInfo> onEnter, Action<TriggerInfo> onExit = null, bool includeChildren = true)

Registers trigger callbacks on a target object.

RegisterCollision(Action<CollisionInfo> onCollision, bool includeChildren = true)

Registers a server-side collision callback on the script object. The callback receives the collided object's ObjectName and ObjectId.

RegisterCollision(SceneObject targetObject, Action<CollisionInfo> onCollision, bool includeChildren = true)

Registers a server-side collision callback on a target object or its child colliders.

RegisterSitpointInput(Action<SitpointInputInfo> onInput)

Registers a callback for input from the sitpoint associated with this script's network id.

ScriptAPI / Action Buttons And Grabs

Action Buttons, Aiming, And Grabs

A script can give a player their own buttons: a Spray button while they hold a spray can, a Fire button in a game. A button shows on screen on every device (a big round touch button on phones and tablets, a smaller one with its key on desktop), works with its keyboard key on desktop and with the chosen controller button in VR. While it is held, the script hears it about 15 times a second, each time with where the player aims: the middle of their view on desktop and mobile, the holding hand's controller in VR.

ShowActionButton(string username, string buttonId, string label = null, string key = "F", VRButton vrButton = VRButton.RightStickClick)

Gives a player (or "*", everybody) a button. key is the desktop key ("F", "G", "1", "Space"...). Showing it again changes its label or key. It stays until HideActionButton, the player leaves, or the scene changes.

HideActionButton(string username, string buttonId)

Takes the button away again.

ListenForActionButton(string buttonId, Action<ActionButtonInfo> callback)

Called when a player presses this script's button (State Down), keeps holding it (Held, about 15 times a second) and lets go (Up). Only the script that showed the button hears it.

ActionButtonInfo

Username, UserId, ButtonId, State (ActionButtonState.Down / Held / Up), IsDown, AimOrigin and AimDirection; what the aim hits: HasHit, HitPoint, HitNormal, HitObjectId / HitObject, HitRootObjectId, HitObjectIds (the hit object and every object it is part of, innermost first), Hits(SceneObject) (true when the aim hits that object, a part of it, or something it is part of), HitMaterialIndex, HitUV with HasUV (the texture position, 0 to 1; on a script canvas the pixel is x = HitUV.x * canvas.Width, y = (1 - HitUV.y) * canvas.Height) and HitDistance (metres from the held object, or the eye, to the hit); HeldObjectId, what the player holds.

VRButton

The controller buttons a script may use, the others already have a job: RightStickClick (pressing the right stick), LeftGrip, LeftTrigger, RightTrigger (careful: the trigger also holds what you grab), or None.

ListenForGrab(Action<GrabInfo> callback) / ListenForGrab(SceneObject target, Action<GrabInfo> callback)

Called when a player grabs or lets go of this object (or another one). GrabInfo: Username, UserId, ObjectId / Target, Hand, IsGrabbed.

GetHolder() / GetHolder(SceneObject target)

The username of whoever holds this object (or another one) now; empty when nobody does.

bool GrabObject(string username, SceneObject obj, string hand = "") / GrabObject(UserInfo user, …)

Puts a Grabbable object in a player's hand, as if they had clicked it. hand: "Left", "Right", "Both", or "" = the object's own Grabbable handedness. ListenForGrab fires (IsGrabbed = true, Username = that player) and every player sees it held, with the object's Hold Pose and Grip. If somebody else holds it, it is taken from them (they get a release event first). If that hand is already holding something, that is let go. Returns false and does nothing if the player is not in the world or the object is not Grabbable.

ReleaseObject(SceneObject obj)

Whoever holds obj lets go of it. ListenForGrab fires (IsGrabbed = false). It stays where it was dropped (where the hand had it); the script can move it afterwards (a move right after ReleaseObject wins, e.g. sending it home). Does nothing if nobody holds it.

AttachToSlot(string username, SceneObject obj, AttachSlot slot) / AttachToSlot(UserInfo user, SceneObject obj, AttachSlot slot)

Puts a Grabbable object on a player's body instead of in a hand: AttachSlot.Head (a hat), Back (a backpack, a parachute pack), Chest or Hips (AttachSlot.Hand is the same as GrabObject). It follows that bone in every pose (walking, seated, ragdoll, VR), scales with the player, uses the object's Grabbable Slot Offset / Rotation / Scale, and its children ride along (e.g. a canopy child a script grows with LerpLocalScale). It takes no hand and is never thrown. Whatever the player wore on that slot before is taken off. ListenForGrab fires with IsGrabbed = true and Slot set. Returns false if the player is not in the world or the object is not Grabbable.

DetachFromSlot(SceneObject obj)

Takes it off whoever wears (or holds) it; it stays where it was. The same as ReleaseObject, and it works even when the player can't release it themselves.

SetScriptOnlyRelease(SceneObject obj, bool scriptOnly = true)

true: players can't let go of this object, worn or held. The drop key (X), the VR trigger and throwing don't release it, and nobody can take it from them; only a script does (DetachFromSlot / ReleaseObject). false: back to normal. Off by default; the Grabbable's Player Can't Release setting turns it on for an object from the start, and a script's setting wins. It works whether or not someone has the object right now.

GrabInfo.Slot / GrabInfo.IsWorn

Where the object is: "Hand" (held), or "Head", "Back", "Chest", "Hips" (worn). IsWorn is true for the body slots.

// Clicking a Grabbable set to Attach To: Back straps it on by itself; a script can do it too (here: /pack).
// SetScriptOnlyRelease: the player can't take it off (X, VR trigger); only the script does (/unpack).
public SceneObject Pack;
public override void Start()
{
    ListenForGrab(Pack, g => { if (g.IsGrabbed && g.IsWorn) Log(g.Username + " is wearing the pack"); });
    SetScriptOnlyRelease(Pack, true);
    RegisterChatCommand("pack", c => AttachToSlot(c.Username, Pack, AttachSlot.Back));
    RegisterChatCommand("unpack", c => DetachFromSlot(Pack));
}
ListenForHeldHit(SceneObject held, Action<HeldHitInfo> callback) / ListenForHeldHits(Action<HeldHitInfo> callback)

Called when a held object touches an object that has a Rigidbody. Held objects push things by themselves, no script needed: a stick, bat, paddle or broom knocks a moving (dynamic) Rigidbody away, or keeps pushing it while it keeps moving into it. These calls are for reacting to the touch (a score, a sound, damage), and for targets that physics does not move (kinematic ones a script moves). The holder's own viewer checks it every frame (in VR at the headset's frame rate), swept from last frame to this one so a fast swing cannot pass through; the server checks each report (the sender holds it, the target is near them, a sane speed). A held object touches with its whole length; Grabbable Custom Hit Point (with its Hit Point, in its own local space, and Hit Radius) makes only that spot touch, e.g. a club head. ListenForHeldHits hears every held object.

HeldHitInfo

Username, UserId, Hand, HeldObjectId / HeldObject, TargetObjectId / Target; in world space: Point (where they touched, on the target's surface), Normal (from the target's centre towards the held object: the target is pushed along -Normal), Velocity (how fast and which way the hit point was moving, metres per second, at most 15). Speed = its length; SpeedAlongNormal = how fast it was moving into the target; IsFirstContact: a new touch (false: still pushing it, every 0.1 s).

SceneObject[] GetHeldObjects(string username) / GetHeldObjects(UserInfo user)

What that player is holding right now (an empty array: nothing).

// A jump rope: two handles (Grabbable Left / Grabbable Right). Grab either one and you get both;
// let go of either and both are dropped and sent home.
public SceneObject LeftHandle, RightHandle;
private Vector3 leftHome, rightHome, leftRot, rightRot;

public override void Start()
{
    leftHome = LeftHandle.transform.position; leftRot = LeftHandle.transform.eulerAngles;
    rightHome = RightHandle.transform.position; rightRot = RightHandle.transform.eulerAngles;
    ListenForGrab(LeftHandle, info => Handle(info, RightHandle, "Right"));
    ListenForGrab(RightHandle, info => Handle(info, LeftHandle, "Left"));
}

// Our own GrabObject / ReleaseObject calls fire ListenForGrab too (a moment later): that is harmless here, grabbing a
// handle you already hold and releasing one nobody holds do nothing.
private void Handle(GrabInfo info, SceneObject other, string otherHand)
{
    if (info.IsGrabbed) GrabObject(info.Username, other, otherHand);
    else
    {
        ReleaseObject(other);
        LeftHandle.transform.position = leftHome;  LeftHandle.transform.eulerAngles = leftRot;
        RightHandle.transform.position = rightHome; RightHandle.transform.eulerAngles = rightRot;
    }
}
// Held objects knock moving Rigidbody objects away by themselves. This counts the hits and plays a sound on a hard one.
public SceneObject Bat;
public SceneObject HitSound;
private int hits;

public override void Start()
{
    ListenForHeldHit(Bat, hit =>
    {
        if (!hit.IsFirstContact) return;   // only new touches, not pushing
        hits++;
        if (hit.SpeedAlongNormal > 4f) PlayAudio(HitSound);
        Log(hit.Username + " hit " + hit.TargetObjectId + " (" + hits + " hits)");
    });
}

Holding and emotes. A Grabbable's Hold Pose poses the holding arm. None (animation drives arm) never poses it: the walk / idle animation or an emote moves the arm and the object follows the hand. With any hold pose, while an avatar emote is playing on the holder (PlayPlayerEmote or the player's own emote) the hold pose, camera aim and two-handed reach step aside, so the emote moves the arms and the object follows the emote's hand, for everyone. Grip: Fist closes the holding hand's fingers and thumb around the object (also while emoting; emotes carry no finger animation).

Grabbable objects have Aim With Camera (on by default, in the Inspector's Grabbable section): while one is held, the arm points it where the camera looks, in 1st and 3rd person on desktop and mobile, with the hold pose still on the rest of the body; a small dot marks the middle of the view, and everybody sees the arm aim. In VR the controller aims the hand. The same section has Grab Distance: how close (metres) a player must be to grab the object (3.05 m when not set), and Throwable (on by default): turn it off and the object can only be dropped (X), never aimed or thrown, on desktop, mobile and in VR. VR Click To Hold (off by default): in VR one trigger press picks it up and it stays in the hand without holding the trigger; a quick double press lets go (a moving hand throws it when Throwable is on).

public override void Start()
{
    ListenForGrab(info =>
    {
        if (info.IsGrabbed) ShowActionButton(info.Username, "fire", "Fire", "F");
        else HideActionButton(info.Username, "fire");
    });
    ListenForActionButton("fire", info =>
    {
        if (info.State == ActionButtonState.Down && info.HasHit) Log(info.Username + " hit " + info.HitObjectId);
    });
}

ScriptAPI / HUDs

HUDs

A HUD is an on-screen window a script builds out of labels, buttons, sliders, dropdowns, toggles, text fields, pictures and progress bars, and shows to the players it chooses: one player, several, or everyone (players who join later get it too). While a player has at least one HUD, a HUDs button shows on their control bar; it opens the HUD view, where only the HUDs and the control bar show. Players drag a HUD by any spot that is not a control to move it; HUDs never overlap (one dropped on another moves to the nearest free spot), and where each was left is remembered for that player in that world (Settings → Reset UI Panels lets them find new places). A HUD the script shows a player opens their HUD view at once, and brings the menus back if they were hidden. HUDs work on desktop, phones and tablets, and in VR.

Every change shows at once for the players who have the HUD. Each setter takes an optional username as its last parameter: left out (null), it changes the HUD for everybody; with a username, for that player only. What players choose (a slider they moved, a dropdown pick) is their own and never changes anyone else's: read it with GetValue(username), GetSelected(username), IsOn(username) or GetText(username). Only the script that made a HUD hears what players do with it. A script can have 8 HUDs at once (a HUD up to 200 items); they go when the script stops (its object is removed) or the scene changes.

ScriptHud CreateHud(string hudId, int width = 320, int height = 0)

Makes a HUD (width in pixels; height 0 = as tall as its items need). Nobody sees it until Show. Creating one with an id that exists replaces that one.

ScriptHud GetHud(string hudId) / DestroyHud(string hudId)

This script's HUD with that id (null: none) / takes it away from everybody.

hud.Show(string username, bool openHudView = true) / hud.Show(UserInfo user, ...) / hud.ShowToEveryone(bool openHudView = true)

Gives the HUD to a player ("*" = everybody). openHudView: their HUD view opens to show it. Showing it again to a player who has it opens their HUD view again.

hud.Hide(string username) / hud.HideFromEveryone() / hud.IsShownTo(string username) / hud.Destroy()

Takes it away from a player or everybody (it can be shown again), whether a player has it now, or removes it for good.

hud.SetClosable(bool closable) / hud.OnClose(Action<HudEvent> callback)

Players can close a HUD themselves with the × in its corner (on by default); OnClose hears about it. A closed HUD is hidden for that player until it is shown to them again.

hud.SetTitle(string title) / SetTitleColor(Color color) / SetTitleStyle(Color color, int size = 20, HudAlign align = Center)

A title across the top ("" = none).

hud.SetLogo(TextureItem logo, int height = 64)

A picture centred at the very top, above the title.

hud.SetBackgroundColor(Color color) / SetBackgroundImage(TextureItem image, HudImageFit fit = Cover) / SetOpacity(float opacity)

The background (colours can be see-through: alpha). HudImageFit: Cover (fills it, cut to fit), Contain (all of the picture shows), Stretch, Tile.

hud.SetFont(HudFont font, int size = 14) / SetTextColor(Color color)

Lettering for the whole HUD. HudFont: Default (the platform's), Sans, Serif, Mono, Rounded, Condensed, Heavy, Handwriting. A public HudFont field is a dropdown in the Inspector.

hud.SetButtonColors(Color text, Color background) / SetAccentColor(Color color)

The colours every button starts with (a button's own SetColors wins), and the colour of sliders, toggles and progress bars.

hud.SetLayout(int columns = 1, int gap = 6, int padding = 12, HudAlign align = Stretch)

Items go top to bottom, columns side by side per row (1 = a list); gap between them and padding around them in pixels; align: where items sit across their column (Left, Center, Right, or Stretch = full width). Also one at a time: SetColumns, SetGap, SetPadding, SetAlign.

hud.SetSize(int width, int height = 0) / SetCornerRadius(int pixels) / SetBorder(Color color, int width = 2)

Size, rounded corners and a border.

HudItem hud.AddLabel(string text)

Text (several lines with \n).

HudButton hud.AddButton(string text, Action<HudEvent> onClick = null) / AddButton(string text, TextureItem icon, Action<HudEvent> onClick = null)

A button, with a picture above its text if you give one ("" text = picture only). button.OnClick(callback) sets or changes what it does.

HudSlider hud.AddSlider(string label, float min, float max, float value, Action<HudEvent> onChange = null, float step = 0)

A slider (step 0 = smooth); the number shows next to the label (SetShowValue(false) hides it). While a player moves it the script hears about it about 8 times a second, and always the last value. slider.GetValue(username), SetValue(float value, string username = null), SetRange(min, max, step), OnChange.

HudDropdown hud.AddDropdown(string label, List<string> options, int selected = 0, Action<HudEvent> onChange = null)

A list to pick from (up to 100 entries). dropdown.GetSelected(username) (the index), GetSelectedText(username), SetSelected(int index, string username = null), SetOptions(List<string> options, int selected = 0), OnChange.

HudToggle hud.AddToggle(string label, bool on = false, Action<HudEvent> onChange = null)

An on / off switch. toggle.IsOn(username), SetOn(bool on, string username = null), OnChange.

HudTextInput hud.AddTextInput(string placeholder = "", Action<HudEvent> onSubmit = null, string text = "", int maxLength = 200)

A text field; onSubmit runs when the player presses Enter (or leaves the field after changing it), with the text in Text. input.GetText(username), SetValue(string text, string username = null), SetPlaceholder, SetMaxLength.

HudItem hud.AddImage(TextureItem image, int height = 96)

A picture (it keeps its shape).

HudProgressBar hud.AddProgressBar(float value = 0, string label = "")

A bar filled from 0 (empty) to 1 (full), with optional text over it: bar.SetValue(float value, string username = null). Good for cooldowns, health, scores.

HudItem hud.AddSpacer(int height = 8) / AddDivider()

Empty space / a thin line.

hud.GetItem(int id) / hud.RemoveItem(HudItem item) / hud.Clear() / hud.Items

An item by its Id, removing one (or item.Remove()), removing all of them (the HUD and its look stay), all of them.

Every item: SetText, SetEnabled, SetVisible, SetTextColor, SetBackgroundColor, SetColors(text, background), SetAccentColor, SetImage(TextureItem), SetSpan(int columns), SetHeight(int pixels), SetWidth(int pixels), SetAlign(HudAlign), SetFontSize(int size), SetBold(bool), SetTooltip(string)

All but SetSpan take an optional username (that player only). SetEnabled(false) greys it out: it cannot be used. SetSpan: how many columns it takes up. SetTooltip: a hint shown when the pointer rests on it. Each item also has Id, Kind (HudItemKind), Hud and IsRemoved.

HudEvent

What a player did: Username, UserId, User (their UserInfo: where they stand and face), EventType (HudEventType.Click, Change, Submit, Close), Hud, HudId, Item, ItemId, and the new Value (slider), Index and Text (dropdown), IsOn (toggle) or Text (text field).

private ScriptHud hud;
private HudSlider speed;
private HudItem result;

public override void Start()
{
    hud = CreateHud("game", 300);
    hud.SetTitle("Race").SetTitleColor(new Color(1f, 0.8f, 0.2f)).SetLayout(2);
    hud.AddButton("Start", e => result.SetText("Go, " + e.Username + "!", e.Username));
    hud.AddButton("Stop", e => result.SetText("Stopped at " + speed.GetValue(e.Username).ToString("0"), e.Username));
    speed = hud.AddSlider("Speed", 0f, 100f, 50f, null, 1f);
    speed.SetSpan(2);
    result = hud.AddLabel("Press Start");
    result.SetSpan(2);

    MakeInteractable(click => hud.Show(click.Username));   // click this object to get the HUD
}

ScriptAPI / Panels

Panels

A panel is the world's own custom UI, separate from the player's personal HUD. Where a HUD is a window in the player's own HUD view, a panel is a surface the script places itself: as a screen overlay that sits behind the platform's own UI, or in world-space on a flat quad that stands in the scene and works in VR. A script builds a panel out of the same controls as a HUD — labels and counters, buttons, sliders, dropdowns, toggles, text fields, pictures and progress bars — plus a live texture of a painted object and an inventory grid, and shows it to the players it chooses (one, several or everyone; players who join later get it too). Panels work on desktop, phones and tablets, and in VR.

Every change shows at once for the players who have the panel. Each setter takes an optional username as its last parameter: left out (null), it changes the panel for everybody; with a username, for that player only. What players choose is their own and never changes anyone else's (GetValue(username), GetSelected(username), IsOn(username), GetText(username)), and every event comes back carrying who did it (PanelEvent.Username), so a server script can act for that player. Only the script that made a panel hears what players do with it. A script can have 8 panels at once (a panel up to 200 items); they go when the script stops (its object is removed) or the scene changes.

ScriptPanel CreatePanel(string panelId, int width = 360, int height = 0)

Makes a panel (width in pixels of its own surface; height 0 = as tall as its items need). Nobody sees it until Show. Creating one with an id that exists replaces that one.

ScriptPanel GetPanel(string panelId) / DestroyPanel(string panelId)

This script's panel with that id (null: none) / takes it away from everybody.

panel.SetMode(PanelMode mode)

Where it lives: PanelMode.Overlay (a flat window on the screen, behind the platform's UI) or PanelMode.WorldSpace (a window standing in the world; best for VR). Switch any time. A public PanelMode field is a dropdown in the Inspector.

panel.SetAnchor(PanelAnchor anchor, int offsetX = 24, int offsetY = 24)

Overlay. Hugs a corner or edge, nudged inward by the pixel offsets. PanelAnchor: TopLeft, TopCenter, TopRight, MiddleLeft, Center, MiddleRight, BottomLeft, BottomCenter, BottomRight. A public PanelAnchor field is a dropdown in the Inspector.

panel.SetScreenPosition(float xPercent, float yPercent, PanelAnchor pivot = TopCenter)

Overlay. Free placement: pins the panel's pivot point at xPercent of the screen width and yPercent of its height (0–100). E.g. SetScreenPosition(70, 6) sits it near the top, 70% across. SetAnchor switches back to corner-hugging.

panel.SetScreenScale(float scale) / SetModal(bool modal, float dim = 0.5f)

Overlay. SetScreenScale draws it bigger or smaller. SetModal floats it in the middle over a dimmed screen until it is closed.

panel.PlaceOnObject(SceneObject target, Vector3 localOffset = default, float widthMeters = 1.2f, bool billboard = false) / PlaceInWorld(Vector3 position, float widthMeters = 1.2f, bool billboard = true)

World-space. Float it above an object (it follows the object) or stand it at a spot, in metres; billboard turns it to face each viewer. Also SetWorldWidth(float) and SetBillboard(bool).

panel.Show(string username, bool open = true) / Show(UserInfo user, ...) / ShowToEveryone(bool open = true)

Gives the panel to a player ("*" = everybody; players who join later get it too).

panel.Hide(string username) / HideFromEveryone() / IsShownTo(string username) / Destroy()

Takes it away from a player or everybody (it can be shown again), whether a player has it now, or removes it for good.

panel.SetClosable(bool closable) / OnClose(Action<PanelEvent> callback)

Players can close a panel themselves (on by default); OnClose hears about it. A closed panel is hidden for that player until it is shown again.

panel.SetTitle / SetTitleColor / SetTitleStyle(Color, int size = 20, PanelAlign align = Center) / SetLogo(TextureItem, int height = 64)

A title across the top ("" = none) and a picture above it.

panel.SetBackgroundColor(Color) / SetBackgroundImage(TextureItem, PanelImageFit fit = Cover) / SetOpacity(float) / SetCornerRadius(int) / SetBorder(Color, int width = 2) / SetSize(int width, int height = 0)

The background (colours can be see-through), rounded corners, a border and the size. PanelImageFit: Cover, Contain, Stretch, Tile.

panel.SetFont(PanelFont font, int size = 14) / SetTextColor / SetButtonColors(Color text, Color background) / SetAccentColor(Color)

Lettering for the whole panel (PanelFont: Default, Sans, Serif, Mono, Rounded, Condensed, Heavy, Handwriting), the colours buttons start with, and the colour of sliders, toggles and progress bars.

panel.SetLayout(int columns = 1, int gap = 6, int padding = 12, PanelAlign align = Stretch)

Items go top to bottom, columns side by side per row. Also SetColumns, SetGap, SetPadding, SetAlign. PanelAlign: Left, Center, Right, Stretch.

PanelItem panel.AddLabel(string text) / AddCounter(string text, TextureItem icon)

Text, or a counter: a little icon with a label beside it (e.g. coins). Update with SetText / SetImage.

PanelButton panel.AddButton(string text, Action<PanelEvent> onClick = null) / AddButton(string text, TextureItem icon, ...)

A button, with a picture above its text if you give one. button.OnClick(callback).

PanelSlider panel.AddSlider(string label, float min, float max, float value, Action<PanelEvent> onChange = null, float step = 0)

A slider (step 0 = smooth). slider.GetValue(username), SetValue(value, username), SetRange(min, max, step), SetShowValue(bool), OnChange.

PanelDropdown panel.AddDropdown(string label, List<string> options, int selected = 0, Action<PanelEvent> onChange = null)

A list to pick from: clicking it opens the options and clicking one picks it (works on desktop, mobile and VR). GetSelected(username), GetSelectedText(username), SetSelected(index, username), SetOptions(...), OnChange.

PanelToggle panel.AddToggle(string label, bool on = false, Action<PanelEvent> onChange = null)

An on / off switch. IsOn(username), SetOn(on, username), OnChange.

PanelTextInput panel.AddTextInput(string placeholder = "", Action<PanelEvent> onSubmit = null, string text = "", int maxLength = 200)

A text field; onSubmit runs when the player presses Enter. GetText(username), SetValue(text, username), SetPlaceholder, SetMaxLength.

PanelItem panel.AddImage(TextureItem image, int height = 96) / AddLiveTexture(SceneObject paintedObject, int height = 160) / AddLiveTexture(string paintedObjectId, int height = 160)

A picture, or a live mirror of an object a script canvas (CreateCanvas) is painted on: it updates as the canvas is painted. item.SetCanvas(...) re-points it.

PanelProgressBar panel.AddProgressBar(float value = 0, string label = "")

A bar filled from 0 to 1, with optional text over it: bar.SetValue(value, username).

PanelInventory panel.AddInventory(int columns = 4, int cellSize = 72, Action<PanelEvent> onPick = null)

A grid of cells (icon + label + count badge). Fill it with grid.SetCells(IEnumerable<PanelCell>, username) (new PanelCell(label, icon, count)); clicking a cell fires onPick with Index (and Text = its label). Also SetColumns, SetCellSize, OnPick.

PanelItem panel.AddSpacer(int height = 8) / AddDivider() / GetItem(int id) / RemoveItem(item) / Clear() / Items

Empty space, a thin line, and managing items (item.Remove(), or Clear to remove all while the panel and its look stay).

Every item: SetText, SetEnabled, SetVisible, SetTextColor, SetBackgroundColor, SetColors, SetAccentColor, SetImage(TextureItem), SetImageSize(int pixels), SetSpan(int columns), SetHeight, SetWidth, SetAlign(PanelAlign), SetFontSize, SetBold, SetTooltip

All but SetSpan take an optional username. SetEnabled(false) greys it out. Each item also has Id, Kind (PanelItemKind), Panel and IsRemoved.

PanelEvent

What a player did: Username, UserId, User (their UserInfo), EventType (PanelEventType.Click, Change, Submit, Close), Panel, PanelId, Item, ItemId, and the new Value (slider), Index + Text (dropdown, inventory cell), IsOn (toggle) or Text (text field).

private ScriptPanel panel;

public override void Start()
{
    panel = CreatePanel("menu", 300);
    panel.SetMode(PanelMode.Overlay).SetAnchor(PanelAnchor.TopRight);
    panel.SetTitle("Menu").SetAccentColor(new Color(0.3f, 0.8f, 1f));
    panel.AddButton("Say hi", e => SendScriptChat(e.Username + " says hi!"));
    panel.AddSlider("Volume", 0f, 100f, 70f, e => Log(e.Username + " = " + (int)e.Value), 1f);
    panel.ShowToEveryone();   // or SetMode(PanelMode.WorldSpace) + PlaceInWorld(...) for VR
}

ScriptAPI / Quests

Quests

Quests are set up in the Scene Editor under Windows → Quests and saved with the scene; no script is needed. For each quest choose how players get it (when they arrive, from the Quests panel, by clicking an object, by walking into an area, or from a script), whether it asks first (Accept / Not now), its tasks (click or pick up objects, walk into areas, or done by a script, each with how many are needed), whether the tasks come all at once or one after another, and how it ends (at once, or by going back to an object or area). Objects can be shown only while their task is under way, and hidden for a player once they have used them. The scene's settings choose whether players' progress is remembered between visits and whether players may reset their own progress.

In the world, a Quests button shows on the control bar in scenes that have quests; it opens the Quests panel (top left) with the player's quests, their tasks and progress. The server runs every quest: what a click or an area means, and when a task or quest is done. Profiles (in the viewer and on the website) show how many quests a player completed of those they tried, across all worlds; on the website, members can hide this in Account settings.

Scripts name a quest or a task by its title (or the id shown in the Quests window), ignoring case. username "*" = every player in the world now.

OfferQuest(string username, string quest)

Offers the quest: the player sees it with Accept / Not now (a quest set not to ask starts at once). Nothing happens when they have it already, or finished it and it cannot be done again.

StartQuest(string username, string quest)

Starts it without asking.

AddQuestProgress(string username, string quest, string task, int amount = 1)

Adds to a task that is under way (for tasks "Done by a script"; any kind works). The task is done when it reaches the number needed; the quest when all its tasks are.

CompleteQuestTask(string username, string quest, string task)

Completes a task that is under way.

CompleteQuest(string username, string quest)

Finishes the whole quest for a player who has started it.

ResetQuest(string username, string quest)

As if the player never started it (their completed count on profiles stays).

QuestState GetQuestState(string username, string quest)

NotStarted, Offered, Active, ReadyToFinish (tasks done, to be finished at its object or area) or Completed. A public QuestState field is a dropdown in the Inspector; states can be compared (state >= QuestState.Active).

QuestInfo GetQuest(string username, string quest) / List<QuestInfo> GetQuests(string username)

A player's quest (null: no such quest) or all of the scene's quests for them. QuestInfo: Id, Title, State, IsActive, IsCompleted, Tasks and GetTask(string task). QuestTaskInfo: Id, Title, Progress, Needed, IsActive, IsDone.

ListenForQuests(Action<QuestEvent> callback) / ListenForQuest(string quest, Action<QuestEvent> callback)

Called for everything that happens with players' quests (any quest, or that one). QuestEvent: Username, UserId, User, QuestId, QuestTitle, TaskId / TaskTitle (task events), State (the quest's state now), Progress / Needed, IsTaskEvent, IsQuest(string), IsTask(string), and EventType: Offered, Declined, Started, TaskProgress, TaskCompleted, ReadyToFinish, Completed, Reset, or Joined: the player is in the world and their quests are known (once per quest) — the moment to set up what they see.

public string Quest = "Egg Hunt";

public override void Start()
{
    MakeInteractable(click => OfferQuest(click.Username, Quest));   // click me to get the quest

    ListenForQuest(Quest, quest =>
    {
        if (quest.EventType == QuestEventType.Completed)
            SendScriptChat(quest.Username + " finished " + quest.QuestTitle + "!");
    });
}

ScriptAPI / Water & Buoyancy

Water & Buoyancy

Make any object float on a Water System object and ride its waves — a boat, a crate, a buoy — and change the water’s waves from a script at runtime.

Waves are serialized for everyone. The wave surface is computed from the room’s shared, synced clock, so every player in the instance sees the same swell at the same moment. A buoyant object’s height and lean are computed on each viewer from those same waves, so it bobs and tilts in sync for everyone with no extra network traffic. Your script keeps control of the object’s horizontal position and heading (move it with SetPosition / SetRotation, or drive it); the water only sets its height and lean.

public float SinkDepth = 0.25f;   // shows in the Inspector

public override void Start()
{
    MakeBuoyant(SinkDepth);          // float this object on the water below it
}

Put buoyant objects at the top level of the scene (not inside a moved or rotated parent), over a Water System. Leave Use Rigidbody off for a floating object the script moves itself. The object floats on the Water System whose area is under it.

MakeBuoyant(float sinkDepth = 0, float bob = 1, float tilt = 0.6f, float responsiveness = 6)

Float this script’s object. sinkDepth: metres it sits below the surface line (higher = heavier, sits lower; negative rides on top). bob: how much it follows the wave height (1 = fully, 0.5 = a heavy ship). tilt: how much it leans with the wave slope (0 = stays flat). responsiveness: how quickly it settles (per second). Call it again to change the values.

MakeBuoyant(SceneObject obj, float sinkDepth = 0, …)

The same for another object (slot it as a SceneObject field).

StopBuoyancy() / StopBuoyancy(SceneObject obj)

Stop floating; the object keeps the position the script gives it.

SetWaterWaves(SceneObject water, bool enabled)

Turn the Water System’s waves on or off.

SetWaveHeight(SceneObject water, float metres), SetWaveSpeed(…, float speed), SetWaveLength(…, float metres), SetWaveDirection(…, float degrees), SetWaveSteepness(…, float 0–1)

Change the waves live. Applied on every viewer, and buoyant objects follow the new waves straight away.

SetWaterPreset(SceneObject water, string preset)

Apply a look preset: "Open Ocean", "Tropical Lagoon", "Calm Lake", "River", "Pool", "Swamp".

Runtime changes made with these calls apply to everyone in the room for the session; the scene’s saved water settings are unchanged. A full worked example (a boat that bobs and leans on the swell where you place it, with chat commands for calm and storm) is in the Examples section: BuoyantBoat.

ScriptAPI / Hoses

Hoses

Turn a mesh into a rubber hose between two objects: a garden hose from a reel to a nozzle, a fuel hose from a pump, a cable from a wall socket to a microphone. The hose hangs and sags under gravity, resists bending, stretches like rubber when pulled, and lies on and slides over the world’s colliders (the same surfaces players walk on, Mesh colliders included). The mesh is bent along the simulated hose every frame, so its texture, couplings and thickness follow the curve.

Synced for everyone with no extra traffic. Each viewer simulates the hose from its two end objects, which are already synced (an anchor, a hook, a nozzle a player is holding), so everyone sees it the same way. Players who join later get the hose with its current ends.

public SceneObject Hose;     // the hose mesh, modelled straight
public SceneObject Reel;     // where it starts
public SceneObject Nozzle;   // Grabbable: the end follows the player's hand

public override void Start()
{
    MakeHose(Hose, Reel, Nozzle);
}

The mesh: model the hose straight, along one axis (it uses the longest one, or HoseSettings.Axis), at the length you want. The more rings along its length, the smoother it bends. The model’s end nearest the start object becomes the start. Turn Enable Collision off on the hose object: the hose collides with the world, the world does not collide with the hose. To hang the end on a grabbed object, slot that object (a nozzle with Grabbable on) as the end; an offset puts the end on a point of it (in its own local space).

MakeHose(SceneObject hose, SceneObject start, SceneObject end, HoseSettings settings = null)

Start simulating hose between start (the anchor) and end. Call it again to change the settings or the ends. An end that is not set (or not found) hangs free.

MakeHose(SceneObject hose, SceneObject start, Vector3 startOffset, SceneObject end, Vector3 endOffset, HoseSettings settings = null)

The same, with each end on a local point of its object (e.g. the back of the nozzle, the spout of a tap).

SetHoseStart(SceneObject hose, SceneObject start) / SetHoseStart(SceneObject hose, SceneObject start, Vector3 offset)

Move the start end to another object.

SetHoseEnd(SceneObject hose, SceneObject end) / SetHoseEnd(SceneObject hose, SceneObject end, Vector3 offset)

Move the other end to another object: from a hook to the nozzle a player picked up, and back.

DetachHoseEnd(SceneObject hose)

Let go of the end: it drops and hangs from the start, or lies on the ground.

StopHose(SceneObject hose)

Stop simulating; the mesh goes back to its modelled shape.

SpinHose(SceneObject hose, float turnsPerSecond, float phaseDegrees = 0, string facingUsername = "", float strength = 0.6f)

Turns the hose like a jump rope around the line between its two ends: it is driven around a turning loop, the same on every viewer, and is still a real rope (it bends, lies on and slaps the floor at the bottom, slides over it). turnsPerSecond > 0 = forward (over the head, down in front, under the feet, up behind); < 0 = backward. phaseDegrees: where it starts: 0 = straight up (overhead), 90 = in front, 180 = under the feet, 270 = behind. facingUsername: “in front” is that player's facing (the jumper); "" = the side to the right of start → end. strength 0–1: how hard the rope is pulled onto the loop each step (1 = exactly on it; 0.5–0.6 = looser, more natural). The loop is sized from the rope's length and stays at least 0.7 m wide (hands crossed). Timing: while the facingUsername player has an emote playing, the rope's clock is that emote's playback time (angle = phaseDegrees + 360 · turnsPerSecond · emote time), so it is locked to the jump on every screen, also while the emote downloads at a different speed on each. Make each jump-rope clip hold a whole number of turns (a 0.5 s clip at 2 turns a second) and its loop wraps seamlessly. With no emote playing (before it has loaded, or no facing player), the angle counts from when that viewer received the command; switching between the two eases over 0.15 s. Call it again to switch speed or phase. Players who join later get it at the right angle. Also SpinHose(hose, tps, phase, UserInfo facing, strength).

StopHoseSpin(SceneObject hose)

Stops turning: the hose is plain hanging physics again and keeps its speed, so it flops down naturally.

new HoseSettings { … }

Length (metres; 0 = automatic: the model’s length, or Slack × the gap between the ends if that is longer) · Slack (default 1.3: how much longer than the gap between its ends the hose is, so it hangs and curls; 1 = taut) · Radius (metres, 0 = from the model) · Segments 4–96 (smoothness, default 24) · Stiffness 0–1 (resists bending, default 0.3; 0 = limp rope, 1 = stiff pipe) · Stretch 0–1 (how rubbery when pulled, default 0.3: pulled past its length it stretches like rubber and still sags with its own weight, going taut only at MaxStretch; 0 = does not stretch, pulls straight) · MaxStretch (how far it may stretch at most, default 2 = double) · Gravity (1 = normal) · Damping 0.9–1 (lower settles faster, default 0.985) · Friction 0–1 on the ground (default 0.6) · Collision (default true) · Axis "auto"/"x"/"y"/"z" · StartDirection / EndDirection: a local direction the hose leaves that end in (Vector3.zero = free), e.g. new Vector3(0, 0, -1) = out of the back of a nozzle.

// A jump rope: the rope is a hose between two held handles; each move plays an emote and turns the rope in step.
MakeHose(Rope, LeftHandle, HandleTip, RightHandle, HandleTip, new HoseSettings { Length = 2.4f, Segments = 32, Stretch = 0.05f });
PlayPlayerEmote(jumper, basicBounce);
SpinHose(Rope, 2f, 0f, jumper, 0.6f);   // 2 turns a second, starting overhead, "in front" = the jumper's front
// ...later
StopPlayerEmote(jumper);
StopHoseSpin(Rope);                     // the rope falls and settles

A full worked example (a reel, a grabbable nozzle that goes back on its hook when let go, and chat commands to drop, re-attach and tune the hose) is in the Examples section: GardenHose.

ScriptAPI / Data Store

Persistent Data Store

Save values that survive world restarts — scores, counters, flags, settings — in a small database on the server. You pick a key (any string) and store a value under it; later you load it back. Values are stored as text and the typed Load… helpers parse them for you. Everything runs server-side, so the keys and values never reach any player’s browser.

Keys are global across the platform, so make yours unique — prefix every key with a private string that other creators can’t guess. Declare that prefix as a [Secret] field so it is stored only on the server and never ships inside your downloadable scene:

[Secret] public string DataKey = "my-unique-prefix";
// ... then key everything under it:
SaveData(DataKey + ":highscore", 1200);
LoadInt(DataKey + ":highscore", 0, score => Log("High score: " + score));

This is not a hard security boundary (anyone who learns your exact key could read or overwrite it), but the [Secret] prefix keeps your keys off the player’s browser and out of copied scenes. Calls are asynchronous: the onDone / onValue callback runs when the database replies.

SaveData(string key, string value, Action<bool> onDone = null)

Store a value under key. Overloads take int, long, float, double and bool too. onDone(true) when it was written. Keys up to 255 characters; values up to about 1 MB.

LoadData(string key, Action<string> onValue)

The stored text, or "" if the key was never saved. Also LoadData(key, string fallback, onValue) to choose what you get when it is missing.

LoadInt(string key, int fallback, Action<int> onValue)

The value parsed as a whole number, or fallback if it is missing or unparseable. Likewise LoadLong, LoadFloat, LoadDouble and LoadBool.

DeleteData(string key, Action<bool> onDone = null)

Remove a key from the store.

A full worked example (a persistent world-wide click counter) is in the Examples section: DataStoreCounter.

ScriptAPI / Advanced

Advanced Helpers

EmitClientCommand(string command, string payloadJson, string targetUsername = null)

Queues a raw client command for this script object's network id.

EmitClientCommand(SceneObject targetObject, string command, string payloadJson, string targetUsername = null)

Queues a raw client command for a target object id.

Log(string msg)

Writes a normal log message prefixed with the script name.

LogWarning(string msg)

Writes a warning prefixed with the script name.

LogError(string msg)

Writes an error prefixed with the script name.