Freebie: Hinged door opener 1.3

So far I haven't seen any functional doors in my explorations. I decided to create a door opener script (set).

Sorry about the indentation being stripped out of the code samples here. If you paste into Visual Studio, it should fix it.

*** NOTE: This is old code now. Please use it only for inspiration on how to do common things in scripting. Please use the latest free door scripts found in the marketplace ***

"Door opener.cs" [this is the old code, not the latest version]

Purpose: Door opener
Created: 7/17/2017 by Galen

Drop me in an object to be used as a door. Then configure my public
settings to control how the door behaves. You most likely need to enabe
OpenOnCollision and set HingeOffsetX to something like -0.5, but the
other settings can be left blank (or zero) and they'll auto-configure.

You are free to hack and use this free script in any way you wish, but
I would appreciate credit. I offer no warranty or service.

Note that this is quite the hack. I fully expect LL to put out hinges
and other features that will outmode this door script fairly quickly.
But I hope this also demonstrates a number of techniques in scripting
for Sansar.


using System;
using Sansar;
using Sansar.Script;
using Sansar.Simulation;
using System.Collections.Generic;

// To get full access to the Object API a script must extend from ObjectScript
public class DoorOpener : SceneObjectScript {

/************************ User-configurable settings ************************/

// A user bumping into this door will not trigger it to open unless this is true
public bool OpenOnCollision;

// A comma-delimited list of names of remote sensors to listen to that trigger opening
public string SensorNames;

// If the origin is not where the door hinge is, this points to it (e.g., -0.5 on a 1m wide door with center pivot)
public float HingeOffsetX;

// If the origin is not where the door hinge is, this points to it
public float HingeOffsetY;

// How far to open the door, measured in degrees (default = 90 degrees)
public int OpenAngleDegrees;

// If true, the door opens out instead of in (with a left-hinged door, out is toward you)
public bool OpenOut;

// How many seconds should it take to open the door (default = 1.0 seconds)
public float SecondsToOpen;

// How many seconds the door will remain open after it started opening (default = 5 seconds)
public int SecondsBeforeClose;

// What sound to trigger when the door starts opening (optional)
public SoundResource OpenSound;

// What sound to trigger when the door starts closing (optional)
public SoundResource CloseSound;

// What channel to listen to remote sensors on (default = 1000)
public int Channel;

// Which rigid body in this object to manipulate (default = first one)
public RigidBodyComponent DoorBody;

/************************ Private variables ************************/

private List<string> SensorNameList = new List<string>();
private Vector ClosedPosition;
private Quaternion ClosedOrientation;
private bool Opening;
private DateTime LatestOpenSignal;
private float OpenAngle;
private Vector HingeOffset;

// Override Init to set up event handlers and start coroutines.
public override void Init() {

// Sensible default setting values
if (OpenAngleDegrees == 0) OpenAngleDegrees = 90;
if (Channel == 0) Channel = 1000;
if (SecondsToOpen == 0) SecondsToOpen = 1;
if (SecondsBeforeClose == 0) SecondsBeforeClose = 5;

// Parse the pipe-delimited sensor names into a clean list
if (SensorNames != null) {
foreach (string Name in SensorNames.Split(',')) {
if (Name.Trim() == "") continue;

// Find the door we'll manipulate.
if (DoorBody == null) ObjectPrivate.TryGetFirstComponent(out DoorBody);

// Compute some basics
OpenAngle = OpenAngleDegrees / Mathf.DegreesPerRadian;
ClosedOrientation = DoorBody.GetOrientation();
HingeOffset = new Vector(HingeOffsetX, HingeOffsetY, 0, 0).Rotate(ref ClosedOrientation);
ClosedPosition = DoorBody.GetPosition() + HingeOffset;

// Only subscribe to collision events if the experience creator requested it
if (OpenOnCollision) DoorBody.Subscribe(CollisionEventType.cCharacterContact, CollisionEvent);

// Only listen for chat if the experience creator wants outside sensors to trip this
if (SensorNames != null) ScenePrivate.Chat.Subscribe(Channel, null, ChatMessage);

// Start a continuous loop updating the door's position and orientation


private void CollisionEvent(CollisionEventType EventType, Sansar.Script.ComponentId ComponentId, Sansar.Script.ComponentId HitComponentId, ObjectPublic HitObject) {

if (!Opening) {
Opening = true;
if (OpenSound != null) ScenePrivate.PlaySound(OpenSound, PlaySettings.PlayOnce);
LatestOpenSignal = DateTime.Now;


private void ChatMessage(int Channel, string Source, SessionId SourceId, ScriptId SourceScriptId, string Message) {

// Parse the message
string[] Parts = Message.Split('|');
if (Parts.Length < 3) return;

// Wrong kind of message
if (Parts[0] != "Sensor") return;

// Is the message from one of the sensors we're listening to?
if (!SensorNameList.Contains(Parts[1])) return;

if (Parts[2] != "Collision") return;

if (!Opening) {
Opening = true;
if (OpenSound != null) ScenePrivate.PlaySound(OpenSound, PlaySettings.PlayOnce);
LatestOpenSignal = DateTime.Now;


private void UpdateLoop() {

float Tick = 0.01f;
float AngleStep = 5 * OpenAngle * Tick / SecondsToOpen;

TimeSpan Moment = TimeSpan.FromSeconds(Tick);

float Angle = 0.0f;

while (true) {

// If we're in the process of opening or closing, we'll adjust the angle a little toward the target
if (Opening) {

if (OpenOut) {
Angle -= AngleStep;
if (Angle < -OpenAngle) Angle = -OpenAngle;
} else {
Angle += AngleStep;
if (Angle > OpenAngle) Angle = OpenAngle;

// If it's been a while since we've heard an open signal, let's start closing the door
if (DateTime.Now.Subtract(LatestOpenSignal).TotalSeconds >= SecondsBeforeClose) {
Opening = false;
if (CloseSound != null) ScenePrivate.PlaySound(CloseSound, PlaySettings.PlayOnce);

} else {

if (OpenOut) {
Angle += AngleStep;
if (Angle > 0f) Angle = 0f;
} else {
Angle -= AngleStep;
if (Angle < 0f) Angle = 0f;


// Compute the new position and rotation of the door
Quaternion Rotation = Quaternion.FromEulerAngles(new Vector(0, 0, Angle, 0));
Quaternion NewOri = ClosedOrientation * Rotation;
Vector NewPos = ClosedPosition - HingeOffset.Rotate(ref Rotation);

// Update the door's position and orientation


private void MessageAllUsers(string message) {
foreach (var Agent in ScenePrivate.GetAgents()) {
try {
} catch (Exception) { }

private Vector QuaternionToVector(Quaternion Q) {
return new Vector(



Tomorrow I'll post the code for the floor sensor to go with it and details on usage.



Please sign in to leave a comment.