CLASS 60: Scripting Interactive Furniture (Sit Targets)
Scripting Interactive Furniture (Sit Targets) โ Free class in Alife Virtual School
Welcome, creators, to Alife Virtual School! In today's master-level workshop, we're diving deep into one of the most fundamental skills for building an immersive metaverse: Scripting Interactive Furniture (Sit Targets). Forget static, lifeless objects. We're going to teach you how to command avatars, positioning them with precision to create everything from cozy armchairs to complex, multi-person interactive experiences. This skill is the bedrock of social spaces, engaging homes, and dynamic role-playing environments. In a free 3D world like Alife Virtual, where your imagination is the only currency, mastering this technique will set your creations apart and elevate them from simple objects to truly interactive art.
The Alife Advantage: Create Without Cost
Before we write a single line of code, let's talk about the economic reality of creation in virtual worlds. Building high-quality, interactive furniture in other platforms can be a costly endeavor. You're often nickel-and-dimed for every texture, every mesh model, and every animation you upload. Worse, the very land you build on comes with staggering monthly fees. In Alife Virtual, we've removed those barriers entirely, fostering a truly free virtual economy.
Let's compare the cost of developing the project in this tutorial:
| Development Aspect | Alife Virtual Cost | Typical "Second Life Alternative" Cost |
|---|---|---|
| Private Island (65,536 sqm) | $0 / Forever | ~$300 USD / month |
| Mesh Chair Model Upload | $0 (Unlimited) | Upload fees + Land Impact fees |
| Texture Upload (Wood, Fabric) | $0 (Unlimited) | L$10 (~$0.04) per texture |
| Custom Sit Animation Upload | $0 (Unlimited) | L$10 (~$0.04) per animation |
| Total First Month Cost | $0 | $300+ |
With Alife Virtual, you get a FREE Private Island to build, test, and showcase your work. You get FREE unlimited uploads for all your assets. You even start with a FREE pro mesh avatar. We are a true Open Simulator platform, fully compatible with the popular Firestorm Viewer, dedicated to empowering creators, not billing them. Now, let's use that freedom to build something amazing.
What You Will Learn in This Class
By the end of this tutorial, you will be able to:
- Understand and use the
llSitTargetfunction to define an avatar's sitting position and orientation. - Leverage
llSetLinkPrimitiveParamsFastto set sit targets on any prim within a linked object. - Build a robust, multi-seat furniture item (like a sofa or bench) that can manage multiple avatars.
- Integrate custom animations to create a seamless "pose" system without clunky pose balls.
- Manage avatar permissions for animations and track who is sitting where.
- Debug and troubleshoot common issues with sit targets and avatar positioning.
Prerequisites
This is an intermediate LSL scripting class. Before you begin, you should be comfortable with the following concepts:
- Basic LSL script structure: states, events (
touch_start,state_entry), and functions. - Understanding of variables (integers, floats, vectors, rotations, lists).
- Creating and linking primitive objects (prims) in-world.
- Adding scripts and other assets to an object's inventory.
- Navigating the build/edit tools in the Firestorm viewer.
Part 1: The Foundation - A Simple Sit Target
At its core, a "sit target" is an invisible point in space defined by an offset and rotation relative to a prim's center. When an avatar "sits" on an object, the server moves them to this exact point. Let's start with a single prim chair.
Step 1: Create Your Object and Script
- In-world, right-click the ground and choose Create to rez a simple cube prim.
- Stretch and shape it to resemble a basic chair or stool.
- With the object selected, go to the Content tab in your edit window.
- Click New Script. This will create and open a new script named "New Script". Rename it to something descriptive, like
simple_sit_script.
Step 2: The Basic Sit Script
Double-click your new script to open the editor. Delete the default content and replace it with this:
// Simple Sit Target Script - Alife Virtual School Class 60
default
{
state_entry()
{
// Set the sit target.
// llSitTarget(<offset>, <rotation>);
// <0.0, 0.0, 0.5> is 0.5 meters directly above the prim's center.
// ZERO_ROTATION means the avatar will face the same direction as the prim.
llSitTarget(<0.0, 0.0, 0.5>, ZERO_ROTATION);
// Optional: Change the hover text when someone sits.
llSetSitText("Relaxing");
}
}
Step 3: Understanding the Code
state_entry(): This event runs once when the script starts or is reset. It's the perfect place to set up our sit target.llSitTarget(<offset>, <rotation>): This is the key function.- offset: A
vectorthat defines the position of the avatar's hips relative to the prim's center (its root). In our example,<0.0, 0.0, 0.5>places the avatar 0.5 meters up on the Z-axis. - rotation: A
rotation(quaternion) that defines which way the avatar will face, relative to the prim's orientation.ZERO_ROTATIONmakes them face the same direction as the prim's forward (X) axis.
- offset: A
llSetSitText("Relaxing"): This changes the floating text above your avatar's head from "Sitting" to "Relaxing". It's a small touch that adds a lot of polish.
Save the script. Now, right-click your chair and choose Sit Here. Your avatar should snap to the position you defined! If they are floating too high or sunk too low, adjust the Z-value (the third number) in the llSitTarget vector and save again. The script will reset, and you can try sitting again.
Pro Tip: The "Helper Prim" Technique
Getting the perfect offset and rotation can be tricky. A classic method is to use a helper prim. Rez a small cube, color it brightly, and set its transparency to 50%. Manually position this helper prim exactly where you want your avatar's hips to be. Then, link it to your main chair (selecting the chair last to make it the root). Use a separate script or the build tool's info to get the helper prim's local position and rotation. These values are your perfect offset and rotation forllSitTarget! You can delete the helper prim afterward.
Part 2: Multi-Prim Objects and llSetLinkPrimitiveParamsFast
What if your furniture is complex? A sofa is made of many prims. The llSitTarget function only works on the root prim of a linked set. To set a sit target on a child prim (like the second cushion of a bench), we need a more powerful tool.
Enter llSetLinkPrimitiveParamsFast.
Step 1: Build a Two-Prim Bench
- Rez two cube prims. Position them side-by-side to form a simple bench.
- Select both prims, ensuring you select the one you want to be the "root" prim last. The last selected prim will have a yellow outline, while the others will be blue.
- Press Ctrl+L to link them. The entire object will now be outlined in yellow. The root is link number 1, and the child prim is link number 2.
- Create a new script in the root prim's inventory called
linked_sit_script.
Step 2: Scripting a Child Prim Sit Target
We want to place a sit target on the second prim (link number 2). Here's how:
// Linked Sit Target Script - Alife Virtual School Class 60
default
{
state_entry()
{
// Define the sit target parameters for the child prim.
vector sit_offset = <0.0, 0.0, 0.25>; // Adjust as needed for your build
rotation sit_rotation = ZERO_ROTATION;
// Apply the sit target to LINK NUMBER 2.
// PRIM_SIT_TARGET is the rule we are applying.
llSetLinkPrimitiveParamsFast(2, [PRIM_SIT_TARGET, sit_offset, sit_rotation]);
// It's good practice to also set a sit target on the root prim,
// even if it's "off" the object, to avoid default sit behavior.
llSitTarget(<0.0, 0.0, -5.0>, ZERO_ROTATION); // Puts it 5m below ground
}
}
Step 3: Understanding the Code
llSetLinkPrimitiveParamsFast(link_num, [rules]): This is our workhorse.- link_num: An
integerspecifying which prim in the linkset to modify.2refers to the first child prim we linked. - [rules]: A
listcontaining the parameters to change. The format is crucial. For sit targets, the rule isPRIM_SIT_TARGET, and it must be followed by avectoroffset and arotation.
- link_num: An
- We also set a "dummy" sit target on the root prim far away. Why? If an avatar right-clicks the root prim and chooses "Sit Here," we don't want them to sit in the default, awkward position at its center. This sends them safely out of sight.
Save the script. Now, right-click the second cushion of your bench. You should see the "Sit Here" option, and selecting it will place your avatar on that specific prim.
Part 3: The Holy Grail - A Multi-Seat, Multi-Pose Sofa
Now we combine everything to build a truly dynamic piece of furniture. We will create a three-seat sofa where a user can touch it to sit in the next available spot, and each spot will have its own sitting animation.
Step 1: The Build and Asset Prep
- Build a three-cushion sofa. Link all the prims together, making the central base prim the root (link number 1). Let's assume the cushions are link numbers 2, 3, and 4.
- Find or create three sitting animations. For this example, we'll call them
"sit_casual","sit_reading", and"sit_crosslegged". - Place a new script (e.g.,
sofa_controller) and the three animation files into the root prim's inventory.
Step 2: The Advanced Controller Script
This script is more complex. It needs to track seats, handle users sitting and standing, and manage animation permissions. Read the comments carefully.
// Multi-Seat Sofa Controller - Alife Virtual School Class 60
// --- CONFIGURATION ---
list g_sit_targets_prims = [2, 3, 4]; // Link numbers of the seat prims
list g_sit_targets_offsets = [<0,0,0.2>, <0,0,0.2>, <0,0,0.2>]; // One offset per prim
list g_sit_targets_rots = [ZERO_ROTATION, ZERO_ROTATION, ZERO_ROTATION]; // One rotation per prim
list g_sit_animations = ["sit_casual", "sit_reading", "sit_crosslegged"]; // Animations from inventory
// --- GLOBALS ---
list g_seat_status; // Will store the KEY of the avatar in each seat, or NULL_KEY if empty.
integer g_num_seats;
// --- FUNCTIONS ---
f_init()
{
g_num_seats = llGetListLength(g_sit_targets_prims);
g_seat_status = []; // Clear the list
integer i;
for (i = 0; i < g_num_seats; ++i)
{
// Initialize all seats as empty
g_seat_status += [NULL_KEY];
// Set up the sit target for each prim using SLPPF
integer link_num = llList2Integer(g_sit_targets_prims, i);
vector offset = llList2Vector(g_sit_targets_offsets, i);
rotation rot = llList2Rot(g_sit_targets_rots, i);
llSetLinkPrimitiveParamsFast(link_num, [PRIM_SIT_TARGET, offset, rot, PRIM_SIT_TARGET_VOLUME, <0.5, 0.5, 0.5>]);
}
// Set the text to show how many seats are free
llSetText("Click to Sit (" + (string)g_num_seats + " seats free)", <1,1,1>, 1.0);
}
// --- EVENTS ---
default
{
state_entry()
{
f_init();
}
on_rez(integer start_param)
{
llResetScript();
}
touch_start(integer total_number)
{
key toucher = llDetectedKey(0); // Who touched the object?
// Check if the toucher is already sitting
if (llListFindList(g_seat_status, [toucher]) != -1)
{
// Avatar is already sitting, so let's make them stand up
llUnSit(toucher);
return;
}
// Find the first available seat
integer free_seat_index = llListFindList(g_seat_status, [NULL_KEY]);
if (free_seat_index == -1)
{
llSay(0, "Sorry, this sofa is full.");
return;
}
// A seat is free! Let's sit the avatar there.
// We use llAvatarOnSitTarget() which returns the key of the sitter.
// This is better than llSitOnLink because it respects the defined sit target.
llAvatarOnSitTarget(toucher, llList2Integer(g_sit_targets_prims, free_seat_index));
}
changed(integer change)
{
// This event is key! It fires when an avatar sits or stands.
if (change & CHANGED_LINK)
{
integer i;
integer free_seats_count = 0;
for (i = 0; i < g_num_seats; ++i)
{
integer link_num = llList2Integer(g_sit_targets_prims, i);
key sitter = llGetLinkKey(link_num); // Get key of avatar on this link
if (sitter != NULL_KEY) // Seat is now occupied
{
if (llList2Key(g_seat_status, i) == NULL_KEY) // Was it previously empty?
{
// A new person just sat down. Store their key.
g_seat_status = llListReplaceList(g_seat_status, [sitter], i, i);
// Request permission to animate them
llRequestPermissions(sitter, PERMISSION_TRIGGER_ANIMATION);
}
}
else // Seat is now empty
{
if (llList2Key(g_seat_status, i) != NULL_KEY) // Was it previously occupied?
{
// Person stood up. Stop their animation and clear the seat.
key old_sitter = llList2Key(g_seat_status, i);
llStopAnimation(llList2String(g_sit_animations, i), old_sitter);
g_seat_status = llListReplaceList(g_seat_status, [NULL_KEY], i, i);
}
free_seats_count++;
}
}
llSetText("Click to Sit (" + (string)free_seats_count + " seats free)", <1,1,1>, 1.0);
}
}
run_time_permissions(integer perm)
{
if (perm & PERMISSION_TRIGGER_ANIMATION)
{
// We have permission! Now find which avatar gave it and animate them.
integer i;
for (i = 0; i < g_num_seats; ++i)
{
key sitter = llList2Key(g_seat_status, i);
// Check if this sitter is the one who just granted permission
if (llGetPermissionsKey() == sitter)
{
// Stop any other animations they might be playing
llStopAnimation("sit", sitter);
// Start our custom animation
llStartAnimation(llList2String(g_sit_animations, i), sitter);
}
}
}
}
}
Common Mistake: Incorrect Link Numbers
The most frequent error in multi-prim builds is getting the link numbers wrong. If your script isn't working, use a simple debug script that, on touch, doesllSay(0, (string)llGetLinkNumber());. Place this script in each prim of your linkset. When you click a prim, it will tell you its link number, allowing you to verify the numbers in yourg_sit_targets_primslist.
Code Breakdown
- Configuration: At the top, we define all our customizable settings in lists. This makes it easy to adapt the script for a 4-seat bench or a 2-seat swing without changing the core logic.
f_init(): A setup function that initializes the sit targets and our tracking list,g_seat_status. This list is crucial; it holds the unique key of the avatar in each seat, orNULL_KEYif the seat is empty.touch_start(): When a user touches the sofa, we find the first available seat (the firstNULL_KEYin our list) and usellAvatarOnSitTarget()to send them there.changed(CHANGED_LINK): This is the magic. The system fires this event whenever an avatar sits on or stands up from any part of the linkset. Our script iterates through all seats, checks if an avatar is present usingllGetLinkKey(), and updates ourg_seat_statuslist accordingly. This is far more reliable than trying to track it manually.- Animation Handling: When the
changedevent detects a new sitter, it requestsPERMISSION_TRIGGER_ANIMATION. When the user clicks "Yes" on the dialog, therun_time_permissionsevent fires. Inside this event, we identify who granted the permission and play the correct animation for their seat. When they stand up, we automatically stop the animation.
Advanced Applications & Ideas
Mastering this system unlocks a vast range of creative possibilities:
- Pose Menus: Use
llDialogin thetouch_startevent to offer a menu of poses for a single seat. Store animations in a list and let the user choose. - Couples Furniture: Create a two-seat object where sitting in the first seat enables the second. When the second person sits, play synchronized animations on both avatars.
- Vehicles: Use sit targets to correctly position a driver and multiple passengers in a car or spaceship. - Interactive Rides: A roller coaster car is just a moving platform with sit targets. As the car moves, the avatars stay perfectly seated.
- Dynamic Scenes: Imagine a campfire with five logs around it. Each log is a sit target, each with a different "warming hands" or "chatting" animation, creating a lively social hub automatically.
Practice Exercise
Your assignment is to solidify your new skills.
- Build a four-person picnic table.
- The table itself should be the root prim. The four seats should be child prims.
- Modify the sofa script to work for four seats.
- Give each seat a unique animation (you can use the same one four times if you don't have many).
- Add a feature: When an avatar touches a seat that is already occupied, have the script use
llInstantMessageto send a message to the toucher saying, "Sorry, this seat is taken by [Name of Sitter]." You will need to usellKey2Name()to get the sitter's name from their key.
Frequently Asked Questions (FAQ)
- 1. Why is my avatar floating above or sinking into the furniture?
- Your sit target's
offsetvector is incorrect. The Z value (the third number) is the most common culprit. A positive value moves the avatar up; a negative value moves them down. Tweak this value in small increments (e.g., 0.05) until the position is perfect. - 2. What's the difference between
llSitTargetandllAvatarOnSitTarget? llSitTargetandllSetLinkPrimitiveParamsFastare used to define where the sit position is. They set up the target.llAvatarOnSitTargetis an action; it sends a specific avatar to a sit target that has already been defined. You use the former in setup and the latter in response to a user action like a touch.- 3. My animation isn't playing when I sit. What's wrong?
- There are three common reasons:
- Permissions: The user must click "Yes" on the permission request dialog. Your script must correctly handle the
run_time_permissionsevent. - Inventory: The animation file must be inside the object's inventory, and its name must exactly match the name in your script (it's case-sensitive).
- Built-in AO: The user's own Animation Overrider (AO) might be overriding your script's animation. A well-written script will stop the default "sit" animation (as ours does) which helps, but some AOs can be aggressive. This is a universal challenge in LSL scripting.
- Permissions: The user must click "Yes" on the permission request dialog. Your script must correctly handle the
- 4. Can I have multiple animations per seat?
- Absolutely! Instead of playing an animation on sit, you can have the
changedevent trigger allDialogmenu for the sitter, offering them a list of animations. When they make a selection, thedialogevent will fire, and you can play the chosen animation. - 5. Why use this instead of just linking invisible "pose balls"?
- This method is vastly superior. It's more efficient, using a single script instead of many. It's cleaner, with no ugly transparent balls to select or bump into. It provides a seamless user experience ("Touch the sofa to sit") instead of a confusing one ("Which of these invisible things do I sit on?"). It is the modern, professional standard for creating interactive furniture in any Open Simulator grid.
Your Creative Journey Starts Now. For Free.
You've just learned a technique that is fundamental to creating a living, breathing virtual world. In other metaverses, the cost of the virtual land and asset uploads to practice this skill would already be setting you back hundreds of dollars. Here, in Alife Virtual, it costs you nothing.
This is your invitation. Stop paying to create. Stop being a renter in someone else's world. Become an owner. Become a pioneer in a metaverse built on the principle of creative freedom.
Join Alife Virtual today, claim your FREE 65,536 sqm private island, and start building your dreams with unlimited uploads and zero fees. The world is waiting for what you'll create.
๐ 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