#21. My Second Life: Scripting a sliding door

A more up-to-date version of this entry is here.

I don’t know about you, but the sliding doors in my house don’t automatically open and close themselves. This is possible, though, and you may have seen automatic doors that open when you step on a mat as you approach. Long before this was practical, I remember seeing the doors on the Starship Enterprise of Star Trek open and close with those classic whooshing sounds. Since Second Life eerily allows you to replicate science fiction, it’s only natural we can do something similar in that virtual world.

Clicking an SL sliding door

What we do need is to build a door and then add a little bit of scripting. “Scripting” means that we will be writing a simple bit of software that controls what happens when we click on the door or, as I often do, walk into it. This is the first time I’ve looked at scripting at any depth in this series, so we’ll take it slowly and iterate several times as we add functionality. First, let’s think about what we want to happen and then we’ll see how to do it.

The sliding door I’m imagining exists in two states: “open” and “closed.” Now we could leave a real door partially open (or partially closed, if you are a pessimist), but I want to keep this simple now. The door moves back and forth between these two states.

I’ll assume that the closed state is the initial position. That is, when we first install the door, it will be closed. If we click it, the door moves to the open state and the new physical location. That new location may be hidden inside a wall or otherwise out of sight, so it is important that the door automatically closes itself after some time has passed.

With this little bit of analysis, we’ve identified several bits of information we need to track. All distances are in meters and all times in seconds, expressed as decimals. For example, 4.5 m or 3.0 sec.

  • The initial position of the door when it is closed, in three dimensions.
  • The position of the door when it is open, in three dimensions.
  • The amount of time to wait, in seconds, before having the door automatically close itself.

One of the wonderful things about programming computers is that there are often multiple ways of doing the same thing, and some of them make subsequent actions easier. We could just as well say we’re going to track the following information.

  • The initial position of the door when it is closed, in three dimensions, gotten from wherever the door was installed.
  • The distance to move the door in each of three dimensions when we move it from the closed to the open positions.
  • The amount of time to wait, in seconds, before having the door automatically close itself.

This is easier because we can just ask what the initial position of the door is rather than entering it manually. Then we move the door to the new position by adding the “delta” distances to the original position. After “delay” seconds, we move the door back to the saved initial position.

Note that I’m allowing a general case here by using three dimensions for the delta distances. Think of these for the moment as west-east, south-north, and down-up. If I only move the door in one of these directions, the other values will be 0.0.

The sliding door to one bedroom might be along the east-west axis while the door in another bedroom might be north-south. Moreover, I might decide to move the door diagonally up and to the right, say. It’s simplest if we build this using all three dimensions, even if we only move it in one for a particular door.

The programming language for Second Life is called LSL, the Linden Scripting Language. It’s quite powerful and has features that allow all sorts of fancy actions to allow you to control objects. You can make sounds play, change the textures on objects, treat objects as physical projectiles that fall to earth, generate astounding particles, animate avatars, and conduct business transactions, just to start. Your primary source of information about LSL is the official wiki. Always look there before asking what may be a trivial question inworld or on a forum.

Scripting a sliding door is at the low end of LSL’s capabilities, but we could get fancy beyond what we’ve designed above. For example, maybe only certain people should be able to open the door. Perhaps a light should go on when the door is open or closed. Maybe different sounds should play or fireworks should go off as the door moves in either direction. A lot is possible, but let’s get the basics down.

LSL explicitly has an idea of states so that we could say “when in the closed state, do A when B happens, but when in the open state, do C when B happens.” Not everyone likes to program this way and it gets tricky when things get very complicated. I’m going to use LSL states for this example, but we could very well have designed things to just keep track of a variable door_is_closed that has a value of TRUE or FALSE.

