Pages

Banner 468

Saturday 4 June 2011

LSL - Objects that Respond to Commands

0 comments
 
Welcome back. Today’s post is all about the Linden Scripting Language and how we can use LSL to make objects respond to simple commands. As with any form of programming, the best way to explain LSL concepts is through practical examples, so I've chosen to build a simple elevator. The elevator will respond to “Touch” events by move vertically between pre-defined minimum and maximum z-axis values.

In order to focus on the actual scripting, I will not spend too much time building and fiddling with the design of the elevator. A simple rectangular platform will do nicely for this example.

Adding a Script

The first thing to do is to add an actual script to our platform. To do this, go to the “Content” tab of the build toolbox and click the “New Script” button. Double-click the newly added script object to open the script editor which should contain the following default script:

default
{
   state_entry()
   {
      llSay(0, "Hello, Avatar!");
   }

   touch_start(integer total_number)
   {
      llSay(0, "Touched.");
   }
}  

States and Events

LSL is an event-driven language based on finite state machines. This means that every object in second life can have a pre-defined number of states and can transition from one state to another by reacting to events. All objects must have at least one state, the default state which is was we can see in the listing above – defined by the ‘default’ keyword. This script also contains two event handlers: ‘state_entry’ and ‘touch_start’. ‘State_entry’ is called whenever the object enters the current (in this case default) state, which in turn calls the LSL pre-defined ‘llSay’ function which basically broadcasts the “Hello Avatar!” message on channel 0, the public channel. On the other hand, ‘touch_start’ is triggered whenever the object is touched while in the default state, broadcasting “Touched” on the public channel.

Building our Elevator script


Going back to our example, our elevator can be in either one of two states: stationary (default) or moving. We want our elevator to start moving when it’s touched and we want it to stop moving when it gets to the target position. Here’s the basic skeleton for our script:

default
{
   state_entry()
   {
      //Determine the next Target position based on the current position
   }
   
   touch_start(integer total_number)
   {
      //Go to the moving state
   }
}

state moving
{
   state_entry()
   {
      //Start Moving and stop when target is reached
   }
}

We need four global variables to store values for the minimum and maximum Z-axis positions, the speed at which the elevator should move and the target position (up or down). These variables are placed at the very top of the script and given their default values. When entering the default state we want to set the target position according to the current position. In other words, if the elevator is currently at the bottom, the target will be the top and vice-versa. We also want the elevator to enter the ‘moving’ state when an avatar touches it:

integer MinZ = 23;  // Minimum Z position (bottom)
integer MaxZ = 27;  // Maximum Z position (top)
integer DeltaZ = 1; // Rate of change of Z (speed)
integer TargetZ;    // Target  Z position

default
{
   state_entry()
   {
      vector position = llGetPos();
      if (position.z >= MaxZ) 
      {
         TargetZ = MinZ;
      }
      else
      {
         TargetZ = MaxZ;
      }
   }
   
   touch_start(integer total_number)
   {
      //Go to the moving state
      state moving;
   }
}

state moving
{
   state_entry()
   {
      //Start Moving and stop when target is reached
   }
}

At line 10 the script calls the llGetPos() function that returns the current position of our object as a vector. A variable of type vector holds three floating point numbers for X, Y and Z values which in this case are our object’s position in 3D space (in metres). The current Z position is then used to determine the elevator’s next target position.

Animating Movement

In the moving state we could of course set the elevator’s position to the current target but that would not be very realistic. An elevator does not simply jump from one floor to another, it gradually moves between floors until the target is reached. To replicate this effect we need some sort of animation to smoothly transition the elevator from the current position to the target. There are multiple ways of doing this but I decided to use a timer event to control movement. Let’s have a look at the code for the moving state:

state moving
{
   state_entry()
   {
      llSetTimerEvent(0.1);
   }
    
   timer()
   {
      vector position = llGetPos();
        
      if (position.z < TargetZ) //Moving Up
      {
         if (position.z + DeltaZ > TargetZ)
         {
            position.z = TargetZ;
         }
         else
         {
            position.z = position.z + DeltaZ; 
         }
      }
      else // Moving down
      {
         if (position.z - DeltaZ < TargetZ)
         {
            position.z = TargetZ;
         }
         else
         {
            position.z = position.z - DeltaZ;
         }
      }        
    
      llSetPos (position);  //Set the new position        
                
      if (position.z == TargetZ) // Target reached
      {
         llSetTimerEvent(0);  //Cancel the timer
         state default;            // Goto the default state
      }
        
    }
}

This code might look slightly complex but in actual fact it’s fairly straight forward. When the object enters the moving state it calls the ‘llSetTimerEvent()’ function to trigger a timer event every tenth of a second. The timer event then does the actual movement. First it gets the object’s current position with the ‘llGetPos()’ function which is then compared to the target. If the current Z position is less than the target it means that the elevator is moving upwards and vice versa. Depending on the direction of movement, the value of ‘DeltaZ’ – the rate of change of vertical position (speed) – is added or subtracted from the current position. The script also makes sure that the maximum and minimum positions are not exceeded. After calculating the new position, the ‘llSetPos()’ function is called to ‘move’ the elevator to the new position. When the elevator reaches its target, the timer is cancelled and the object is moved back to the default state. And that’s it. Now, when the elevator is touched it will gradually move to its destination where it will stop until it is touched again to bring it back.

Further Improvements

Of course there are lots of ways to improve the script. For instance floating point numbers could be used for DeltaZ to make the animation smoother. The elevator itself could be designed much better rather than a simple platform by linking various prims together and use messaging to make them move as a single unit. I’ll be looking into messaging and linking in a future post where we’ll see how we can create a call button for our elevator.

Conclusion

Although building and scripting is one of the most enjoyable things in Second Life (for me at least) it can also be very, very frustrating at times. Given that scripts are compiled and run on the server, each time you modify a script it could take a while for the change to take effect depending on the amount of lag being experienced. When testing a script you usually need to continuously correct or tweak the code and having a bad response time can make this a rather frustrating experience. Also, the risk of being disconnected from second life without warning is also there (happened to me a couple of times) so I had to resort to writing code on a local text-editor and pasting it into Second Life to make sure I always had a backup I could fall back to. Although I have only begun to scratch the surface, it is already apparent that scripting in second life can be pretty powerful.

Leave a Reply