User:SuzannaLinn Resident/ScriptingClasses/Floating Texts
Floating Texts
Monday class
Add a floating text to a box
-> Object 1 Floating Text on box (a red box) -> Script 1 Floating Text on box
The script starts with a list of color names, in lines 0-16. Let's start explaining how the colors work.
A color is defined with 3 values with decimals, between 0 and 255, for the Red, Green and Blue components of the color. To keep the three RGB values we use the type "vector".
Vectors are often used for the position of the object, for the values of the x, y and z coordinates. Also for the size of the object, the height, width and depth coordinates.
And since we also use 3 values to define colors , the scripting language uses the same type vector for the colors (instead of creating a type "color").
We are creating a variable for each color. A variable of type "vector". And we are assigning a value to the variable immediately.
As you see in the script, the 3 values are separated by commas between < and >. This is the way to assign values to a vector type variable.
But, although RGB values are in the range from 0 to 255 everywhere in the world, lsl script uses a range from 0 to 1, so most of them have decimals.
For instance, in line 0, we are creating the variable NAVY, of type "vector", and assigning to it the value <0.000, 0.122, 0.247>.
We are using a different naming style for the variables in lines 0-16, all in uppercase.
It's a naming convection to show that the value of this variable wil be constant along the script. We are assigning the value once at the start and never changing it.
Other programming languages have a way to create constant named values. LSL has not. So we use variables with all uppercase names.
If the name of the "constant" variable has several words, we will use an underscore _ to separate the words, like MY_CONSTANT.
Now let's go to write a floating text on our box.
The function that write the floating text is llSetText() and it has 3 parameters: the text to display, the color of this text, and the alpha.
The parameter "text" is of type string. The parameter "color" is of type vector, as we have just seen. And "alpha", that can have decimals, is of a type for numbers with decimals, the type "float".
We can have the text in several lines using "\n" which means new line, for instance:
- "Hello world \n I'm here!"
The alpha is the transparency of the text. Its values are between 0.0 and 1.0.
1.0 is completely opaque, 0.0 is transparent and invisible, 0.5 is half transparent, etc.
Since floating texts are sometimes difficult to read we will use 1.0. You can try later with other alpha values to see how it looks.
In the event state_entry, lines 24-29, we initialize the variables for the three parameters.
For the text we will display our name. Here we use two functions.
The function llGetOwner() returns the UUID of the owner of the object where the script is in.
The function llGetDisplayName() takes an avatar UUID and returns the name.
And finally, in the event touch_start, lines 31-34, we have llSetText() to set the floating text.
Well, we could have just scripted this:
default { state_entry() { } touch_start(integer num_detected) { llSetText("Hello world!",<1,0,0>,1.0); } }
which does about the same, but i think that it's more fun to be learning more things along the way.
Now touch your box, to see the floating text appearing.
Make the text change colours every half second
-> Object 2 Floating Text changing colors (a yellow box) -> Script 2 Floating Text changing colors
This script has the same code as the previous one with some more lines added.
To make the floating text change color from time to time, we need to control the time.We will get it using the event timer and the function llSetTimerEvent().
The event "timer" is triggered regularly with an interval of seconds. If the timer is set, for instance, to 10 seconds, every 10 seconds our script will receive and event timer telling us that it's time to do something.
We set the interval with the function llSetTimerEvent(), in line 36.
The function llSetTimerEvent() has one parameter: after how many seconds we want the timer event triggered.
We want to change the color every half second, so we will use llSetTimerClock(0.5), and the event timer will be triggered every half a second.
To know which color to change to, we are using a counting variable, colorCounter, thats goes from 1 to 3, increased in each timer, and when arrives to 4 is changed back to 1, in lines 41-57:
colorCounter = colorCounter + 1; if (colorCounter>3) colorCounter=1;
And for each value of colorCounter we assign a color. Later as homework, you can change the colors and use 4 or more colors instead of 3.
If we wanted the event timer to stop being triggered, we would use llSetTimerEvent(0). Setting the timer to 0 seconds stops it.
Now touch your box, to see the floating text changing colors.
Option to type a new text in chat
-> Object 3 Floating Text change text (a green box) -> Script 3 Floating Text change text
We will change the floating text typing a new text in chat. So we will need a way to get that the script knows what we are typing. And we will like that what we type doesn't appear in the public chat, because it's just a private conversation between us and our scripts.
We need to open a private channel of comunication. We will type our new text in this channel and our script will be listening in the same channel.
Channels have numbers. The channel number 0 is the public chat. Our chat goes to channel 0.
We can choose any number for our channel, up to 9 digits. In this script we will use channel 11.
We can type in a channel starting with a "/" and a number of channel, for instance:
- "/11 I'm typing in channel number 11" (without the quotes, of course)
And if we type this, nobody and nothing will see it, unless they are listened to this channel.
Let's see how we will make our script to listen.
The function is llListen(), in line 39. It has 4 parameters:
- the channel we want to listen to (number 11, in our example).
- the user name of the person we want to listen to.
- the UUID of the person, we will use this parameter or the user name parameter, not both.
- the message, if we are listening for a specific message.
We have to choose a channel to listen, the other parameters are optional, and we will use them to filter the message.
In this case we will listen to channel 11, and only to messages sent from ourselves. Otherwise we will be listening to the messages from the other students, who will be using also channel 11 to change their floating texts:
- llListen(11,"",llGetOwner(),"")
We are again using llGetOwner(), to get our UUID.
And what happens when the script received a message from us to the channel 11? It triggers the event listen, which has the same parameters than the llListen function.
In the event listen. in lines 65-72, is where we will change the floating text.
Let's look at our script to see what we have added to the previous script.
At the start, after the colors, in line 18, there is:
- integer CHANNEL_FLOATING_TEXT=11;
We could have used the number 11 in the listen function and event, but it's better to use a constant with a nice name or it would get very confusing if we listen to several channels.
In the event touch_start, lines 34-40, we have the llListen() function.
And at the end of the script we have added the event listen, lines 65-72.
You see that in the parameters of the listen event, the 3rd parameter, which is the UUID, has the type "key".
The type key is used for UUIDs of any kind: avatars, objects, textures, notecards,... it could have been called type "uuid" but it's named key.
Inside the listen event we check that the channel is the our CHANNEL_FLOATING_TEXT. No need to do this here. We are only listening to a channel and we will not receive messages from any other channel. But it's better to do it, in case that we add more channels to our script in the future.
And we change the text variable to what we have typed in channel 11. And... don't we use the llSetText() function to change it?
No need, we have a timer that sets the text every half a second.
Now touch your box, and say a new text on channel 11, to see the floating text changing.
Add a second text and make the texts change every 15 seconds
-> Object 4 Floating Text two texts (a light blue box) -> Script 4 Floating Text two texts
Now we want to have two texts and the floating text will change from one to the other every 15 seconds, and they will stay changing colors.
Do we add a second channel to listen for the second text message? Yes, we will do this.
And do we add a second timer set to 15 seconds? No, this is not possible!
There is only one timer. If we set it to 15 seconds, it will stop being triggered every half second, and our floating texts will change colors very slowly, every 15 seconds.
We need another way to get it.
We will set the timer at the shorter time, 0.5 seconds. And we will have a variable that counts the timer events. Our 15 seconds "timer" is 30 times the 0.5 seconds timer. So we will count 30 timer events before changing between the two floating texts.
Let's look at our script to see how we are doing this all.
At the start we have added a second channel and changed the name of our previous one, in lines 18-19: integer CHANNEL_FLOATING_TEXT_1=11; integer CHANNEL_FLOATING_TEXT_2=12;
Right after it, we have added new variables:
- text1 and text2, for the two floating texts.
- currentText to know what text, 1 or 2, is currently displayed.
- textTicks is the counter of 0.5 timer events, to make our 15 seconds "timer".
At the event state_entry, lines 32-40, we have added:
- the text goes to the variable text1 and a new text goes to text2, the function llGetUsername() takes a UUID (a type key variable) and returns the username.
- the variable is set to text1 to start displaying this text, and currentText is set to 1.
A detail about the functions llGetDisplayName() and llGetUsername(): DisplayName has upper N, and Username has lower n.
At the event touch_start, lines 42-50, we have added:
- the listen() function for the second channel, and changed the name of the first channel.
- the textTicks to start at 30, it will go down by 1 at each timer event, and change text when it arrives to 0.
At the event timer, lines 52-89, we have added:
- the textTicks decreases by 1.
- when the textTicks arrives to 0, it changes the text, and sets textTicks back to 30.
At the event listen, lines 91-106, we have added:
- we are listening to the second channel, and changed the name of the first channel.
- the current text is set to the other one, and textTicks are set to 1, so in the next timer the text will be changed to the new one.
Now touch your box, and say two new texts on channels 11 and 12, to see the text changing.
Use the text on ourselves
-> Script 5 Floating Text on yourself
We can't add a floating text to avatars, only to objects.
The way to have one floating text on us is adding an object with the floating text. We want the text to be over our head so we will attach this object to the skull.
And we don't want the object to be visible, so we will make it transparent.
The function to change the transparency is llSetAlpha. To make our object transparent we will use:
- llSetAlpha(0.0,ALL_SIDES)
0.0 is the value of the alpha (the transparency), it's the same value that we have seen in the 3rd parameter of the llSetText function.
ALL_SIDES is a predefined constant that refers to all the faces of the object.
In the script there are only 2 changes:
- we have added the llSetAlpha function in the event state_entry, in line 34.
- we have moved the lines in the event touch_start event to the event state_entry, the floating text will appear as soon as we add the object without touching it.
Now look at your Class Materials folder and add the object "Floating Text on yourself", to see the floating text on you. You can change the texts using channels 11 and 12.
If you want the floating text to be shown higher or lower you can edit the object size while wearing it. The bigger the object the higher the floating text displays.
Answers to questions
Why color black isn't just <0,0,0> ?
- It's a good idea to avoid using <0,0,0>, pure black, when representing colors because even nearly black reflects some light. If our object reflects no light at all, we will lose details that add to the realism and look of your object.
Is different the transparency in the floating text and the transparency in the editor?
- In the scripts the transparency (alpha) goes from 1 (fully opaque) to 0 (fully transparent), with decimals.
- In the editor it goes from 0 (fully opaque) to 100 (fully transparent), without decimals.
How does the line break to show several lines in the floating text?
- The \n is the new line character.
- We can have a floating text with several lines adding \n between the lines.
- For instance, if we get the data in a loop, we can add it to a variable like this:
- text = text + line + "\n";
- For instance, if we get the data in a loop, we can add it to a variable like this:
What is changed(interger change)?
- changed, in line 66, is an event.
- The event changed is triggered when something related to the object changes.
^^ in the script we are looking if the owner has changed, or the inventory has changed, to reset the script.
Is the event changed triggered when we edit a notecard?
- When we edit a notecard in an object and save it, the old notecard is replaced by a new one with the same name and this counts as a change in the object.
- This means that if we have a configuration notecard that the script reads on initialization, all we have to do to make it update is watch for CHANGED_INVENTORY in the event changed and reset the script at that point, so it will read our updated configuration.
- The event changed is triggered by changes external to the script. Changing the floating text doesn't trigger it.
In the code in the event timer, could we, instead of four if conditions, use only one if and thre else if conditons, so the script would not go through all of them if one TRUE is found?
- Yes, very right, we can use "else if" to optimize the code.
If we delete the script, will the text keep animating?
- Deleting the script, the text will stop changing color, but the text will stay, in the color that it had at the moment of deleting the script.
- The floating text is associated with the object, not with the script, so deleting the script doesnt remove the floating text.
- To remove the text we need set it to empty:
- llSetText( "", <0,0,0>, 0);
Could we listen to 3 channels instead of 2?
- Yes, we can listen up to 64 channels, but better to use only a few.
What is a scrolling floating text?
- There are two ways of scrolling:
- A very long line, where we show only a part of it, and the part shown is moving one charracter to the left with a timer.
- Or many short lines, where we show only some of them, and the part shown is moving one line up with a timer.
Wednesday class
Text with random colors
The idea is to have a floating text that changes its color, as usual with a timer, to a random color.
We will use a list to store the colors. We will see two scripts, one with a strided list and the other with parallel lists.
To select a random color we will use the function llFrand() that have one parameter: the maximum value that we want:
- float randomNumber = llFrand( 10 );
randomNumber will be a value between 0 and 9.999999, but not 10.
If we want a random integer, we typecast the returned value to integer:
- integer randomInteger = (integer)llFrand( 10 );
randomInteger will be a value between 0 and 9.
And to get a random index in a list:
- integer randomIndex = (integer)llFrand( llGetListLength( myList ) );
Let's start with the strided list script.
-> Object 6a Floating Text random colors (strided list) (a red sphere) -> Script 6a Floating Text random colors (strided list)
We have moved our usual colors into the strided list colors in lines 0-18.
In state_entry, lines 27-33, we store the length of the list in totalColors, dividing the length of the list by the stride, 2.
We prepare the text and start the timer.
In the event timer, lines 35-44, we set the floating text.
In line 40, we get the random index in the list of colors, as we have seen before.
We get the name and the color code from the strided list in lines 41-42.
And we set the floating text in line 43.
Let's go on with the parallel lists script.
-> Object 6b Floating Text random colors (parallel lists) (a yellow sphere) -> Script 6b Floating Text random colors (parallel lists)
We two parallel lists, colorNames and colors, in lines 0-13.
From line 20 the script is the same than before, with 3 changes.
In state_entry, line 29, totalColors is the length of the list (we divided by 2 in the other script).
In timer, lines 41-42, we get the values from the list with the variable index (we used index * 2 and index * 2 + 1 in the other script).
Text scrolling
The idea is to have a long floating text, and to show only a part of it, scrolling the text with a timer to show all of it.
There are two options of scrolling:
- A long line that moves from right to left.
- Many short lines that move from bottom to top.
Let's start with scrolling right to left.
-> Object 7 Floating Text scrolling (one long line) (a green sphere) -> Script 1 7 Floating Text scrolling (one long line)
In line 0, MESSAGE_LENGTH is the number of characters that we will display in the floating text. You can change this value to show more or less.
In line 4 we set the text to display.
Let's go to state_entry, lines 13-22.
We use the function llStringLength() to get the quantity of characters in the text, in line 15.
llStringLength() takes a string as parameter and returns the number of characters in the string.
In lines 16-17 we check is the text is too short, shorter than the characters displayed.
The text is expected to be long, but if it wasn't the case, to avoid trying to show more characters than exist, we change the characters to display.
We set the first character to display in line 18. The positions of the characters in a string starts with 0 (like the indexes in a list).
And we start the timer in line 21.
touch_start is just saying a Halloween greeting.
llRegionSayTo() says the message to the avatar in its first parameter, in this case to the toucher.
Let's go to timer, lines 29-37.
textShown is the part of the text that we are going to display.
When we are arriving to the end of text, we start displaying the start of the text again.
So, sometimes we will be displaying the end of the string and the start of the string.
To make it easier we add the string to itself, text + text. In this way we have all the text that we need all together.
We use llGetSubString() to get the part of the "doubled" string that we want.
llGetSubString() has three parameters:
- the string
- the start of the substring, the first position is 0
- the end of the substring
In line 32 we get the text to display, starting at textPos, and calculating the ending for the length of MESSAGE_LENGTH.
In line 34 we increase the position, next time we will display starting with the next character.
When the position arrives to the length of the string, we start with position 0 again, in lines 45-46.
And now scrolling bottom to top. The script is structured in the same way.
-> Object 7 Floating Text scrolling (several lines) (a light blue sphere) -> Script 1 7 Floating Text scrolling (several lines)
In line 0, MESSAGE_LINES is the number of lines that we will display in the floating text. You can change this value to show more or less.
In lines 4-15 there is the text to display.
Let's go to state_entry, lines 24-32.
We get the quantity of lines, in line 25.
In lines 26-27 we check is the text has too few lines, less than the lines displayed.
To avoid trying to show more lines than exist, we change the lines to display.
We set the first line to display in line 28.
And we start the timer in line 31.
Let's go to timer, lines 29-37.
textShown is the part of the text that we are going to display.
When we are arriving to the last lines of text, we start displaying the first lines of the text again.
In the same way that in the previous script, we are doubling the list in line 42, with text + text.
llList2List() returns some elements of the list. It has three parameters:
- the list
- the index of the first item
- the index of the last item
In line 42 we get the list with the text to display, starting at textPos, and calculating the ending for the length of MESSAGE_LINES.
To set the floating text we use llDumpList2String(). It has two parameters:
- the list
- a separator
It returns a string with all the elements of the list, separated, in this case, with new lines, that is what we need for the floating text.
Another use of llDumpList2String() is for debugging, to show us the content of a list:
- llOwnerSay( llDumpList2String( myList, ", " ) );
Saying a string with the elements of the list separated by commas.
In line 44 we increase the position, next time we will display starting with the next line.
When the position arrives to the lines of the text, we start with line 0 again, in lines 45-46.
Answers to questions
Would it be better for memory to instantiate the color list in a function instead of at the global level?
- In general, if we can define a variable as local instead of global is better for memory.
"But with a constant list like colors, it doesn't actually save memory to declare it locally because the compiler still has to store the constant information on the heap. Then when we declare the local variable, what is put on the stack is in essence a pointer to the stored data.
What are we chosing as parameter for llFrand?
- In the script we choose llFrand( llGetListLength( colors ) )
- There are 17 colors, so it's llFrand( 17 ), which returns values from 0 to 16.999999, and (integer)llFrand( 17 ) returns from 0 to 16, that are the indexes for a list of 17 elements
Why do we multiply by 2 and then add 1?
- The strided list is color name and color code
- The index of, for instance, OLIVE, is 8, which is the fifth stride (stride number 4, starting with 0) multiplied by 2.
- The color code for OLIVE is the next element in the list, index 9, so the color code is at 4 * 2 + 1
- We use the list as a strided list, because we know how it is designed, but internally is just a list as any other.
"index" is used to count the number of items in a list or in a strided list? or to identify a given item in the list?
- In this case the index is the number of the stride, not the index in the list. It would be better to call it strideNum instead of index.
Has "index" a general meaning like "integer"?
- No, "index" a just a name of a variable, instead of "index" we could use "elementNumber" or "pieceOfList", etc.
How did we insert the special characters? Using a character code?
- No, copy-pasting. This webpage has lots of them:
https://apps.timwhitlock.info/emoji/tables/unicode
How to reset the scrolling text to read it again from the start when the text is very long?
- The only way now is to reset the script. We could modify the script to add an option to do it.
Does llDumpList2String display somewhere?
- No, it doesn't display. It returns a string, with the elements in the list, separated with the separator in its second parameter.
How does the script go through the whole thing, five lines at a time?
- First time we show lines 0 to 4, in the next timer from 1 to 5, then from 2 to 6, etc
- Later we will show from 7 to 9 and from 0 to 1.
- This is way we use text + text , so we have the first lines right after the last lines and we can get the five lines that we want using only one function.
Is the separator in llDumpList2String a line break?
- Yes, to set the floating text, we separate the lines in the string with a newlline, \n.
- We could do it with a loop for, adding lines one by one and with the \n at the end, but we have llDumpList2String() that does it more easily.
Can we write "rainbow" with each letter in a rainbow color?
- No, the floating text can only have one color, for all the text, we can change colors, but only show one color at the same time.
- To get a floating text in different colors, we could use several prims linked together, each one with its floating text in one color.