My Pages

Wednesday, 8 June 2011

Second Life - Advanced Scripting


Introduction

This week’s post is my personal favourite since a lot of scripting will be taking place. Last week’s blog entry focused on primitives which are the building blocks of Second Life and introduced Linden Scripting which as I like to call it the heart and soul of Second Life mainly because it defines behaviour to all entities within this virtual world. This week we will go into the different facades of Linden Scripting and demonstrate each one in some detail. I would like to divide this post into four sections each of which will focus on to different areas of Linden Scripting.
  • Scripting a Teleporter
  • Communications
  • Creating objects
  • Inventory

Task Summary

This is an overview of the tasks that will be taking place in the sections below.

TaskSummary
TeleportingCreate a short distance teleporter and an Intrasim teleport.
CommunicationCreate object to object messaging, Prim to prim within an object using link messages and long distance communications using email and browsing web pages.
Creating objectsCreate a temporary rezzer
InventoryCreate a Notecard giver


Teleporting

Short Distance Teleport

In the previous blog post we saw that an avatar sits down relative to the center of the prim holding the script and stands up at the sit target defined by llSitTarget. This same technique can be used to teleport an avatar up to 519 meters away. This is achieved by setting the target position to your teleport location then unsit the avatar at the destination. The first I had to find out was the destination for my teleport and to be able to demonstrate this I created two short distance teleports each of which teleports the avatar to the other teleport. The following script was inserted in both teleports with a different destination vector.

Short Distance Teleport

Each teleport is made up from a cube prim and named ShortDistanceTeleport1 and ShortDistanceTeleport2 respectively. To demonstrate this script I selected one of the teleports and clicked on teleport since the sitting text was replaced using the llSetSitText to Teleport. 

Teleporting to Second Teleporter

Arriving at second teleporter

As you can see the avatar is forcibly sent to the other teleporter by allocating the destination vector of that teleporter. DEST is a destination target location that is relative to the world or sim, but sit targets must be relative to the prim. The first five lines of the state_entry() method convert DEST from sim-local to prim-local. llGetRot() calculates the rotation of the prim relative to the sim, while llGetPos() calculates the prim’s location. The next line calculates the location of the target relative to the prim’s current location by subtracting the DEST variable with primCurrentPosn. The next two lines 9 and 10 adjust for any rotation applied on the primitive. To notify the arrival of the avatar at the sit locaion the script is relying on the changed() event. This is invoked under many conditions, including change of color, owner and location. The argument changebits is a bit pattern that indicates exactly what has changed but doesn’t tell how that change came to be. To know the previous values, the script needs to get these values from llGetPrimitiveParams. Since an avatar sitting on an object acts like an object being added to the linkset the CHANGED_LINK bit is set in the pattern, using the bitwise & operator. The script then uses llAvatarOnSitTarget() function which returns the GUID value key of the avatar sitting on the primor NULL_KEY if nothing is specified. If there is an avatar the script will result that it is the same avatar that just sat. The script then sleeps using llSleep(float time) to make sure the simulation has reposition the avatar and then unsits the avatar.

Intrasim Teleport (Medium Teleport)

The limitation posed upon by the short distance teleport is as the name suggests distance. What if one wants to teleport farther than 519m or more than 300m along one of the axis. This can be achieved by taking the distance from the prim’s original location to the target location and divide it into 10m steps. This algorithm has a nasty side effect of pausing the teleport for 0.2 sec (useful for elevators). The script about to be listed completes the teleport by using the function llSetPrimitiveParams(). This function takes a list as an argument which in our case will be the primitive position and the next position.

Intrasim Teleporting

From the intrasim teleporting script one can notice that instead of unsitting the avatar we are utilising a custom function called teleport which in turn takes the orgin position and then calls the custom function  moveTo to do the actual teleporting. The moveTo function takes three arguments origin, destination and jumpdist (jump distance). The jump distance is the increment by which the avatar is  moved until the destination is reached. To demonstrate this script first I opened the world map and selected the furthest place in the sand box then selected the destination coordinates and placed them in the DEST variable.

