Thursday, November 30, 2006

Scripting: the basics, part 3

This script is one I wrote for a barrier which prohibits entry unless the barrier's owner touches or collides with it; when this happens, it will set itself to PHANTOM and turn partially transparent for five seconds, and then return to its previous opaque solidity.

It's also a good demonstration of how to modify prim parameters, set timers, and use multiple states.

default {
collision_start(integer total_number) {
if (llDetectedKey( 0 ) == llGetOwner()) state open;

touch_start(integer total_number) {
if (llDetectedKey( 0 ) == llGetOwner()) state open;

state open {
state_entry() {
llSetPrimitiveParams( [PRIM_PHANTOM,TRUE,
PRIM_COLOR,2,<1,1,1>,0.75] );
llSetTimerEvent( 5 );

timer() {
llSetPrimitiveParams( [PRIM_PHANTOM,FALSE,
PRIM_COLOR,2,<1,1,1>,1.0] );
llSetTimerEvent( 0 );
state default;

Okay, now the first line in this script is the default state. States are a way for the script to behave differently based on certain criteria; without states, we would have to use variables to keep track of things. In this script, I have two states: the default state (in which a touch or collision by the owner will "open" the barrier), and the "open" state, in which it opens itself, waits five seconds, and then closes again; in this state, further touches and collisions will do nothing.

We have two functions in the default state: collision_start and touch_start. These two functions are called, if they exist, when an avatar or object starts to collide with the prim containing the script, or when an avatar starts touching the prim containing the script.

These two functions are passed a parameter, an integer value indicating how many objects or avatars have started colliding with (or touching) the prim. This number is usually one (it's theoretically possible for two avatars to start colliding with or touching the prim at the exact same instant, but for the sake of simplicity we're going to assume that it's only one).

As it happens, I want both of these conditions to do exactly the same thing, so the code within each function is identical:

       if (llDetectedKey( 0 ) == llGetOwner()) state open;

llDetectedKey() gets the UUID of the object detected by the function. Index number 0, which we're using here, is the first object or avatar detected. (We could check the "total_number" variable, and if it's greater than one, we could loop through and check all of them, using llDetectedKey( 1 ), llDetectedKey( 2 ), and so on.)

The statement then compares the value returned by llDetectedKey() against the key of the person who owns the barrier, which is returned by the llGetOwner() function. If they're equal, then it switches into the state I've named "open".

Tip: The default state must occur first in the script. If you try to insert another state above default, the LSL compiler will produce a syntax error when you try to save the script.

The "open" state contains a function named state_entry. This gets called, if it exists, whenever the script enters the state the function is contained within. All states may have a state_entry function; like with all predefined functions, whichever one exists in the currently active state is the one that gets called.

The state_entry function calls llSetPrimitiveParams(). This is a function which can change nearly every feature of a prim, including what shape it is! It takes a list of things you want to change, and what you want to change them to. (The wiki has a great page describing all the things it can do.)

Here, we're changing the PRIM_PHANTOM parameter, and setting it to TRUE. I also decided to give a little visual feedback, so I'm setting the front and back faces of the barrier to partially transparent. With llSetPrimitiveParams(), you have to change both the color and the alpha setting simultaneously. Fortunately, I know that the faces should be completely white, so for each face I pass the PRIM_COLOR keyword (which tells llSetPrimitiveParams() what I want to change), the number of the first face to change, the RGB value <1,1,1> (white), and the alpha value of 0.75, (75% opaque). I then pass the same sequence again for the other face.

Tip: To find out what face you want to change, edit the prim and click on the "Select Texture" radio button in the edit dialog. You should see crosshairs appear on all faces of the prim. Click on the face you want to find the number for, and hit Ctrl-Alt-Shift-T. Your chat window will give you certain information about the texture on that face, including the face number.

After setting the prim parameters, I then set a timer event to trigger five seconds later (and subsequently in five-second intervals until the timer is killed), and that's the end of the state_entry function.

The next function is the timer function, which gets called when the timer event is triggered. In this function, I call llSetPrimitiveParams() again to reverse the changes I made earlier (I set the PRIM_PHANTOM parameter back to FALSE, and set the alpha on the front and back faces to 1.0 (fully opaque).

The next line kills the timer events (by passing a parameter of 0), and then the next line after that sets the script back into the default state again so that it will "listen" for collisions and touches again.


Robert said...

Thanks :)

It's so much easier to understand when it's explained what the parts are and what they do!

Speuan Ewing said...

Thanks dude, good tutorials

Anonymous said...

Yes i want also thank you.I would appreciate more tutorials like these. Very good explained everything, much better than others ive read until now. Thanks

Anonymous said...

yes, this is the best tutorial i have found, more please :))))

Pius Noel said...

Wow! Finally, even I understand what states are good for.

My new code looks much better. It has become better readable and understandable since I could get rid of some complicate if-then-else constructs controlled by some silly flags (which did nothing than expressing a state).

Thank you. Reading your tutorial was a real bargain :-)

Anonymous said...

Tutorials with a soul are always fun to read. Thank you for talking as a person instead of a manual. XD