Let’s look at some basic LSL datatypes we’ll need in this example. A float is a decimal value like 3.4, 0.0, or -5.67. A vector represents three floats that are the X, Y, and Z coordinates of the center of an object or else represents a value (such as our movement delta above) with components in each of these directions. In the Second Life editing scheme, the X value represents the west-east axis, is shown with a red arrow, and larger values are more east. The Y value represents the south-north axis, is shown with a green arrow, and larger values are more north. The Z value represents the down-up axis, is shown with a blue arrow, and larger values are more up.

In our script for moving the sliding door, we’ll have the following:

float  delay = 3.0;             // time to wait before automatically closing door
vector delta = <1.0, 0.0, 0.0>; // amount to move door when we open it

Everything on a line after the // symbols is a comment and is ignored when processing the script.

The delay variable holds the number of seconds that the system will wait before closing the door. The delta variable holds a vector of three values representing how much we should move the door when we open it from its original closed position. In this example, we will move the door 1.0 meters in the easterly direction, and only in that direction (the other directions have 0.0 displacement values). If it had the value -1.0, the door would move along the same axis, but to the west.

We also need:

vector closed_position;         // original position of the door when closed

The LSL system will give us this value when we ask for it in the script. Here is our first version of the script:

// Sliding door LSL script #1
// Handles the touch event.

float  delay = 3.0;             // time to wait before automatically closing door
vector delta = <1.0, 0.0, 0.0>; // amount to move door when we open it

vector closed_position;         // original position of the door when closed

// Processing for the script when it first starts up

default {
    // What we do when we first enter this state

    state_entry() {
        closed_position = llGetPos();      // Save position when door is closed
        state closed;                      // Move to the closed state
    }
}

// Processing for the script when it is in the closed state

state closed {
    // What we do when we first enter this state

    state_entry() {
        llSetPos(closed_position);         // Move door to closed position
    }

    // What we do when the door is clicked ("touched") with the mouse

    touch_start(integer total_number) {
        state open;                        // Move to the open state
    }
}

// Processing for the script when it is in the open state

state open {
    // What we do when we first enter this state

    state_entry() {
        llSetPos(closed_position + delta); // Move door to open position
    }

    // What we do when the door is clicked ("touched") with the mouse

    touch_start(integer total_number) {
        state closed;                      // Move to the closed state
    }
}

When the script starts, we save the original position and then move into the closed state. When the closed state begins, we move the door to the closed position. This is not necessary the very first time, but is subsequently. When the closed door is touched, that is, clicked with the mouse, we move to the open state. When the open state begins, we move the door to the original position plus the change amounts in each direction (here it is just 1.0 meters eastward). If the open door is clicked, we move to the closed state, and the door is moved back to the closed position. And so on and so on.

We used llGetPos to get the position of the door in the closed state and llSetPos to set a new position for the door. This could be the open position from the closed one, or vice-versa. Follow the links to the LSL wiki entries for these functions if you wish to learn more.

The whole idea is to just keeping moving from one state to another when events happen. The only event we’ve seen here is the “touch” event. The door alternately opens and closes as you click it. Let’s add one more event, the “collision” one. This fires when something hits the door, probably you. If the door is closed, it should open. However, if you happen to hit an open door it should stay open.

// Sliding door LSL script #2
// Handles the touch event.
// Handles the collision event.

float  delay = 3.0;             // time to wait before automatically closing door
vector delta = <1.0, 0.0, 0.0>; // amount to move door when we open it

vector closed_position;         // original position of the door when closed

// Processing for the script when it first starts up

default {
    // What we do when we first enter this state

    state_entry() {
        closed_position = llGetPos();      // Save position when door is closed
        state closed;                      // Move to the closed state
    }
}

// Processing for the script when it is in the closed state

state closed {
    // What we do when we first enter this state

    state_entry() {
        llSetPos(closed_position);         // Move door to closed position
    }

    // What we do when the door is clicked ("touched") with the mouse

    touch_start(integer total_number) {
        state open;                        // Move to the open state
    }

    // What to do when something hits the door 

    collision_start(integer total_number)
    {
        state open;                        // Move to the open state
    }
}

// Processing for the script when it is in the open state