Selecting destination on world map

Then I right clicked the teleporter and selected teleport from the menu.

Teleporting using intrasim teleport

Arriving to destination

Communication

The conversational model can be used to interact with objects just as it is used to interact with other avatars. In the previous blog post the chatter cube script allowed for avatar-to-object messages by using the listen event. Next in this communication section we will be focusing on object-to-object communication.

Opening and Closing doors

This type of communication can be displayed by demonstrating a set of double doors which swing open or closed when one of them is touched. This can be achieved by external communication using llSay() between objects. The doors shown below are distinct objects which when one of them is touched both open or close together.

Left and Right door panels

The doors take symmetrical actions: the left door opens by rotating PI / 2 radians and closes by rotating -PI/2 radians; the right door does the opposite. 

Doorman algorithm

Rotating one door 90 degrees in the vertical plane is only half the job. The other part of the job is to make the other door operate in synch. The controller has two communication channels, inboundCmdChannel(990) and outboundCmdChannel(991).
Using inbound and outbound channels to pass message to other door

The right door will listen on its inboundCmdChannel and speak on its ouboundCmdChannel. Both doors need to know both values else the script wown’t work. The script shown is for the left door, the right door reverses the two channels. When either script is triggered the script posts a listen event via the llListen(inboundCmdChannel, “”, NULL_KEY, “”).
After the doorman function is finalised the script send a command to the other door using llSay(). The listen event handler on either door validates the message, making sure the message matches either the OPEN or CLOSE string. The other door then takes the approproate and symetrical action.
Opening Doors


Lamp Shade

When prims in an object need to communicate to each other the llMessageLinked() function is used. Through the previous example the function llSay was used, so what is the difference between the two. Link messages are much more private and secure, messages are sent faster and there isn’t a255 characters limit which is particularly useful when passing data structures from prim to prim.

Objects are composed from more than one prim, and in every object there is always the root prim. The root prim is the very last prim to be selected. When an object is selected the root is selected in yellow rather than blue. Every prim in a link set has names, keys and link numbers that are visible and can be used to coordinate actions between elements of a link set. The root prim always has thee link number of 1 (LINK_ROOT). There are some constants used in linked sets such as LINK_SET which references every prim in the set.

In this section a lamp shade will be demonstrated and linked message will be used. Link messages will allow any part of the lined prim to be touched and have the touched prim send a message to the other linked parts. The following lamp shade was constructed;

Created lamp on table

The lamp is constructed from two cubes as base and one on top and the light bulb cube in between. The light bulb was constructed out of a cube with transparent texture. The following code was inserted at the base of the lamp shade which will be functioning as a light switch;

Lighting Switch Script

The light switch script send a message to the root prim by using the function llMessageLinked(). To show the light bulb as being on the following code was inserted into the light bulb;

Setting lighting and color to light bulb

This script uses llSetPrimitiveParams function which in turn takes arguments such as brightness, color and the sides which will be effected. This function is called by the light bulb when the light switched is touched by inserting the following code;

Triggering lampLighter()

In llMessageLinked(), the first argument is either a linked set constant or the specific link number of the prim that will be receiving the message. In the script LINK_ROOT was used to send a message to the root prim bulb. The other three parameters of llMessageLinked() are passed directly to the link_message() event. When the root prim receives the link message it switched the lamp’s state between true or false. 

Switching lamp shade on

Sending Email and Browsing Web Pages

The section will cover long distance communication specifically opening a web page and sending an email. First to open web pages in Second Life using LS the llLoadURL() function is used.  This function takes three arguments which are the avatar key GUID, a message as string and the URL that is requested. Throughout this demonstration we will be using the chair created in the previous blog post to trigger the URL request.

Script to load URL

The script above gets the avatar touching the object by using the llDetectedKey function and then shows a dialogue box which asks the user if he/she wants to be redirected to the mentioned URL.

Dialogue for browsing

Sending e-mails using LS can be achieved using the llEmail(string email, string subject, string message) function. As shown in the function signature three arguments are required to send an email. The following script is triggered when the cube is touched:

