You are currently browsing the monthly archive for December 2007.

Holly wreathI know from my blog stats that I have a steady and slowly rising number of readers, so to all of you who have been following my blog over the past months, thanks for being there and giving some point to my ramblings.

I hope you have a very merry Christmas, and I’ll be back when the festivities are over.

Enjoy.

In my previous blog entry about creating a HUD, I briefly mentioned that it was possible, but not necessarily easy, to have dynamic output on a HUD.

Here is a relatively simple way of doing this, along with a script that makes use of the technique. It relies on three simple facts relating to the floating text that can appear above a prim. First, this text still appears even when the prim is used in a HUD; second, it is then only visible to the wearer; and finally, the floating text can have multiple lines. We can use this to create a screen display for the HUD.

As an example, let’s create a ‘green-screen’ monitor for a HUD. Create a basic HUD window (a simple block prim), and add two more prims, one directly above the other. The upper prim will provide the background, the lower prim will generate the text.

Set the upper prim’s color to black, and size it to something suitable (the actual size depends on the number of lines of text you will be displaying, so some trial and error is probably required). Link the prims, and attach the result as a HUD. See my previous article if you need more details about creating a HUD.

Now we need a script to generate our dynamic text. The following script implements a scrolling display, and adds a line to the display every time the owner clicks the text prim (just as a demonstration). Create a new script in the lower prim, and copy the script into it, replacing the default script.

The result should look something like this:

integer linePos = 0;
list lines;
integer listLength;
vector textColor = ; // Green
init()
{
    clear();
}
clear()
{
    lines   = ["", "", "", "", ""];
    listLength = llGetListLength(lines);
    linePos = 0;
    updateDisplay();
}
setLine(integer lineNo, string lineText)
{
    // If the requested line number is past the end of
    // the list of lines, scroll the lines.
    if (lineNo > listLength)
    {
        lines = llList2List(lines, 1, listLength - 1);
        lines += "";
        lineNo = listLength - 1;
    }
    lines = llListReplaceList(lines, [lineText], lineNo, lineNo);
    updateDisplay();
}
updateDisplay()
{
    string output;
    integer i;
    integer count = listLength;
    for (i = 0; i < count; i++)
    {
        output += llList2String(lines, i) + "\n";
    }
    llSetText(output, textColor, 1.0);
}
addLine(string lineText)
{
    setLine(linePos, lineText);
    if (linePos < listLength)
      linePos++;
}
default
{
    state_entry()
    {
        init();
    }
    touch_start(integer count)
    {
        // This is just a dummy example of how to update
        // the display. When the owner touches the prim
        // we will add a message to the display. We'll
        // check that it was the owner who touched us,
        // although that isn't really necessary if this
        // is in a HUD. 
        if (llDetectedKey(0) == llGetOwner())
        {
            addLine("Touched at " + llGetTime());
        }
    }
    on_rez(integer param)
    {
        init();
    }
}

Shopping malls are two-a-penny in Second Life. In fact, in some places it’s hard to move without finding yourself in another mall. Most of them, let’s face it, are badly designed, or even not designed at all, being little more than a motley collection of stores all jumbled together.

There are exceptions. Pixel Dreams, owned and created by Tya Fallingbridge, is one of them. From the very first glimpse that you get, you can tell that this is something a little different, a little more stylish than the usual mall:

First glimpse of Pixel Dreams

Rather than a disparate collection of differently designed shops, Pixel Dreams shares the same basic ‘look’ throughout. The Girls’R’Us shop adds an appropriate dash of pink (!), but the rest of the shops share a similar black and white color scheme. The interiors are more varied, but still equally attractive:

Pixel Mode interior:

Pixel Mode

And when you have finished your shopping, you can dance the night away at Pixel Dream’s own dance club, Schmooze:

Schmooze

There are more pictures of Pixel Dreams in my Snapzilla photo-album — see the link below.

Links

Photo Album

SLURL for Pixel Dreams

I’ve done a number of posts now about scripting, so maybe it’s time to move on to a new subject. I’m going to assume (as I did with scripting) that you are already familiar with the basics of texturing in Second Life, and also that you are probably familiar with a suitable graphics package for creating or adjusting textures.

Recently I’ve started experimenting with drop-shadows, and on reading Iris Ophelia’s article in New World Notes I see that I am not the only one. Drop- shadows are evidently the new thing!

But what is a drop-shadow, and why would you want one? An illustration is in order. Here is a snap-shot of one of the rooms of my SL home, Morgridge Mansion:

