Making a Basic HUD
April 25th, 2008 Posted in TutorialsMany products in SL come with a head-up-display or HUD device to control aspects of the product. These range from simple colour changes to the ultra sophisticated multi-tool do-everything type HUDS.
The reasons you might create a HUD is if you want to make some aspect of your project controllable by your customer but don’t want to have to add lots of button prims on the product or have your customer edit notecards.
This tutorial covers some basic concepts in putting together a basic hud, the basic type that simply sends commands for processing in a separate in-world device, but doesn’t show a complete example (I’ll add that if it’s requested, should anyone read this).
More complex devices that might incorporate 2-way communication are beyond this simple tutorial.
Which HUD Position?
There are eight possible positions that you can attach a HUD to. These are show in the following picture:

Where T: Top, L: Left, R: Right, B: Bottom, C: Centre
The primitives that were used in the picture have the dimensions X=0.5, Y=0.1 and Z=0.1. By default it is the Y,Z face that faces the user on HUD attachments and not the X,Y.
A few things can be noted:
- the Centre and Centre2 positions are the same (meaning you can have two items defaulting to the centre of the screen).
- the other positions correspond to the edge of the screen in each case, and the centre point of the prim is placed on the attachment position. This means that if you attach a prim for a HUD to the Top Left attachment point, ¾ of the prim will not be visible to the user.
Choosing the right position is important. If the hud takes up a lot of screen real-estate and is only worn occasionally, then the centre positions may be ok. If it is something that is going to be worn all of the time, then it may be better to design for one of the bottom positions or the left side. As a note: the right side gets the statistics bar when enabled, and that can’t be moved elsewhere on the screen.
By editing the object you can move the object to a different position with respect to the attachment point and the prim will remember that position if you detach and reattach it and anyone you give the HUD to will also get that remembered setting.
SL sometimes forgets the remembered position, so check it is stored correctly!
How To Communicate?
When discussing two separate objects communicating with one another within SL, generally this is realised via various forms of chat:
llSay()- This is the basic method used by most, this technique contributes to lag of the sim. This also has a 20m max range between the two objects.llShout()- This technique has a 80m working radius instead, but has a greater contribution to lagllRegionSay()- This technique allows communication within anywhere in the same sim. However, if the objects are either side of a sim boundary, even if this is less than 5 meters, they will not be able to communicate.
There are other ways also! You could use Email between objects, however this method requires that your object has fixed UUID’s (or that you can detect it). You could also use HTTP requests, with an out-world server storing the information to be communicated. However, both of these methods are slower than a basic Listen handler (normally).
I have intentionally left out llWhisper() because in a moderately lagged sim, this seems to reliably fail! This is the one problem with one-way communication, there is no guarantee that the message will be received and no way for you to know if it was received or not.
If your HUD is talking to another attachment worn by the user, llSay() would probably be the best technique. If it is for an attachment with a fixed position, then llRegionSay() might be a better idea.
Making the HUD
The HUD itself is fairly straightforward, the general design is to have one root prim and a number of linked buttons. The button send tells the root prim that it has been pressed and the root prim in turn sends this information to the object to be communicated with.
The script used in the buttons is as follows:
default {
on_touch() {
llMessageLinked(LINK_ROOT,0,llGetObjectName()+"|"+llGetObjectDesc(), "");}
}
This sends the contents of the prim name and description fields to the root node. Note the seperator “|”. This is used such that the commands can be decoded by the receiver, any separation character will do as long as it is not used in the object name.
The root node then has this script:
integer channel = -188800;
default {
state_entry() {
llPreloadSound("6005e358-33fd-d08b-012f-6110ab27a413"); }
link_message(integer sender_num, integer num, string str, key id) {
// Play UI click and say message to other object
llPlaySound("6005e358-33fd-d08b-012f-6110ab27a413", 1.0);
llSay(channel,str); }
}
The root node simple sends the information straight to the other object via llSay and plays a “click” sound. The click sound is used to give the user feedback that they did press a button.
The channel selection is fairly important. Low channel numbers tend to be more congested, channel 0 (normal chat) should never be used. Positive channels can be used directly by the avatar (ie, by typing /55 do something) but negative channels can not.
This is not the only, or even the best way to do this! A better way (in terms of less scripts and therefore less lag) is to use llDetectedLinkNumber() to detect which button was touched from the root object. The downside to this approach is that there is only a llGetLinkName() function, so the information to be passed to the other object has to be all in the prim name.
Target Object Processing
In the target object you need to set up a listen for the command from the hud, this is done by declaring an open listen on the channel to be used: llListen(channel, "", "", ""). Note, this is an open listen so all messages on that channel will be picked up regardless of source.
To prevent listening to other avatars and HUD’s owned by other avatars, the following code is used:
listen(integer channel, string name, key id, string message) {
if ((id == llGetOwner())||(llGetOwnerKey(id) == llGetOwner())) {
// process commands
The first part of this checks if the owner sent the message, the second part checks if an object owned by the owner sent the message.
The processing of the commands depends on what exactly the hud is going to do. If you have used a separation character to pass both a command and a parameter, the following code usefully extracts the command and parameter (and discards the separation character).
message = llToLower (message);
// get the position of the seperation character
integer sepCharPos = llSubStringIndex(message,"|");
//extract
string command = llGetSubString(message, 0, (sepCharPos-1));
string parameter = llDeleteSubString(message, 0, (sepCharPos));
This could of course be extended to include any number of separation characters and parameters. Listening to the owner is not without risk, LSL does not have rigorous type checking or error detection functions. There is no isInteger() function! If you intend to type cast the received parameter to a different type from string there is a risk that you may get errors due to the input being malformed.
Alternatives
There are a few alternative techniques that can be used in place of a HUD:
- Use a Menu - LSL provides for a menu system that pops a blue menu up in the top right corner which the user can click. Since the reply from the menu system is spoken back to the user, this still needs a Listener and thus contributes to lag. The menu system can only show a limited number of characters per button.
- Use a Configuration file - It is also possible to read a notecard in the objects inventory, the user can just edit this to change settings. This doesn’t use a Listener and therefore is less laggy. However, it does affect the time taken to change settings.

You must be logged in to post a comment.