Emailing Script

The script above passes the required parameters into llEmail and sent the mail to my mailbox.

Sent email from Emailing Cube




Creating objects

Rezzing is the creation of a new object in Second Life. Whenever in previous posts we had to create an object we went to the build panel and selected a prim of our choosing and manually created it. Scripting the creation of objects gives additional flexibility and eliminates the time taken to create repetitive objects. There is a class of scripts known as loop rezzers which handle the creation of a number of similar objects such as the creation of multiple spheres from one sphere.

This hopping ball demonstration consists from one sphere prim which the following script and a copy of the same sphere in its contents.

TempRezBall contents

The script within this prim rezes as set of objects and set up a communication channel between them. When the main sphere (controller for the other rezzed balls) is touched it rezzes a set of five balls each of which sets up a communication channel to listen to the ball immediately before it in the chain. The first thing the script does is call setUpListens() which sets up prim properties depending on whether the ball was rezzed from inventory or from the main ball. 

setUpListen function

In the function above the first thing the spheres do is get their start parameters. The main ball sets up the floating text with llSetText() ans selects a random number to use as a communication channel for the next ball gSpeakChannel. The main ball speaks to the first ball in the chain, the channel gSpeakChannel, and rezzes it with the value gSpeakChannel as its start parameter; the first ball then caches that value in gListenChannel ans sets up an llListen() on it. Each ball gets a unique communication channel for passing messages along the chain, each ball except the main one selects the channel to speak on using gSpeakChannel = gListenChannel+1. setUpListens() takes care of some other initialisations such as all except the main ball sets themselves to temporary using the property PRIM_TEMP_ON_REZ. Finally all balls select a random color.

touch_start() event handler

When the main ball is touched through touch_start() the event handler generates all children with a sequence of call to llRezObject(). selects an appropriate position and sets velocity and rotation to zero. The event handler passes in an argument so that the object knows that is was rezzed by a script and set an appropriate llListen(). The script call llSleep for 0.2 sec and calls the function hop_ball().

hop_ball() function

The hop_ball() function increments a counter and gets the current position of the ball. Then the llSetPrimitiveParams function is called with an increment on the vector position just retrieved. The function sleeps for 0.5 seconds and then sets the ball to its original state.

Demonstration of the hopping ball

To demonstrate I created three instances of the hopping ball and touched each ball to create the shown effect in the image above.


Inventory

In this section we will be focusing on giving inventory to other avatars which in this case will be in the form of a notecard.  A notecard is created by right clicking the Notecards sub directory in the inventory panel and select New Notecard. For this demonstration I have created the following notecard.

Created notecard

In order to give a notecard I created a small pedestal which when touched the avatar will be asked on whether to accept my notecard.

Notecard pedestal

Once the pedestal was created I created a new script and inserted the notecard in the pedestal contents.

Inserting script and notecard in pedestal contents

The next logical step would be to create the script that will be offer the notecard to the avatar.

Script to offer notecard when pedestal is touched

This script makes use of two new in-built functions which are related to inventory. llGiveInventory() takes two arguments the GUID of the avatar touching the pedestal. This avatar ID is detected using the llDetectedKey() function. The next argument is a GUID for the item which will be given to the detected avatar. This item ID is retrieved by using the in-built function llGetInventoryName() which takes two parameters the item and the index in which the item is found .In this case since only one notecard is available in the pedestal contents a zero is passed to refer to the notecard created earlier.

When the pedestal is touched the avatar is asked whether to accept or decline the notecard.

Accepting or declining the notecard


Conclusion

To conclude I would like to say that at first I found that creating these scripts was a bit tough since most of the algorithms used are fairly complex especially when dealing with rotation and positioning. At the end I can say that I learned a lot from these scripts and from the amount of research that I had to do to produce the demonstrations. Throughout these three blog entries on Second Life I started out a bit skeptical and frustrated at first but in these last two blogs I managed to do things that I never imagined doing using other technologies especially in such a short period of time.





No comments:

Post a Comment