Morgridge Mansion without shadows

The key thing to note is that in real life there would be an element of shadowing beneath the objects (especially the chairs and the table). Let’s add this in Second Life:

Morgridge Mansion with shadows

Much better. A nice bit of added realism (to be honest, I’ve slightly overdone some of the shadowing, just to make it stand out for this illustration). You’ll notice that I’ve also added a shadow behind the portrait on the wall.

So…how is it done?

Well, I don’t know how other people do it, but I’ll explain how I achieved the effect.

The first thing you need is a shadow texture. For the Mansion I created two textures, one circular, and one square. I won’t give exact details on how to do this, because they depend on which graphics package you use (personally I use The Gimp, which is free, powerful). If you aren’t familiar with creating textures, I’ve included links to my own shadow textures, which you can feel free to copy and use — see the bottom of this post.

If you want to try creating your own shadow texture, these are the steps that I took. Translate them to suit your own graphics package:

I used a 128×128 graphic (shadows don’t need a high-resolution, so this size is perfectly adequate) with transparency. I then selected an area about 3/4 of the size of the entire graphic, feathered it to about 25 pixels (you can play about with this — it will determine how quickly your shadow ‘fades out’ towards the edges), and used a blend fill of black to dark gray to fill the area. The end result (in both square and circular versions):

(To be honest, I’m not happy with the square version, which has something of an ‘X’ effect to it. More work required, methinks.)

Ok, now we’ve got our texture, how do we apply this in Second Life?

Start with a new prim — cube for square shadows, cylinder for circular shadows. Resize the Z-scale to the minimum, 0.01, and adjust the X and Y scales until the prim is slightly larger than the object that you want to shadow (if you are adjusting the prim using the ‘grab handles’ rather than entering numbers into the dialog, you probably want to scale the X/Y size first, before minimising the Z scale).

You now need a completely transparent texture. There is a transparent texture in the default library, but unfortunately it is not completely transparent, and will leave you with an ugly white edge. I’d avoid it and use your own texture. If you don’t have a fully-transparent texture, I’ve included a link to mine at the bottom of this post.

Apply the texture. Then select the ‘Edit Texture’ option from the build dialog, and select the top surface of the prim. Apply the shadow texture to this surface.

Move the prim so that it is directly beneath the object that is to be shadowed.

On the Features tab of the build dialog, adjust the transparency. The higher the transparency, the more subtle the shadow. I’ve found that a value of between 40 and 60 seems to work best. Experiment.

Link the shadow prim to the main object, and you’re done.

And finally

The transparency of the shadow prim affects the darkness of the shadow, and the best value to use depends on where the object is located. It would be handy to give the owner of the object a way of adjusting this. Here’s a simple script that does just that. Drop this script into the shadow prim. Each time the owner clicks on it, the transparency will increase by 10 (fading the shadow). When the transparency reaches 100, the next click will return it to 0 (completely black):

// Default transparency value. Change to suit.
float transparency = 50.0;
// Amount to change transparency on each click.
float changeBy     = 10.0;
default
{
    touch_start(integer count)
    {
        // Only allow the owner to change the shadow.
        if (llDetectedKey(0) == llGetOwner())
        {
            // Adjust the transparency value.
            transparency = transparency + changeBy;
            if (transparency > 100.0)
            {
                transparency = 0.0;
            }
            // Apply the new value to the prim. llSetAlpha
            // works in reverse to the Transparency value
            // in the build dialog (!), so we'll subtract from
            // 100 to make them match.
            llSetAlpha(1.0 - (transparency / 100.0), ALL_SIDES);
        }
    }
    changed(integer change)
    {
        // Reset the script when the owner changes,
        // otherwise llGetOwner() might return the
        // wrong value.
        if (change & CHANGED_OWNER)
        {
            llResetScript();
        }
    }
}

Downloadable textures

Circular shadow texture

Square shadow texture

Fully-transparent texture

Caledopoly!

I sometimes go for a random wander around Caledon (my home country in Second Life), just to see what I can find.

Today, I found this:

Caledopoly Board

I was completely agog. It is still under construction, but it seems that it is intended to be fully playable.

You can already roll the dice:

Caledopoly Dice

And take a Chance:

Caledopoly Chance Card

It is being built by LittleBlackDuck Lindsay (what strange names people have round these parts), and for myself, I can’t wait to play a game!

If you have Second Life installed, and want to take a look for yourself, you will find the board here:

SLURL for Caledopoly Board