state open {
    // What we do when we first enter this state

    state_entry() {
        llSetPos(closed_position + delta); // Move door to open position
    }

    // What we do when the door is clicked ("touched") with the mouse

    touch_start(integer total_number) {
        state closed;                      // Move to the closed state
    }

    // What to do when something hits the door 

    collision_start(integer total_number)
    {
        // Do nothing, the door is already open
    }
}

The instance of collision_start in the closed state moves us to the open one. The instance in the open state does nothing since the door is already open. We could skip this latter instance, but I’m including it for clarity and to explicitly show what happens. Later on we might want to add some behavior here such as a message that says “Stop walking into this door!”. You could do that via:

llSay(0, "Stop walking into this door!");

Attentive readers may have noted that I haven’t used the delay value to automatically close the door. We’ll add this now. This will involve another kind of event, a timer one:

llSetTimerEvent(delay);

Every delay seconds a timer event will happen (think of an alarm going off). We’ll want to do something when this happens, possibly canceling future alarms. We would cancel them via

llSetTimerEvent(0.0);

Here’s the process logic: when the door is first opened, we set a timer event to happen every delay seconds. When it fires, we will close the door if it is not already closed. In any case, we want to cancel future firings. If we clicked and closed the door before the timer went off for the open door, there is no need to anything other than the cancelation.

// Sliding door LSL script #3
// Handles the touch event.
// Handles the collision event.
// Handles closing the door automatically via a timer event.

float  delay = 3.0;             // time to wait before automatically closing door
vector delta = <1.0, 0.0, 0.0>; // amount to move door when we open it

vector closed_position;         // original position of the door when closed

// Processing for the script when it first starts up

default {
    // What we do when we first enter this state

    state_entry() {
        closed_position = llGetPos();      // Save position when door is closed
        state closed;                      // Move to the closed state
    }
}

// Processing for the script when it is in the closed state

state closed {
    // What we do when we first enter this state

    state_entry() {
        llSetPos(closed_position);         // Move door to closed position
    }

    // What we do when the door is clicked ("touched") with the mouse

    touch_start(integer total_number) {
        state open;                        // Move to the open state
    }

    // What to do when something hits the door 

    collision_start(integer total_number)
    {
        state open;                        // Move to the open state
    }

    // What to do when the timer goes off

    timer()
    {
        llSetTimerEvent(0.0);              // Set the timer to 0.0 to turn it off
    }
}

// Processing for the script when it is in the open state

state open {
    // What we do when we first enter this state

    state_entry() {
        llSetPos(closed_position + delta); // Move door to open position
        llSetTimerEvent(delay);            // Set the timer to automatically close it
    }

    // What we do when the door is clicked ("touched") with the mouse

    touch_start(integer total_number) {
        state closed;                      // Move to the closed state
    }

    // What to do when something hits the door 

    collision_start(integer total_number)
    {
        // Do nothing, the door is already open
    }

    // What to do when the timer goes off

    timer()
    {
        llSetTimerEvent(0.0);             // Set the timer to 0.0 to turn it off
        state closed;                     // Move to the closed state
    }
}

There are some system sounds we can use for when the door opens and closes:

key    open_sound  = "cb340647-9680-dd5e-49c0-86edfa01b3ac";
key    close_sound = "e7ff1054-003d-d134-66be-207573f2b535";

We can also control the volume:

float  volume = 0.5;            // 0.0 is off, 1.0 is loudest

Finally, we can cause the sound to be heard via llTriggerSound:

llTriggerSound(open_sound, volume);

If you are lagging, the sound may not be heard for several seconds, if at all. Here’s the final version of our script:

// Sliding door LSL script #4
// Handles the touch event.
// Handles the collision event.
// Handles closing the door automatically via a timer event.
// Triggers sounds when the door opens or closes.

// Parameters you might want to change

float  delay = 3.0;             // time to wait before automatically closing door
vector delta = <1.0, 0.0, 0.0>; // amount to move door when we open it
float  volume = 0.5;            // 0.0 is off, 1.0 is loudest

