Alife Virtual School Class 58: Advanced LSL: Working with Linksets
Advanced LSL: Working with Linksets — Free class in Alife Virtual School
Welcome, creators, to the Alife Virtual School. In the expansive digital universe of the metaverse, the ability to create complex, interactive objects is the cornerstone of innovation. Many aspiring developers hit a wall when their creations grow beyond a single prim. How do you control a detailed vehicle, a piece of interactive furniture, or a complex machine without a tangled mess of scripts? The answer lies in mastering linksets. This comprehensive class, Advanced LSL: Working with Linksets, is your guide to transforming collections of simple prims into a single, cohesive, and efficiently controlled object. We will dive deep into mastering linkset data and messaging in LSL, focusing on the powerful functions llGetLinkNumber, llSetLinkPrimitiveParamsFast, and the elegant concept of Linkset Data (LSD). This is a vital skill for anyone serious about high-level creation in a free 3D world like Alife Virtual, where your imagination is the only limit, not your wallet.
The Alife Advantage: Build Without Financial Barriers
In many virtual worlds, advanced scripting and building come with significant real-world costs that stifle creativity. Experimentation is expensive. Failure costs money. In Alife Virtual, we've removed those barriers entirely, creating a truly 100% free economy for creators. Let's compare the cost of tackling a project that requires this level of advanced scripting:
| Feature / Cost | Alife Virtual | Competitors (e.g., Second Life) |
|---|---|---|
| Private Building Space | FREE 65,536 sqm Private Island, forever. | ~$300 USD per month for a full region. |
| Asset Uploads (Mesh, Textures, etc.) | FREE and Unlimited. | L$10 (~$0.04 USD) per upload. A complex object can require dozens of uploads, costing real money. |
| Cost to Learn & Experiment | $0.00. You can build, script, re-upload, and re-link your project a thousand times without ever paying a fee. | High and Ongoing. Monthly land fees and per-upload costs create financial pressure and punish mistakes. |
This freedom is the Alife Advantage. Here, you can follow this advanced tutorial, build the examples, and develop your own complex creations on your own free private island, using free unlimited uploads. Your journey into becoming an expert LSL scripter is supported, not monetized.
What You Will Learn
By the end of this class, you will have mastered the techniques to control multi-prim objects with grace and efficiency. You will be able to:
- Understand the fundamental structure of a linkset, including the roles of the root and child prims.
- Confidently use
llGetLinkNumber()to allow scripts to identify their position within a linkset. - Master
llSetLinkPrimitiveParamsFast()to issue commands to multiple prims simultaneously, dramatically improving performance. - Design and implement a Linkset Data (LSD) system to create flexible, easily configurable objects.
- Move beyond single-script-per-prim thinking to a more powerful, centralized control model.
- Write cleaner, more efficient, and more maintainable code for your complex creations.
Prerequisites
This is an advanced class. To succeed, you should already be comfortable with the following LSL concepts:
- Basic LSL syntax (variables, functions, control structures like
if/elseandforloops). - Common events, particularly
state_entry()andtouch_start(). - Basic prim manipulation functions like
llSetColor(),llSetAlpha(), andllSetText(). - The concept of lists and basic list manipulation.
- A general understanding of the client-server relationship in an open simulator environment. Alife Virtual is fully compatible with the popular Firestorm Viewer, so you'll have the best tools at your disposal.
Tutorial: From Clunky to Cohesive
Let's begin our journey. We'll start with the basics of linkset structure and build our way up to a sophisticated control system.
Part 1: The Anatomy of a Linkset
A linkset is a collection of primitive objects (prims) that have been linked together to behave as a single object. When you link prims, you establish a parent-child hierarchy.
- The Root Prim: This is the last prim you selected before linking. It is the "parent" of the entire object. The root prim is always assigned link number 1.
- Child Prims: All other prims in the linkset are child prims. They are assigned link numbers 2, 3, 4, and so on, based on the order in which they were selected and linked.
- Link Number 0: This special number is returned by
llGetLinkNumber()if the prim is not part of a linkset (i.e., it's a root prim of a single-prim object).
The script that controls the entire linkset almost always resides in the root prim (link number 1). This is our "command center."
Part 2: Finding Your Place with llGetLinkNumber()
Before a script can do anything useful in a linkset, it often needs to know where it is. Is it in the root? Is it in a specific child prim? The function llGetLinkNumber() provides this crucial piece of information.
Example: The Self-Identifier
Create two prims (e.g., two cubes). Place the following script into both prims. Then, select the first prim, then shift-select the second prim, and link them (Ctrl+L). The second prim you selected becomes the root.
default
{
state_entry()
{
integer myLinkNumber = llGetLinkNumber();
if (myLinkNumber == 0)
{
llOwnerSay("I am not linked.");
}
else if (myLinkNumber == 1)
{
llOwnerSay("I am the ROOT prim of this linkset. My link number is 1.");
}
else
{
llOwnerSay("I am a CHILD prim. My link number is " + (string)myLinkNumber + ".");
}
}
}
When you rez the object, you'll see messages from both prims. The root will identify itself as number 1, and the child will identify itself as number 2. If you unlink them, then touch each prim to reset the script, they will both report "I am not linked." This function is the first step in creating linkset-aware logic.
Part 3: The Powerhouse: llSetLinkPrimitiveParamsFast()
This function is the heart of efficient linkset control. In the past, to change the color of three different prims, you might have called llSetLinkPrimitiveParams() three times in a row. This creates three separate update messages to the simulator. llSetLinkPrimitiveParamsFast(), however, bundles all the instructions into a single, highly efficient message.
The syntax is: llSetLinkPrimitiveParamsFast(integer link, list params)
integer link: The target prim's link number. You can also use the constantLINK_SETto target all prims in the linkset, orLINK_ALL_CHILDREN, orLINK_ALL_OTHERS.list params: A list containing parameter-value pairs. This is where you define what you want to change.
Example: The Instant Traffic Light
Let's build a simple traffic light. 1. Create three spheres and one box prim. 2. Stack the spheres vertically: top, middle, bottom. Place the box at the base. 3. Select the top sphere, then the middle, then the bottom, and finally the box. Link them. The box is now the root (link 1), the bottom sphere is link 2, middle is 3, and top is 4. 4. Place the following script inside the root prim (the box).
// --- Constants for readability ---
integer RED_LIGHT = 4; // Top sphere
integer YELLOW_LIGHT = 3; // Middle sphere
integer GREEN_LIGHT = 2; // Bottom sphere
// --- Color Vectors ---
vector COLOR_RED_ON = <1.0, 0.0, 0.0>;
vector COLOR_YELLOW_ON = <1.0, 1.0, 0.0>;
vector COLOR_GREEN_ON = <0.0, 1.0, 0.0>;
vector COLOR_OFF = <0.2, 0.2, 0.2>; // Dark grey
// --- State variable ---
integer gState = 0; // 0=Green, 1=Yellow, 2=Red
// --- Function to update the lights ---
updateLights()
{
if (gState == 0) // GO
{
llSetLinkPrimitiveParamsFast(GREEN_LIGHT, [PRIM_COLOR, ALL_SIDES, COLOR_GREEN_ON, 1.0, PRIM_FULLBRIGHT, ALL_SIDES, TRUE]);
llSetLinkPrimitiveParamsFast(YELLOW_LIGHT, [PRIM_COLOR, ALL_SIDES, COLOR_OFF, 1.0, PRIM_FULLBRIGHT, ALL_SIDES, FALSE]);
llSetLinkPrimitiveParamsFast(RED_LIGHT, [PRIM_COLOR, ALL_SIDES, COLOR_OFF, 1.0, PRIM_FULLBRIGHT, ALL_SIDES, FALSE]);
}
else if (gState == 1) // CAUTION
{
llSetLinkPrimitiveParamsFast(GREEN_LIGHT, [PRIM_COLOR, ALL_SIDES, COLOR_OFF, 1.0, PRIM_FULLBRIGHT, ALL_SIDES, FALSE]);
llSetLinkPrimitiveParamsFast(YELLOW_LIGHT, [PRIM_COLOR, ALL_SIDES, COLOR_YELLOW_ON, 1.0, PRIM_FULLBRIGHT, ALL_SIDES, TRUE]);
llSetLinkPrimitiveParamsFast(RED_LIGHT, [PRIM_COLOR, ALL_SIDES, COLOR_OFF, 1.0, PRIM_FULLBRIGHT, ALL_SIDES, FALSE]);
}
else // STOP
{
llSetLinkPrimitiveParamsFast(GREEN_LIGHT, [PRIM_COLOR, ALL_SIDES, COLOR_OFF, 1.0, PRIM_FULLBRIGHT, ALL_SIDES, FALSE]);
llSetLinkPrimitiveParamsFast(YELLOW_LIGHT, [PRIM_COLOR, ALL_SIDES, COLOR_OFF, 1.0, PRIM_FULLBRIGHT, ALL_SIDES, FALSE]);
llSetLinkPrimitiveParamsFast(RED_LIGHT, [PRIM_COLOR, ALL_SIDES, COLOR_RED_ON, 1.0, PRIM_FULLBRIGHT, ALL_SIDES, TRUE]);
}
}
default
{
state_entry()
{
llSetText("Click to cycle", <1,1,1>, 1.0);
updateLights();
}
touch_start(integer total_number)
{
gState++;
if (gState > 2)
{
gState = 0; // Cycle back to green
}
llOwnerSay("Changing state to: " + (string)gState);
updateLights();
}
}
When you click the root prim, the lights will cycle. Notice how we use one llSetLinkPrimitiveParamsFast call per prim we want to change in the updateLights function. Each call efficiently sets both the color and the fullbright property in a single command. This is vastly superior to sending multiple commands or using slower functions.
Pro Tip: You can combine multiple parameter changes for the same prim into one list for
llSetLinkPrimitiveParamsFast. As shown in the example,[PRIM_COLOR, ..., PRIM_FULLBRIGHT, ...]is perfectly valid and highly efficient.
Part 4: The Ultimate Technique: Linkset Data (LSD)
Hard-coding link numbers like integer RED_LIGHT = 4; works, but it's brittle. What happens if you need to add another part, or re-link the object in a different order? All your link numbers would be wrong, and your script would break. You'd have to edit the script every time.
Linkset Data (LSD) is an advanced design pattern that solves this problem. The concept is simple: instead of hard-coding the configuration in the script, you store the configuration data in the object itself, typically in the Description field of each child prim.
The master script in the root prim then reads this data on startup, building a dynamic map of the linkset. This makes your objects incredibly flexible and easy to modify without ever touching the code.
The LSD Workflow:
- Store Data: Edit each child prim and put configuration data into its Description field. Use a simple, parsable format. E.g.,
part=light;color=red; - Scan on Startup: In the root script's
state_entry, loop from link 2 to the total number of prims (llGetNumberOfPrims()). - Read and Parse: For each prim, use
llGetLinkPrimitiveParams(link_num, [PRIM_DESC])to get the description. Parse this string (e.g., withllParseString2List) to understand what that prim is supposed to be. - Build a Data Map: Store the parsed information in a master list or strided list. For example, you could store the link number, its role, and its color.
- Operate: When an event occurs (like a touch), the script consults its data map to decide what to do, rather than using hard-coded link numbers.
Example: The Dynamic Control Panel
Let's create a 3-button panel that controls the color of a display prim.
1. Create 4 prims: 3 small "button" prims and 1 larger "display" prim.
2. Link them, making sure the larger display prim is the root (select it last).
3. Configure the child prims (the buttons):
- Edit the first button. In the "Description" field (in the edit floater), enter: button|red
- Edit the second button. Description: button|green
- Edit the third button. Description: button|blue
4. Add scripts:
- Put this simple "messenger" script into all three button prims:
// --- Child Prim Script (for all buttons) ---
default
{
touch_start(integer total_number)
{
// Tell the root prim that I was touched.
// I'll send my link number as the integer parameter.
llMessageLinked(LINK_ROOT, llGetLinkNumber(), "", "");
}
}
- Put this advanced "master" script into the root display prim:
// --- Root Prim Script (The Master Controller) ---
// This list will store our configuration map.
// Format: [ link_num1, "red", link_num2, "green", ... ]
list gConfigMap;
// Color vectors for easy lookup
vector VEC_RED = <1,0,0>;
vector VEC_GREEN = <0,1,0>;
vector VEC_BLUE = <0,0,1>;
// Function to scan the linkset and build the data map
scanLinkset()
{
gConfigMap = []; // Clear the old map
integer numPrims = llGetNumberOfPrims();
// We only scan children, so we start at 2
if (numPrims > 1)
{
integer i;
for (i = 2; i <= numPrims; ++i)
{
// Get the description of the child prim
list primParams = llGetLinkPrimitiveParams(i, [PRIM_DESC]);
string description = llList2String(primParams, 0);
// Parse the description string
list parsedDesc = llParseString2List(description, ["|"], []);
if (llList2String(parsedDesc, 0) == "button")
{
// It's a button! Get its assigned color.
string colorName = llList2String(parsedDesc, 1);
// Add this button's info to our map
gConfigMap += [i, colorName];
// For feedback, let's label the button
llSetLinkText(i, "Set " + colorName, <1,1,1>, 1.0);
}
}
}
llOwnerSay("Scan complete. Found " + (string)(llGetListLength(gConfigMap) / 2) + " configured buttons.");
}
default
{
state_entry()
{
llOwnerSay("Initializing controller...");
scanLinkset();
}
// This event fires when we get a message from a child prim
link_message(integer sender_num, integer num, string str, key id)
{
// 'sender_num' is the link number of the prim that sent the message
// Find the sender's link number in our config map
integer index = llListFindList(gConfigMap, [sender_num]);
if (index != -1)
{
// We found the button in our map!
// The color name is the next item in the list.
string colorToSet = llList2String(gConfigMap, index + 1);
llOwnerSay("Button " + (string)sender_num + " pressed. Setting color to " + colorToSet);
vector newColor;
if (colorToSet == "red") newColor = VEC_RED;
else if (colorToSet == "green") newColor = VEC_GREEN;
else if (colorToSet == "blue") newColor = VEC_BLUE;
// Set the root prim's (my) color
llSetColor(newColor, ALL_SIDES);
}
}
// If the linkset changes, we should re-scan.
changed(integer change)
{
if (change & CHANGED_LINK)
{
llOwnerSay("Linkset changed, re-scanning...");
llResetScript(); // Easiest way to re-run state_entry and scan
}
}
}
Now, when you click the button with "button|red" in its description, it sends its link number to the root. The root script looks up that link number in its gConfigMap, finds the associated color "red", and changes its own color accordingly. You can now add a fourth button, change their order, or even remove one, and the system will automatically adapt the next time it resets. This is the power and flexibility of the LSD pattern.
Common Mistakes & Pro Tips
Mistake: Forgetting that the root is link 1, not 0. LSL lists are 0-indexed, but link numbers start at 1. This is a frequent source of off-by-one errors.
Pro Tip: Use the
LINK_SETconstant inllSetLinkPrimitiveParamsFast(LINK_SET, [...])to apply a change to every prim in the linkset at once, including the root. This is perfect for setting alpha fades or applying a texture to an entire object.
Mistake: Using
llSetLinkPrimitiveParamsin aforloop. While it works, it generates significant script overhead and network traffic compared to usingllSetLinkPrimitiveParamsFast. On a complex object with many prims, the difference in performance is enormous.Pro Tip: When using the LSD pattern, provide feedback. The example script uses
llSetLinkTextto label the buttons it finds. This makes it immediately obvious if your scan was successful and helps with debugging.
Mistake: Hard-coding link numbers in a complex, evolving project. This creates a maintenance nightmare. Embrace the LSD pattern early for any object you might modify or expand later.
Pro Tip: Your LSD format can be as complex as you need. For a vehicle, a prim's description could be
part=wheel;pos=front_left;action=spin. The root script can parse this to control all vehicle functions from one central location.
Advanced Applications in the Metaverse
Mastering these skills opens the door to creating truly professional-grade content, a key part of any thriving virtual economy.
- Complex Vehicles: A single root script can control lights, doors, dashboard gauges (as linked prims), engine sounds, and particle effects.
- Interactive Furniture: Manage multiple sit targets, texture-change menus, and animations across a multi-prim sofa or bed.
- HUDs (Heads-Up Displays): Most complex HUDs are linksets attached to the screen. Each button is a child prim with its own function defined via LSD, all controlled by one root script.
- Art & Machinery: Create kinetic sculptures with dozens of synchronized moving parts, or factory machines with interactive levers and displays, all running on a single, efficient script.
Practice Exercise
Time to put your new knowledge to the test. This exercise will solidify your understanding.
- Create a new object by linking 5 prims together. Make a central prim the root (link 1) and arrange the other 4 prims (links 2, 3, 4, 5) around it.
- Task 1 (The Basics): Write a script for the root prim. When the root is touched, it should use
llSetLinkPrimitiveParamsFastto make prims 2 through 5 flash a sequence of colors, one after the other, with a 0.5-second delay between changes. - Task 2 (LSD Implementation): Modify your project.
- In the description field of prim 2, write:
light|<1,0,0> - In the description of prim 3, write:
light|<0,1,0> - In the description of prim 4, write:
light|<0,0,1> - In the description of prim 5, write:
light|<1,1,0> - Update your root script to use the LSD pattern. On
state_entry, it should scan all children, find any prims described as a "light", and parse the vector color. When the root is touched, it should set the color of each "light" prim to the color specified in its description.
- In the description field of prim 2, write:
This exercise combines everything we've learned: linkset structure, efficient commands, and the flexible LSD pattern.
Frequently Asked Questions (FAQ)
- 1. What is the real difference between
llSetLinkPrimitiveParamsandllSetLinkPrimitiveParamsFast? - Performance.
llSetLinkPrimitiveParamsis an older function that sends one command at a time. If you use it in a loop to change 10 prims, you send 10 separate updates.llSetLinkPrimitiveParamsFastbundles a single prim's changes into one update. While it might seem like you're still calling it multiple times in a loop, the underlying engine implementation is far more optimized and results in significantly less script time and server lag. - 2. What is the maximum number of prims in a linkset in Alife Virtual?
- Alife Virtual, being based on modern open simulator code, supports a high prim count for linksets, typically 256 prims or more depending on the region configuration. This gives you immense freedom for complex builds, far beyond what was possible in the early days of virtual worlds.
- 3. Why not just put a small script in every prim instead of using LSD?
- Three reasons: efficiency, memory, and maintenance. A single, larger script uses less total memory than 50 tiny scripts. It's also more CPU-efficient as the simulator only has to manage one script state machine. Most importantly, if you need to update the logic, you only have to edit one script, not 50.
- 4. Can a script in a child prim control another child prim directly?
- Yes, using
llMessageLinked(target_link_num, ...). However, this often leads to "spaghetti code" where every prim needs to know about every other prim. It's a much cleaner and more scalable design to have all children report to the root (the "command center" pattern) and let the root issue all the commands. This centralizes your logic. - 5. What happens to the scripts if I link two objects that both already contain scripts?
- The scripts in all prims remain and continue to run in their new context. The prim that becomes the root will now have a link number of 1, and the other prims (and their scripts) will be assigned new child link numbers. This is why using the
changed(CHANGED_LINK)event to trigger a re-scan or reset is so important for robust linkset scripts.
Your Creative Sandbox Awaits
You now possess the knowledge to build bigger, better, and smarter. The techniques of linkset control are what separate casual builders from expert creators in the LSL scripting world. The best part? In Alife Virtual, you have the ideal environment to practice and perfect these skills.
Imagine what you can build on your FREE 65,536 sqm private island, with FREE unlimited uploads for all your meshes and textures. There are no fees holding you back. Your free pro mesh avatar is ready, and the world is waiting for your creations.
Ready to build without limits?
Join Alife Virtual Today, claim your free island, and start your journey as an advanced creator in the next-generation metaverse.
🎓 Ready to Practice In-World?
Get your FREE island and practice everything you just learned — no credit card, no monthly fees.
Claim Your Free Island Now →No credit card required · Takes 2 minutes · Your island is FREE forever