This is going to be a long one. Get yourself a cup of coffee first.

What is a HUD? If you have to ask that question, you probably won’t get much from this article, but HUD is an acronym of Head-Up Display, and allows creators in Second Life to add a user-interface to gadgets.

They are created from prims just like any other user-created objects in Second Life, but they are only visible to their owner, and always appear ‘flat’ against the screen. If you’ve used any gadgets in Second Life, you will almost certainly have come across them.

For example, here is the popular blogHUD, for making blog postings from within Second Life:

blogHUD

When I started trying to create HUDs myself, I found that there was very little information about how to go about it, and I struggled for a while with even the simplest things. Now that I think I know what I am doing(!), I thought I would post a blog passing on my hard-won information!

In this post, I’ll show the basic steps of creating a HUD, and point out some of the pitfalls and ‘gotchas’ that you are likely to encounter along the way.

Are you sitting comfortably? Then I’ll begin…

The first thing to do, of course, is to decide what our HUD will contain. Because of the limitations of SL, it is not easy to include any kind of output in the HUD (with some ingenuity it can be done, but that is beyond the scope of this blog), so our HUD will basically be restricted to having some buttons to push.

For the sake of an example, we are going to create a HUD which allows the user to scan the area for other avatars, and to ‘poke’ (via chat) one of those avatars. Not very exciting, I know, but it is only an example!

We will need two buttons: Scan and Poke. We will also need some sort of ‘window’ for them to sit on. We could have them just floating on the screen, but that would look rather odd.

The first step is to create a sort of ‘rough draft’ of our HUD. I’ll explain why in a minute.

Let’s start with the window.

(Oh, one quick point – I am going to assume that you are fully familiar with creating/scaling/texturing prims. If you aren’t, you really shouldn’t be trying to create HUDs yet!).

Create a simple cube. Set the X scale to 0.01, so that you end up with a thin, flat board. Move it to somewhere close so that you can see it easily. If you want, you can blank the texture, though that isn’t important – we’ll deal with the texture later.

Next, let’s add a couple of buttons. Create two more cubes, setting the X scale to 0.1 again, and setting the Y and Z scale to something that approximates a sort of rectangular button shape. Place them flat against the ‘window’ board. Don’t worry about positioning them exactly yet, but you might want to colour them so that you can see them more clearly.

Link the buttons and the window (I’d suggest making the window the root prim, by selecting it last before linking, though it isn’t important for the simple HUD that we are going to make). One warning – don’t rotate this object, otherwise when you come to attach it, it might not be facing you!

Ok, now we have a primitive-looking window and buttons. Time to turn them into an actual HUD. Right-click the linked object, click ‘More’ on the pie menu, then click Attach HUD, and select Center.

If by chance you find that it is at the wrong angle (probably sideways!), you can rotate it even though it is attached. You might find it easier to adjust the numbers manually in the build dialog, rather than trying to use the Rotate tool.

Attached HUD

Yuk. You now have a grotesque HUD, probably occupying far too much of the screen, and obscuring your view. Don’t worry, there are things we can do about it. In fact, if you are at all familiar with building stuff, I’m sure that you are already ahead of me. Hold fire, though, because there are a few things you need to know before going on.

Firstly, yes, you can edit the HUD when it is attached. This is very convenient, because an unattached HUD is very small and fiddly to work with. However, there are some limitations on what you can actually edit once the HUD is attached. This is why it is useful to do a ‘rough draft’ while the HUD is unattached and you have all the editing facilities available to you.

The most important limitation on editing an attached HUD is that you cannot unlink the prims that make up the HUD, and you cannot link new prims to the HUD. If you find that you need to add a new prim, you will have to detach the HUD (it will vanish into your inventory, and you will need to re-rez it, which is rather annoying), create and link the new prim, then re-attach the HUD.

To work on the individual elements of the HUD, you need to tick the ‘Edit linked parts’ checkbox on the Build dialog. I’ve found that this normally lets you do most things to the individual parts, although rescaling prims sometimes refuses to ‘take’. If you get that happening to you, unfortunately all you can do is drop the HUD and unlink it while you edit the offending prim.

For our simple example, rescale the entire HUD to something more suitable. You can then edit the two buttons to position them more exactly (this is much easier to do now that the HUD is attached).

Of course, it is still obscuring our view. For now, select the ‘window’ prim, blank the texture, colour it grey (or whatever you prefer), and set the transparency to something between 50 and 75.