// Variables you will most likely leave the same

vector closed_position;         // original position of the door when closed

key    open_sound  = "cb340647-9680-dd5e-49c0-86edfa01b3ac";
key    close_sound = "e7ff1054-003d-d134-66be-207573f2b535";

// Processing for the script when it first starts up

default {
    // What we do when we first enter this state

    state_entry() {
        closed_position = llGetPos();      // Save position when door is closed
        state closed;                      // Move to the closed state
    }
}

// Processing for the script when it is in the closed state

state closed {
    // What we do when we first enter this state

    state_entry() {
        llTriggerSound(close_sound, volume); // Trigger the sound of the door closing
        llSetPos(closed_position);           // Move door to closed position
    }

    // What we do when the door is clicked ("touched") with the mouse

    touch_start(integer total_number) {
        state open;                        // Move to the open state
    }

    // What to do when something hits the door 

    collision_start(integer total_number)
    {
        state open;                        // Move to the open state
    }

    // What to do when the timer goes off

    timer()
    {
        llSetTimerEvent(0.0);              // Set the timer to 0.0 to turn it off
    }
}

// Processing for the script when it is in the open state

state open {
    // What we do when we first enter this state

    state_entry() {
        llTriggerSound(open_sound, volume);// Trigger the sound of the door opening
        llSetPos(closed_position + delta); // Move door to open position
        llSetTimerEvent(delay);            // Set the timer to automatically close it
    }

    // What we do when the door is clicked ("touched") with the mouse

    touch_start(integer total_number) {
        state closed;                      // Move to the closed state
    }

    // What to do when something hits the door 

    collision_start(integer total_number)
    {
        // Do nothing, the door is already open
    }

    // What to do when the timer goes off

    timer()
    {
        llSetTimerEvent(0.0);             // Set the timer to 0.0 to turn it off
        state closed;                     // Move to the closed state
    }
}

There are several ways for you to get this script onto a door object. The first is to go to your Inventory and from the top menu item select Create > New Script. You’ll see something called “New Script” below (you may have to look in the Scripts subdirectory). Right click and rename that to “Sliding door script #4″. Open up the script and replace the contents with the script text above.

Sliding door script in the LSL editor

Now you can drag and drop that onto anything you want to have the sliding door behavior. Try creating a generic cube on the ground and adding the script. To create the cube, right click on the ground, click Create, make sure the cube is highlighted in the dialog box, and then click on the ground where you want the cube. Drag and drop the script to the cube. Open up the Content tab on the Edit menu for the cube, right click the script, and then either Save or Reset the script. Click the cube, kick the cube, and otherwise watch it open and close per the behavior we programmed above.

A second way is to start with a cube, right click and go to Edit mode. Choose the Content tab and press the New Script button. Again, rename the script as above and open it by double-clicking on it. Copy the script above over what is the default script text. Save the script. Click, kick, as above.

If you move the “door” you must reset the script! This is very important and is often forgotten. If you modify the script by changing delta or delay, make sure you save the script. Also ensure that the Running box is checked.

The only thing left is for you to make the door the size you want and texture it to look like the door you need. This could be a front door, a car door, or even a space station door. Experiment with delta and delay to get the behavior you want. Learn more about LSL by looking at the wiki.

In future entries I’ll look at rotating doors and the texturing process.

Also see: Building, Building a basic door, Scripting a swinging door, with linking

Next: Building a basic door
Previous: How to fly high
All entries in this series


This entry was posted in Virtual Worlds and tagged , , , . Bookmark the permalink.

One Response to #21. My Second Life: Scripting a sliding door

  1. Jerome Davies says:

    Bob,

    this is completely off topic, you may have heard this already, but apparently the scientific publications “Science” and “Nature” have problems with OOXML.

    http://www.robweir.com/blog/2007/04/math-markup-marked-down.html

Comments are closed.