Jump to content

Welcome to the Second Life Community

Get all your questions answered here. Read up on the latest Second Life news and announcements in our Blogs, discover useful tips in the Knowledge Base, and follow conversations in the Forums.

Need more help? Visit our Support page


Latest Blog

HINT: It’s winter-y, fun, games and a series of quests. Can you find the gift hidden in the magical chest?  http://bit.ly/2SIOpp1 Grab the gift before it's gone! OR HINT UPDATE: Winter Swaginator Hunt Week Four Clue: Winter is here, isn't it grand? Find the gift near the door to our snowy land! If you missed out last week and/or didn't get your HUD to get started - you can drop by Winter Wonderland to get one now - just in time to get this week's gift. How to get started - if you have not already: Grab your HUD from Winter Wonderland. Equip your HUD from your inventory, by finding it, right-clicking it and selecting “ADD” Find and collect your gift! Stay tuned to our blog, MoTD, and Social Media channels for new clues each week. Happy Hunting!

View All Blog Posts

Recent Forum Topic


Tutorial: An introduction to rezzing objects by script
People sometimes ask about how to use llRezAtRoot and the related llRezObject, and particularly about how to position the rezzed object in relation to the rezzer, so that no matter how the rezzer is placed, the rezzed object always ends up in the same position, and looking in the same direction, relative to the rezzer, rather than, for example, always rezzing n metres north of the rezzer and looking south (which will probably place the object where you want it to be when the rezzer is rotated at <0,0,0> but not otherwise).    Here I will demonstrate a simple tool to read the rezzed object's position and rotation in terms of the rezzer's frame of reference (which is what llRezAtRoot usually needs) and also show how to pass information from the rezzer to the rezzed object at run time. I will also demonstrate a simple workaround for the sometimes annoying restriction that llRezAtRoot and llRezObject can rez articles no more than 10 metres away from the rezzer. I hope this tutorial is helpful to anyone who wants to learn something about rezzing objects with LSL.    However, builders who have little or no scripting knowledge should find they can use  the tools and scripts here to make their own functioning rezzers without needing to do more than a bit of cutting and pasting, and basic editing.  First, reading the rezzed object's position and rotation Place the rezzer as you want it.   Then put this script in it: //CALCULATE POSITION AND ROTATION FOR REZZED OBJECT, RELATIVE TO THE REZZER integer iChannel = -39251389;//an arbitrary channel key kOwner; default { state_entry() { llListen(iChannel,"","",""); kOwner = llGetOwner(); } listen(integer channel, string name, key id, string message) { if(llGetOwnerKey(id) == kOwner){//if the object sending the message belongs to the owner of the object containing this script (the object) list temp = llGetObjectDetails(id, [OBJECT_POS,OBJECT_ROT]);//read its position and rotation on the region vector vMyPos = llGetPos();//read the position on the region of the object containing this script (the rezzer) vector vObjectPos = llList2Vector(temp,0);//pull out the object's region position rotation rMyRot = llGetRot();//read the rezzer's rotation on the region rotation rObjectRot = llList2Rot(temp,1);//pull out the object's rotation. vector vOffset = (vObjectPos/rMyRot) - (vMyPos/rMyRot); //calculate the object's offset from the rezzer, correcting for the rezzer's local rotation //this formula gives what the result would be if the rezzer is at ZERO_ROTATION and the object is in the same position relative to the rezzer //that is, so many metres in front of the rezzer/behind it, to its left/to its right rObjectRot = rObjectRot/rMyRot;// //does a similar correction for the object's rotation, so it's "facing the rezzer/with its back to the rezzer/looking to the rezzer's left/looking to tje rezzer's right right " float f = llVecDist(vMyPos,vObjectPos); if(f>10.0){ llOwnerSay("Warning: the object "+name+" is "+(string)f+" metres from the rezzer. llRezAtRoot fails if the rez position is > 10,0 metres. Consider rezzing the "+name+" at the rezzer position, sending it the final destination in chat and using llSetRegionPos to move it there"); } llOwnerSay("[\""+name+"\","+(string)vOffset+","+(string)rObjectRot+"];"); } } } Then position the object you want to rez, the "rezzed object" as I'm calling it, where you want it to appear when it's rezzed by the rezzer -- so n metres in front of the object,  and x metres to its left, -- and rotate the rezzed object so it's pointing the way you want it, again in relation to the rezzer (so, looking at the rezzer, or looking away from it, or whatever). Please be aware that scripts can rez objects no more than 10 metres away from the rezzer.  If you try rez something further away than that, the rezzer will silently fail and nothing will happen. There is a workaround -- simply rez the object at the same location as the rezzer (or perhaps 2 metres underneath it, so it’s hidden) and then, after it’s rezzed, have the rezzer use llRegionSayTo to tell it where to go.   The script above, CALCULATE POSITION AND ROTATION, will therefore calculate positions more than 10 metres away but will also warn you that it is too far to rez in one operation. If you want the rezzed item to move to a position more than 10 metres from the rezzer, please pay particular attention to the final section of this post (which also demonstrates an alternative method for passing a channel to the rezzed object). When you have positioned everything to your satisfaction, drop this very simple script into the rezzed object. //TELL REZZER TO READ OBJECT POSITION AND ROTATION //PUT ME IN THE OBJECT YOU ARE GOING TO REZ integer iChannel = -39251389; default { state_entry() { llRegionSay(iChannel,"boo!");//tell the rezzer object that the object to rez is in position llRemoveInventory(llGetScriptName());//that's all the script needs to do, so it will remove itself } } The rezzer will chat out a line of text, such as ["test rezzed object",<1.50000, 0.00000, 1.00000>,<0.00000, 0.00000, -1.00000, 0.00000>]; Make a note of it (including the square brackets and the final semi-colon)..   You will see that the short script you just put in the “rezzed object” has vanished (or it will soon, if it still seems to be there).  It’s no longer needed. At this point you can also, if you wish, remove the CALCULATE POSITION AND ROTATION FOR REZZED OBJECT script from the rezzer. Second, installing the rezzer script… Scripts, obviously, need events to trigger them to do something.   In these examples, the llRezAtRoot function is called in both the touch_end and collision_start events.    That’s really to demonstrate this -- you could also call llRezAtRoot in a sensor or listen event.   It does not, of course, mean that someone needs  both to touch the object AND collide with it to make it work -- either will trigger it.   Place this script, REZ OBJECT BY SCRIPT, in the rezzer //REZ OBJECT BY SCRIPT. PUT ME IN THE REZZER float fLifeSpan = 30.0;//how long the object lives for integer iStartParam = 99;//arbitrary integer to feed rezzed object as its start parameter key kObject; list lParams; rotation rRot; rotation rObjectRot; string strObject; vector vPos ; vector vOffset; default { state_entry() {//some sanity checks if(lParams ==[]){ llOwnerSay("Please fill in some values for lParams at the start of the script."); } else{ strObject = llList2String(lParams,0);//read the object's name from the list lParams if(llGetInventoryType(strObject)!=INVENTORY_OBJECT){ //if it's not an item in my inventory, warn my owner llOwnerSay("There is nothing in my inventory for me to rez."); } else{ vOffset = llList2Vector(lParams, 1);//read the object position if(llVecMag(vOffset)>10.0){//if the offset is greater than 10 metres llOwnerSay("The position at which "+strObject+" is to rez is more than 10 metres away from the rezzer position. I can rez objects only within a 10 metre radius. You will need to rez the "+strObject+" closer to me and then to use llSetRegionPos to move it where you want it to go."); } } } } changed(integer change){ if(change & CHANGED_INVENTORY){ llResetScript();//if the inventory contents change, check for what the object is the script to rez. } } touch_end(integer num_detected) { if(!llGetObjectPrimCount(kObject)){//if there is no object already rezzed if(llGetInventoryType(strObject)==INVENTORY_OBJECT){//don't try to rez something that's not there vPos = llGetPos();//read the position of the object containing this script rRot = llGetRot();//read the location of the object containing this script vOffset = llList2Vector(lParams, 1);//read from lParams the offset from the rezzer at which the rezzed object should rez rObjectRot = llList2Rot(lParams,2);//read from lParams the rotation at which it is to rez llRezAtRoot(strObject, vPos + vOffset * rRot, ZERO_VECTOR, rObjectRot * rRot, iStartParam); } } } collision_start(integer num_detected) { if(!llGetObjectPrimCount(kObject) && (llDetectedType(0)& AGENT_BY_LEGACY_NAME)){//if there is no object already rezzed, and if it was an avatar who collided with the rezzer if(llGetInventoryType(strObject)==INVENTORY_OBJECT){//don't try to rez something that's not there llSleep(0.5);//wait a moment to give the avatar time to step onto the prim before proceeding vPos = llGetPos();//read the position of the object containing this script rRot = llGetRot();//read the location of the object containing this script vOffset = llList2Vector(lParams, 1);//read from lParams the offset from the rezzer at which the rezzed object should rez rObjectRot = llList2Rot(lParams,2);//read from lParams the rotation at which it is to rez llRezAtRoot(strObject, vPos + vOffset * rRot, ZERO_VECTOR, rObjectRot * rRot, iStartParam); } } } object_rez(key id) { llSleep(0.2);//brief delay to allow the rezzed object to wake up and for the simulator to register its UUID kObject = id; //record the object's id llRegionSayTo(kObject, iStartParam, (string)fLifeSpan);//tell the object how long to wait before deleting itself } } Find the entry list lParams; at the top of the script (line 3, or near there) and copy paste the line of text you saved earlier when the rezzer chatted it out) so that the line now reads, for example, list lParams = ["test rezzed object",<1.50000, 0.00000, 1.00000>,<0.00000, 0.00000, -1.00000, 0.00000>]; What is this list for?    It holds some values that are needed by llRezAtRoot, the function that does the magic.   llRezAtRoot takes five arguments (that is, it needs five pieces of information to function), presented in a list: llRezAtRoot([ (string) Name of Object to rez, (vector) Initial Position, (vector) Initial Velocity, (rotation) Initial rotation, (integer) Start Parameter ]); The first argument, the name of the object to rez, should be self-explanatory. The second argument, Initial Position, is a vector containing the position on the region where the object is to appear.   Since you will normally want to be able to use the rezzer anywhere on a region, you need to express as rezzer position + offset.    Furthermore, you will generally want to use the rezzer’s frame of reference rather than the region’s frame of reference -- that is, no matter which way the rezzer happens to be pointing, the object should rez 1.5 metres in front of the object (object’s frame of reference) regardless of whether that happens to be 1.5 metres north of the object or south, or north by north west of the object (region frame of reference). To tell the script that’s what you want to happen, you have to tell it to translate the offset by the rezzer’s rotation, which is what vPos = llGetPos();//read the position of the object containing this script rRot = llGetRot();//read the location of the object containing this script vOffset = llList2Vector(lParams, 1);//read from lParams the offset from the rezzer at which the rezzed object should rez rObjectRot = llList2Rot(lParams,2);//read from lParams the rotation at which it is to rez llRezAtRoot(strObject, vPos + vOffset * rRot, ZERO_VECTOR, rObjectRot * rRot, iStartParam); in the touch_end and collision_start events is all about.   vPos is the object’s position.  vOffset is the desired offset from the rezzer, assuming the rezzer is rotated at <0,0.0,> (facing due East in SL terms).   *rRot means “translate this position through my own frame of reference” or “so many metres in front of me, and so many metres to my left”.   The asterisk may look as it it means “multiply by” but it doesn’t in this context.   It means translate.  The significant difference in this context is that multiplication is commutative (6 x 3 == 3 x 6) but translation isn’t ( vVector * rRotation means something very different from rRotation * vVector). The third argument, Initial Velocity, isn’t relevant here.  It’s the speed and directon at which physical objects should be moving when they are rezzed (e.g. bullets rezzed by a gun).  Since it’s not relevant here, the value is ZERO_VECTOR. The fourth argument, Initial Rotation, means the initial rotation of the rezzed object, and translating it by the rezzer’s rotation tells it use the rezzer’s frame of reference (“looking towards me” rather than “looking to my north”).. The fifth and final argument, Start Parameter, is an arbitrary integer that you send to the rezzed object to use as its start parameter.   Typically, as in this example, it’s used to tell the rezzed object to open a listener on a particular channel, so the rezzer can  send the rezzed object further instructions after rezzing it. Note that when either the touch_end or collision_start events fire, they check that if(!llGetObjectPrimCount(kObject)){//if there is no object already rezzed In English that means, “if the prim count (land impact) of the object with the UUID kObject is zero,”  which is a roundabout way of saying “if there is no object on the region with the UUID kObject. If there is no object already rezzed, then the script goes ahead and rezzes the object at the required offset and rotation.    It then, in the object_rez event, takes a moment to let things settle down after the object rezzes, then records its UUID (kObject = id;) and then uses llRegionSayTo to pass it the value of fLifeSpan (the number of seconds to wait before deleting itself).    It could just as well, or even also, send it a position to move to using llSetRegionPos, as suggest above. Third and finally, the script in the rezzed object Place the following script in the object you want to rez: //SCRIPT FOR REZZED OBJECT -- PUT ME IN THE OBJECT YOU WANT TO REZ float fHowLong; integer iChannel; default { on_rez(integer start_param) { if(start_param){//if the start parameter is not 0, that means the object containing this script came out of a rezzer, not someone's inventory iChannel = start_param; //use the start parameter as a channel llListen(iChannel,"","","");//start listening on that channel } } listen(integer channel, string name, key id, string message) { fHowLong = (float)message; llOwnerSay("setting timer for "+(string)fHowLong+" seconds.");//remove this after you've finished testing, obviously llSetTimerEvent(fHowLong); } timer() { llDie();//delete this object } } As noted above, this reads the value of start_param, the start parameter passed in the llRezAtRoot call, and opens a listener using that value as the channel.  The rezzer then sends it, using llRegionSayTo, the value of fLifeSpan, which it casts as a float and uses it to set a timer.   When the timer fires, the object vanishes. If you want the object to remain rezzed indefinitely, then simply supply 0.0 as the value for fLifeSpan (llSetTimerEvent(0.0) means, possibly unintuitively, turn off the timer rather than make it fire immediately). Workaround for llRezAtRoot’s 10 metre limit As noted above, llRezAtRoot and llRezObject fail if they try to rez the object more than 10 metres away.   However, there is an easy workaround.   You simply need to make some slight alterations both the rezzer script and the script for the rezzed object. Use this script in the rezzer.  It rezzes the object underneath it (so people do not see an object appear at the rezzer and then vanish) and then tells the object where to go on the region. These two examples also demonstrate a way to have thescripts calculate a channel based on the rezzer’s UUID and an alternative way to pass the value of fLifeSpan.    If you don’t want the object to remove itself after a certain time, simply set fLifeSpan to 0.0 (though if you do this, remember that the script checks before rezzing anything and won’t rez a new object while the old one is still on the region). //REZ OBJECT BY SCRIPT AND GIVE IT A DESTINATION ON THE REGION. PUT ME IN THE REZZER float fLifeSpan = 30.0;//how long the object lives for integer iChannel; key kObject; list lParams; rotation rRot; rotation rObjectRot; string strObject; vector vPos ; vector vOffset; integer Key2Number(key objKey) { return ((integer)("0x"+llGetSubString((string)objKey,-8,-1)) & 0x3FFFFFFF) ^ 0x3FFFFFFF; } default { state_entry() { iChannel = Key2Number(llGetKey());//calculate an integer based on my UUID if(lParams ==[]){ llOwnerSay("Please fill in some values for lParams at the start of the script."); } else{ strObject = llList2String(lParams,0);//read the object's name from the list lParams if(llGetInventoryType(strObject)!=INVENTORY_OBJECT){ //if it's not an item in the inventory of the object containing this script, complain llOwnerSay("There is nothing in my inventory for me to rez."); } } } changed(integer change){ if(change & CHANGED_INVENTORY){ llResetScript();//if the inventory contents change, check for the object that is to be rezzed } } touch_end(integer num_detected) { if(!llGetObjectPrimCount(kObject)){//if there is no object already rezzed if(llGetInventoryType(strObject)==INVENTORY_OBJECT){//don't try to rez something that's not there vPos = llGetPos();//read the position of the object containing this script rRot = llGetRot();//read the location of the object containing this script vOffset = llList2Vector(lParams, 1);//read from lParams the offset from the rezzer at which the rezzed object should rez rObjectRot = llList2Rot(lParams,2);//read from lParams the rotation at which it is to rez llRezAtRoot(strObject, vPos +<0.0,0.0,-2.0>, ZERO_VECTOR, rObjectRot * rRot,(integer)fLifeSpan);//rez the object 2 metres beneath the rezzer, so people don't see it rez, and pass the lifespan as the start parameter } } } collision_start(integer num_detected) { if(!llGetObjectPrimCount(kObject) && (llDetectedType(0)& AGENT_BY_LEGACY_NAME)){//if there is no object already rezzed, and if it was an avatar who collided with the rezzer if(llGetInventoryType(strObject)==INVENTORY_OBJECT){//don't try to rez something that's not there llSleep(0.5);//wait a moment to give the avatar time to step onto the prim before proceeding vPos = llGetPos();//read the position of the object containing this script rRot = llGetRot();//read the location of the object containing this script vOffset = llList2Vector(lParams, 1);//read from lParams the offset from the rezzer at which the rezzed object should rez rObjectRot = llList2Rot(lParams,2);//read from lParams the rotation at which it is to rez llRezAtRoot(strObject, vPos +<0.0,0.0,-2.0>, ZERO_VECTOR, rObjectRot * rRot,(integer)fLifeSpan);//rez the object 2 metres beneath the rezzer, so people don't see it rez, and pass the lifespan as the start parameter } } } object_rez(key id) { llSleep(0.2);//brief delay to allow the rezzed object to wake up and for the simulator to register its UUID kObject = id; //record the object's id llRegionSayTo(kObject, iChannel, (string)(vPos + vOffset * rRot));//tell the object its target position } } Use this script in the rezzed object.   It listens for a message from the rezzer and, when it receives it, it moves to that position.   It initially sets itself to TEMP_ON_REZ so that, if anything goes wrong and it doesn’t receive any message giving it a destination, it will vanish in a minute or so.   If it receives an invalid destination, or if it cannot reach its destination, it deletes itself. //MOVE ANYWHERE ON REGION -- PUT ME IN THE REZZED OBJECT float fHowLong; integer iHandle; integer Key2Number(key objKey) { return ((integer)("0x"+llGetSubString((string)objKey,-8,-1)) & 0x3FFFFFFF) ^ 0x3FFFFFFF; } vector vTargetPos; default { on_rez(integer start_param) { key kRezzer = llList2Key(llGetObjectDetails(llGetKey(), [OBJECT_REZZER_KEY]),0);//read the uuid of the person or object that rezzed the object containing this script if(llGetAgentSize(kRezzer)==ZERO_VECTOR){//if the agent who rezzed the object containing this script doesn't have a size, it must have been rezzed by an object iHandle = llListen(Key2Number(kRezzer),"",kRezzer,"");//calculate the channel based on the rezzer's uuid and start listening llSetLinkPrimitiveParamsFast(LINK_THIS,[PRIM_TEMP_ON_REZ,TRUE]);//set to temp on rez, in case something goes wrong and the object containing this script can't reach its destination fHowLong = (float)start_param;//read value of fHowLong from the start_param. A value of 0 means the object remains indefinitely, assuming it makes it to its ultimate destination } } listen(integer channel, string name, key id, string message) { llListenRemove(iHandle);//stop listening vTargetPos = (vector)message; if(vTargetPos !=ZERO_VECTOR){//there is probably an edge case where someone might actually want to place the object underground at the corner of the region, but usually it means the message isn't a valid vector llSetRegionPos(vTargetPos); llSleep(0.1);//probably not necessary but to be on the safe side, before the next test if(llVecDist(llGetPos(), vTargetPos)>0.25){//if the arrival point and the target point are close enough for government work.... // in theory you could say "if(llSetRegionPos(vTarget)" but that will fail if, for some reason, the object is even fractionally target so it's safer to allow some leeway) llOwnerSay("I have failed to reach my target. Oh, the shame and disappointment! Farewell, cruel world!"); llDie(); } else{ llSetLinkPrimitiveParamsFast(LINK_THIS,[PRIM_TEMP_ON_REZ,FALSE]);//arrived safely, so turn off temp on rez llOwnerSay("setting timer for "+(string)fHowLong+" seconds.");//remove this after you've finished testing, obviously llSetTimerEvent(fHowLong); } } else if (ZERO_VECTOR == vTargetPos){ llOwnerSay("ZERO_VECTOR is not a valid destination for me. Dying because I don't know what else to do."); llDie(); } } timer() { llDie();//delete the object containing this script } }  
Read More...
View All Forums

Second Life Pic of the Day

Today's Second Life pic of the day is "When baking gets messy, turn to wine" by …
Read the Blog
×