You could do something similar with the buttons, but we need to get text onto these in some way. Because SL doesn’t yet provide text-on-a-prim, you will have to break out your favourite graphics program, and create some button textures.

Naturally, you can do something similar with the texture for your window prim, and create something fancier than the plain version that we’ve just made.

If you are feeling lazy, here are a couple of example button textures that you can use instead:

“Scan” button texture (.tga)
“Poke” button texture (.tga)

Here is the final result (I’ve done some resizing, and reattached the HUD at the top-left instead of the centre):

Example HUD

One final ‘gotcha’. Go back to when we originally attached the HUD, and we found that it took up large amounts of screen space. How much screen space do you imagine a prim of a given size will take up? The answer is…it depends. Most crucially, it depends on the size of the window (or the screen resolution if running full-screen) that Second Life is using. A HUD will occupy more space in a smaller window, less space in a larger window. You need to think about this when designing your hud (make the mistake of trying to create a ‘full-screen’ HUD, and it will only fit properly at one specific screen-size. Not good).

Ok, we now have our HUD designed and built…but it doesn’t actually do anything! Time to rectify that.

Select the buttons individually, and create a new script in each one.

For the Scan button, edit the script, and replace the default with the following, which does a simple scan of the surround area, and outputs the names of any avatars found (the output is done via llOwnerSay, so it is only visible to the user of the HUD):

default
{
    touch_start(integer total_number)
    {
        llOwnerSay("Scanning...");
        // Look for any avatars within 96m.
        llSensor("", NULL_KEY, AGENT, 96.0, PI);
    }
    sensor(integer num_detected)
    {
        integer i;
        while(i < num_detected)
        {
            if (llDetectedKey(i) != llGetOwner())
            {
                llOwnerSay(llDetectedName(i));
            }
            ++i;
        }
    }
}

For the Poke button, replace the default script with this rather more elaborate one. This again does a scan of avatars in the vicinity, and then displays a dialog with their names as the buttons. When you select an avatar from the dialog, the chat message ‘[Your name] pokes [avatar name]’ is displayed.

integer dlgHandle = -1;
integer dlgChannel = -9999;
list avatarList = [];
reset()
{
    llSetTimerEvent(0.0);
    llListenRemove(dlgHandle);
    dlgHandle = -1;
}
default
{
    touch_start(integer total_number)
    {
        llOwnerSay("Scanning...");
        avatarList = [];
        // Look for any avatars within 10m.
        llSensor("", NULL_KEY, AGENT, 96.0, PI);
    }
    sensor(integer num_detected)
    {
        integer i;
        while((i < num_detected) && (i < 9))
        {
            if (llDetectedKey(i) != llGetOwner())
            {
                avatarList += [llDetectedName(i)];
            }
            ++i;
        }
        if (llGetListLength(avatarList) > 0)
        {
          state dialog;
        }
    }
}
state dialog
{
    state_entry()
    {
        // Set up a listener to detect button clicks.
        dlgHandle = llListen(dlgChannel, "", llGetOwner(), "");

        // Start a new timer.
        llSetTimerEvent(30.0);

        // Add a 'Cancel' button.
        avatarList += ["Cancel"];

        // Display the dialog.
        llDialog(llGetOwner(), "Please select an avatar.", avatarList, dlgChannel);
    }
    listen(integer channel, string name, key id, string message)
    {
        // The message parameter holds the caption of the
        // button that was clicked. Search the menu options
        // list for it.
        if ((channel == dlgChannel) && (llListFindList(avatarList, [message]) != -1))
        {
            if (message != "Cancel")
            {
                llSay(0, llKey2Name(llGetOwner()) + " pokes " + message);
            }
            reset();
            state default;
        }
    }
    timer()
    {
        reset();
        state default;
    }
}

Close the Build dialog, and your HUD should be fully active. Click on the buttons to check that they work.

Congratulations, you’ve just built your first HUD!

It’s often useful for an object to maintain a record of avatars or objects. One example might be a shop vendor which keeps a list of who has bought which item from it, or perhaps you want something which will tell you who has visited your shop recently.

There are various ways of doing this, but all of them eventually require you to store the information somewhere, and short of providing a link to an off-world database (a perfectly viable solution, but beyond the scope of this blog), the best option is to use a list.

The LSL implementation of lists is relatively primitive, but quite flexible, especially once you get to grips with the various associated functions, of which there are quite a few.

A list is just what the name implies. It is a simple collection of values, of any data type.

Declaring a list variable, and adding some items, is quite simple:

list animals = [];    // This creates an empty list.
animals += ["cat"];   // This adds one entry.
animals += ["dog"];   // This adds another entry.

Our list is now [“cat”, “dog”]. We can add more than one item at once:

animals += ["rabbit", "mouse"];

The list now holds [“cat”, “dog”, “rabbit”, “mouse”].

If we want to get a particular item out of a list, we use one of the llList2… functions. As our list only holds strings, we use llList2String. This needs to know the list that we are extracting from, and the item number. Be careful – items in a list are numbered from 0! If you have never done any programming before, this might catch you out, but it is very common in programming languages (for reasons that I won’t go into here):

// Copies "dog" to the 'item' variable.
string item = llList2String(animals, 1);

Actually, a list can hold any data type, and any mixture of data types, except (and this is an important exception) other lists.

For example, we could do this:

animals += [10.12, 16, "kangeroo"];

Our list is now [“cat”, “dog”, “rabbit”, “mouse”, 10.12, 16, “kangeroo”]. As you’ll realise, this probably isn’t a clever thing to do, because we now have strings and numbers mixed in our list, in no particular order.

Assuming that we do know the order, we can extract these values using the llList2Float and llList2Integer functions:

float fvalue = llList2Float(animals, 4);
integer ivalue = llList2Integer(animals, 5);

On the other hand, suppose we want to store several items of information for each ‘entry’ in our list. Suppose we want a script that holds avatar names along with some kind of count for each avatar (maybe the number of times they have visited your shop).

What we need is a strided list. This is exactly the same as a normal list, but we impose certain conditions on it, and use a slightly more convoluted way to extract information out of it.

Let me explain. To make things easier, let’s use some standard computer parlance, and call our combination of avatar id and count as a ‘record’. Our constraint on the list is that we always add a complete record at a time, and this record must always contain the same number of items, each item being of a specified type. In our example, a record is a list which contains a string (the avatar name) and an integer (the count).

For example:

list avatarList = []; // Create an empty list.
avatarList += ["Pyter Morgridge", 0];   // Add a record.
avatarList += ["Someone Else", 0]; // Add another record.

Our list now holds [“Pyter Morgridge”, 0, “Someone Else”, 0]. Because we have imposed a structure on it, we can extract a complete record out of the list by using the llList2List function, which extracts a sublist. In this case, we will extract the first record, which starts at position 0 in the list, and ends with position 1.

list pytersrecord = llList2List(avatarList, 0, 1);

We now have [“Pyter Morgridge”, 0] in pytersrecord. The only tricky bit (and it isn’t that tricky) is working out where a particular record starts.

We deal with than by making things a little more generic. Let’s start a new script:

integer FIELD_COUNT = 2;  // Number of items in each record.
list avatarList = []; // Create an empty list.

Now we can create a utility function that will return the position in the list for a particular record number. In our example above, the record for Pyter is record number 0, and the record for Someone Else is record number 1. The list entries for them start at 0 and 2 respectively.

integer getPosition(integer recordNumber)
{
    return recordNumber * FIELD_COUNT;
}

We can use this to create functions that will return a single record, delete a record, and update a record:

list getRecord(integer recordNumber)
{
    integer pos = getPosition(recordNumber);
    if (pos < (llGetLength(avatarList) + FIELD_COUNT - 1))
      return llList2List(avatarList, pos, pos + FIELD_COUNT - 1);
    else
      return [];  // Or possibly raise an error.
}
deleteRecord(integer recordNumber)
{
    integer pos = getPosition(recordNumber);
    if (pos < (llGetLength(avatarList) + FIELD_COUNT - 1))
      avatarList = llDeleteSubList(avatarList, pos, 
                                   pos + FIELD_COUNT - 1);
}
updateRecord(integer recordNumber, list newRecord)
{
    integer pos = getPosition(recordNumber);
    if (pos < (llGetLength(avatarList) + FIELD_COUNT - 1))
      avatarList = llListReplaceList(avatarList, newRecord, pos, 
					      pos + FIELD_COUNT - 1);
}

You’ll find more information about lists and strided lists here:

LSL Wiki – Lists

…but also, it seems, the season of exceptional silliness.

Baby Shang Miss Fogwoman Gray of Caledon announced this morning that she has produced a Baby Shang, causing great astonishment all around Caledon (for those who are not familiar with Caledon, the esteemed owner is Desmond Shang, otherwise known as the Guv’nah).

In a very short time, several other residents of Caledon were similarly in possession of Baby Shangs. It is possible that Mr Shang may face some very awkward questions…