Jump to content

Basic Scripting Help


You are about to reply to a thread that has been inactive for 119 days.

Please take a moment to consider if this thread is worth bumping.

Recommended Posts

Posted (edited)

Hi, 

I am not looking to do anything complicated. 

I am wanting to figure out how to tweak an existing script to rotate two difffernt objects in different directions

 

I am wanting to link two objects (a1 and a2) and i want them to have two different scripts 

one with 

default
{
    state_entry()
    {
       llTargetOmega(<0,0,1>,2*PI/8,1.0);
    }

 

and one with 

default
{
    state_entry()
    {
       llTargetOmega(<0,1,0>,2*PI/8,1.0);
    }
}

I have a basic understanding that i need some sort of coding to identify the objects which would be adjusting something to TargetOmega i imagine since when i link the two objects it defaults to the script of the parent object, but i really have no idea what to do or how to do it. 

If you have some resources you could point me towards to help me figure it out, that'd be lovely. 

Edited by Luxianna Swords
Link to comment
Share on other sites

Posted (edited)

You /can/ have different scripts in different objects(prims or meshes) in a linkset (EDIT: or even multiple scripts in the same object) and they will /all/ run at the same time. That being said it's best-practice to consolidate scripts into a single script if you can.

for the sake of keeping things simple and for learning, try linking a few cubes together and put one or the other of your scripts into different cubes and see what happens.

Omega is a bit weird, to get things to stop rotating, put this script into any of your cubes and set it to running:

default
{
    state_entry()
    {
        integer index = llGetNumberOfPrims()+1;
        while(--index>0)
        {
          llSetLinkPrimitiveParamsFast(index,
            [PRIM_OMEGA, <0,0,0>,0.0,0.0]);
        }
    }
}

Some things like 'Omega', Settext and particle effects are just special properties of the prim (like its size and shape, and textures) that for some reason you can't directly edit with the build window. if you remove the script that set the property, (in this case the spinning) it will continue to be there.

You will also notice that if you have one of your omega scripts in the 'root prim' of your linkset, something slightly different will happen.

Edited by Quistessa
fixed infinite loop, woops.
  • Like 1
Link to comment
Share on other sites

In SL all linked prims are linked to the root. So if you rotate the root all linked prims will rotate around the root.

If you want to have 2 independant rotating linked prims you need 3 prims!

One is the root and you can make it small+invisible. You link your other 2 prims to it and then the scripts will rotate each of them independant.

As Quistessa showed you, you need only one script and not one per prim.

  • Like 1
Link to comment
Share on other sites

3 hours ago, Nova Convair said:

As Quistessa showed you, you need only one script and not one per prim.

I didn't actually show how to (efficiently) use only one script. Gathering link numbers is a bit of a pain, and IMO not really a 1st time scripting task. For setting prim properties like omega, that are just going to stay static, it's a bit less of a chore to just place a script in the prim you want to have the property, (then take it out again, or set it inactive.)

For the record though, two "correct" ways of going about the problem:

Method 1: hard coded link numbers:

First, after you've linked your thing together how you like it, use a touch-say script to gather the link numbers of the relevant pieces (the link numbers quoted in the build menu can be wrong, especially if you at any point linked together two linksets instead of linking everything in one step)

default()
{	touch_start(integer i)
	{	llOwnerSay("Link: "+llDetectedLinkNumber(0)+" Face: "+llDetectedTouchFace(0));
	}
}

then knowing the link numbers, plug them into some global variables (so you can use them for any other manipulations)

integer link1=2; // or whatever you got from step 1.
integer link2=3;
default
{
  state_entry()
  {
    llSetLinkPrimitiveParamsFast(link1,
       [PRIM_OMEGA,<0,0,1>,TWO_PI/8,1.0]);
    llSetLinkPrimitiveParamsFast(link2,
       [PRIM_OMEGA,<0,1,0>,TWO_PI/8,1.0]);
  }
}

Method 2: Object Description:

Use a specific identifier in the description of the prims you want to manipulate, for this example, set the description of the relevant prims/meshes to Thing1 and Thing2

integer link1;
integer link2; // will fill these in state_entry()

default
{
  state_entry()
  {
    integer index=llGetNumberOfPrims+1;
    while(--index)
    {
      string desc = llList2String(llGetLinkPrimitiveParams(index, [ PRIM_DESC ]), 0);
      if(desc=="Thing1")
      {	link1=index;
      }else if(desc=="Thing2")
      { link2=index;
      }
    }
    llSetLinkPrimitiveParamsFast(link1,
       [PRIM_OMEGA,<0,0,1>,TWO_PI/8,1.0]);
    llSetLinkPrimitiveParamsFast(link2,
       [PRIM_OMEGA,<0,1,0>,TWO_PI/8,1.0]);
  }
}

 

Hope that helps!

  • Like 2
Link to comment
Share on other sites

It depends what this construct is about to do.

If it's just rotating then put a script in each prim and once it's rotating the scripts can be removed. (the scripts only start the rotation, removing the script will NOT stop the rotation)

If it's planned to toggle the rotation on/off then a better script is needed. Toggle by click? Then you need to click both or send a message from script to script or use one script instead of two. The complexity of a universal solution exceeds the complexity of just making things rotate alot!

For an experienced scripter a simple solution can be painful 😁 but for a beginner ... just make the stuff rotate!
Anything else is only interesting if there is a general interest to learn how to script.

Link to comment
Share on other sites

Thanks, this is all really insightful and gives me some thoughts on what to tinker with. 

 

The end name is, i have three objects in total. i want one to rotate clockwise, one to to rotate counter clockwise, and one to rotate top to bottom - but i also need all three objects linked. I am trying to make myself this neat little hand orb thing. I can drop the scripts into the three objects and they work fine independently but when i link them they all default to the what i am assuming is referred to as the primary/root or parent objects script. I've also tried linking everything then editing linked objects and putting the scripts in each object but that didn't work either. 

 

I'll try the integer trick from Quintessa. Really appreciate everyone's advice and input. This community is so awesome.

Link to comment
Share on other sites

Posted (edited)
5 hours ago, Luxianna Swords said:

I can drop the scripts into the three objects and they work fine independently but when i link them they all default to the what i am assuming is referred to as the primary/root or parent objects script.

If you just want to start them going and leave them spinning on the Prim_property, then delete all scripts, stop any rotations that might be going on, link them, then alter each of your working scripts to replace

llTargetOmega(whatever); 

with Quistessa's suggestion , but use LINK_THIS instead of a had-coded link number

 

llSetLinkPrimitiveParamsFast(LINK_THIS,
       [PRIM_OMEGA,[whatever]);

Drop each script into the correct child prim's content tab (don't forget to check edit linked part first :)

Then delete each script once you've got it spinning.

Edited by Profaitchikenz Haiku
Link to comment
Share on other sites

8 hours ago, Luxianna Swords said:

Thanks, this is all really insightful and gives me some thoughts on what to tinker with. 

 

The end name is, i have three objects in total. i want one to rotate clockwise, one to to rotate counter clockwise, and one to rotate top to bottom - but i also need all three objects linked. I am trying to make myself this neat little hand orb thing. I can drop the scripts into the three objects and they work fine independently but when i link them they all default to the what i am assuming is referred to as the primary/root or parent objects script. I've also tried linking everything then editing linked objects and putting the scripts in each object but that didn't work either. 

 

I'll try the integer trick from Quintessa. Really appreciate everyone's advice and input. This community is so awesome.

To keep track of which link is which, I always do something like this (there are others to do it, but this is what I do, because I'm used to it and it's automatic by now).

integer iClockwisePrim;
integer iCounterClockwisePrim;
integer iTopToBottomPrim;

string strClockwise = "Clockwise";
string strCounterClockwise = "CounterClockwise";
string strTopToBottom = "TopToBottom";

findPrims(){
	integer iMax = llGetNumberOfPrims()+1;
	integer	iCounter=1;
	do {
		string str = llList2String(llGetLinkPrimitiveParams(iCounter,[PRIM_DESC]),0);
		if(strClockwise == str){
			iClockwisePrim = iCounter;
		}
		else if (strCounterClockwise == str){
			iCounterClockwisePrim = iCounter;
		}
		else if (strTopToBottom == str){
			iTopToBottomPrim = iCounter;
		}
	}
	while(++iCounter < iMax);

}

Call that in state_entry and when the linking changes, and you don't need to worry about link numbers, since iClockwisePrim will always be the one you gave that link description to.   

Then you can call llSetLinkPrimitiveParamsFast,  and set all the different speeds and directions at once, in llSetLinkPrimitiveParamsFast, doing something like this

		llSetLinkPrimitiveParamsFast(LINK_SET,[
			PRIM_LINK_TARGET,iClockwisePrim,PRIM_OMEGA,vAxis1, fSpinrate1, fGain1,
			PRIM_LINK_TARGET,iCounterClockwisePrim,PRIM_OMEGA,vAxis2, fSpinrate2, fGain2,
			PRIM_LINK_TARGET,iTopToBottomPrim,PRIM_OMEGA,vAxis3, fSpinrate3, fGain3			
			]);
		

with appropriate values for each link's rotation, speed and gain.     Doing it that way makes it ever so easy to adjust.

 

  • Like 2
Link to comment
Share on other sites

14 minutes ago, Quartz Mole said:

integer iMax = llGetNumberOfPrims()+1; integer iCounter=1; do {

Minor nitpick, but ever since I learned backwards iteration from the wiki I never looked back. my automatic loop is something like:

integer index = 10; // usually llGetListLength(list);
while(--index>=0)
{	//do things
}

although the barebones most efficient version is probably something like

integer index=10;
do{
	//do things
}while(~--index);

 

  • Thanks 1
Link to comment
Share on other sites

Posted (edited)
8 hours ago, Quistessa said:

I'm pretty sure they're both equivalent, no?

Yes, but I wasn't suggesting a new script. I was attempting to explain that the OP can continue to use their three existing scripts with a small alteration.

The new statement would work both on an individual prim when LINK_THIS will return 0, and on a child in a link set. If all they want to do is set the three spinning and never alter them, they could therefore link three static prims, drop the scripts into each one by one, then remove the scripts.

Sorry if I've confused you.

Edited by Profaitchikenz Haiku
Link to comment
Share on other sites

5 hours ago, Quistessa said:

 barebones most efficient version

just adding to the conversation

in production code then with lists use:

integer n = llGetListLength(alist);
while (~--n)
{

}

the reason is that sometimes the list can be empty and when so then we want the While condition to fail when --n < 0. (~-1 = FALSE)

with linksets then use

integer n = llGetNumberOfPrims();
do
{

} while (--n);

linksets (including linkset of one prim) are in the range 1.. to numberofprims. And we want the while condition to fail when --n = 0

when we know that n can never be 0 (is always >= 1) then use Do..While. Do..While uses 1 less bytecode branch instruction than does While

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

1 hour ago, Mollymews said:

just adding to the conversation

in production code then with lists use:


integer n = llGetListLength(alist);
while (~--n)
{

}

the reason is that sometimes the list can be empty and when so then we want the While condition to fail when --n < 0. (~-1 = FALSE)

with linksets then use


integer n = llGetNumberOfPrims();
do
{

} while (--n);

linksets (including linkset of one prim) are in the range 1.. to numberofprims. And we want the while condition to fail when --n = 0

when we know that n can never be 0 (is always >= 1) then use Do..While. Do..While uses 1 less bytecode branch instruction than does While

Is there a significant advantage to de-incrementing iNumberOfPrims (start at the end of  the list, and work back) rather than incrementing -iNumberOfPrims (start at beginning of the list and work forwards), especially when the loop runs only rarely?   

Link to comment
Share on other sites

I think so, both on fewer lines of code, fewer potential spots for bugs, and faster execution.

To increment, you would, after having got the number of prims,

1) create a second variable (index) initialised to 1

2) in the while loop test, increment index and then test for it still being less than or equal to the maximum.

So that's two extra statements and a more complicated test

Whilst the optimizer might condense that into more compact bytecode it would still have to allocate two variables where Molly's solution only needs one.

Forgetting the optimiser, (which would be just as efficient at oprimizing programmer errors as it would good code) every extra statement you add to a piece of code increases the chances of errors due to programmer misunderstanding, typing errors, forgetfullness, whatever.

Link to comment
Share on other sites

Posted (edited)
1 hour ago, Profaitchikenz Haiku said:

I think so, both on fewer lines of code, fewer potential spots for bugs, and faster execution.

To increment, you would, after having got the number of prims,

1) create a second variable (index) initialised to 1

2) in the while loop test, increment index and then test for it still being less than or equal to the maximum.

So that's two extra statements and a more complicated test

Whilst the optimizer might condense that into more compact bytecode it would still have to allocate two variables where Molly's solution only needs one.

Forgetting the optimiser, (which would be just as efficient at oprimizing programmer errors as it would good code) every extra statement you add to a piece of code increases the chances of errors due to programmer misunderstanding, typing errors, forgetfullness, whatever.

I've just done a quick test with lists of different lengths, first counting up from the first item to the last, and then backwards from the last item to the first, and comparing the time it took to process the list each time.

list lTest;
integer iMax;
integer iHowMany;

default {
	touch_end(integer num_detected) {
		//populate the list
		do{
			lTest +=[(string)iMax];
		}
		while(++iMax< iHowMany);
                                
		integer  n = llGetListLength(lTest);
		
        //loop through lTest from start to finish by incrementing -llGetListLength(lTest);
		
        iMax =-n + 1;           
		llResetTime();
		do{
			llSay(-99,llList2String(lTest,iMax));//so I don't have to watch my screen fill up while it counts
		}
		while(++iMax);
		llOwnerSay("With a value of "+(string)iHowMany+" for iHowMany,incrementing -iMax took "+(string)llGetTime()+" seconds to complete");

		//loop through lTest from last item to first by de-incrementing llGetListLength(lTest);
		
        iMax = n ;
		llResetTime();
		do{
			llSay(-99,llList2String(lTest,iMax));
		}
		while(~(--iMax));
		llOwnerSay("With a value of "+(string)iHowMany+" for iHowMany, de-Incrementing iMax took "+(string)llGetTime()+" seconds to complete");

	}
}

Results just now on Magnum RC Sandbox 1 (where I happened to be) were:

Quote

With a value of 25 for iHowMany,incrementing -iMax took 0.022256 seconds to complete
With a value of 25 for iHowMany, de-Incrementing iMax took 0.022694 seconds to complete

With a value of 100 for iHowMany,incrementing -iMax took 0.043794 seconds to complete
With a value of 100 for iHowMany, de-Incrementing iMax took 0.044556 seconds to complete

With a value of 250 for iHowMany,incrementing -iMax took 0.132735 seconds to complete
With a value of 250 for iHowMany, de-Incrementing iMax took 0.133607 seconds to complete

With a value of 1000 for iHowMany,incrementing -iMax took 0.444629 seconds to complete
With a value of 1000 for iHowMany, de-Incrementing iMax took 0.422689 seconds to complete

YMMV, but I don't see there's much in it, at least not until the lists start to get quite long for SL (I'm normally counting stuff like items in a linkset or an object's inventory, or avatars on a region, so my lists don't normally contain more than a few hundred entries, at most, and normally far fewer).    

Edited by Quartz Mole
  • Thanks 2
Link to comment
Share on other sites

1 hour ago, Quartz Mole said:

Is there a significant advantage to de-incrementing iNumberOfPrims (start at the end of  the list, and work back) rather than incrementing -iNumberOfPrims (start at beginning of the list and work forwards), especially when the loop runs only rarely?   

Especially when a procedure happens rarely, any minor optimizations are pretty much pointless and up to preference.

That said, I would still (and do) use Mollymews' first example because it's the most concise and more appealing than the alternatives.

  • Like 3
Link to comment
Share on other sites

I too pretty much always decrement through loops; one exception that comes to mind is if there's reason to expect a loop termination condition to be more likely near the front of the list than the rear.

One advantage of decrementing is to be able to insert or delete at the current index of the list (or, I suppose llCreate- or -BreakLink on a linkset) and just continue the loop, not needing to adjust the index nor a length-of-list termination condition.

  • Like 3
Link to comment
Share on other sites

4 hours ago, Quartz Mole said:

test

a little thing with this

iMax = -n + 1;           
do        
{
    ... llList2String(lTest, iMax));
} while (++iMax);

it doesn't produce the 0th element, it should be: iMax = -n;

and to a do complete comparison (meaning that it produces the list in reverse order) then

iMax = -n;           
do        
{
   ... llList2String(lTest, -iMax-1);
}
while (++iMax);

 

this said you are right that where possible it is best to use ++ or -- rather than ~++ or ~--

  • Thanks 1
Link to comment
Share on other sites

2 hours ago, Mollymews said:

a little thing with this


iMax = -n + 1;           
do        
{
    ... llList2String(lTest, iMax));
} while (++iMax);

it doesn't produce the 0th element, it should be: iMax = -n;

and to a do complete comparison (meaning that it produces the list in reverse order) then


iMax = -n;           
do        
{
   ... llList2String(lTest, -iMax-1);
}
while (++iMax);

 

this said you are right that where possible it is best to use ++ or -- rather than ~++ or ~--

Just to clarify, for other readers, if you change the llSay into llOwnerSay in my example and set iHowMany to 100, you'll see you get the 100 elements, but they're 0--99, not 1--100.   

So when you're dealing with link numbers, you have to be really careful , because link numbering begins at 1, of course, and it's so easy to get confused.   That's why I name my links when I'm storing them in lists, because then it's always llList2Integer(lLinks, iIndex) and I can keep track of what I'm doing.

  • Thanks 1
Link to comment
Share on other sites

Posted (edited)
5 hours ago, Quartz Mole said:

Just to clarify, for other readers, if you change the llSay into llOwnerSay in my example and set iHowMany to 100, you'll see you get the 100 elements, but they're 0--99, not 1--100.   

So when you're dealing with link numbers, you have to be really careful , because link numbering begins at 1, of course, and it's so easy to get confused.   That's why I name my links when I'm storing them in lists, because then it's always llList2Integer(lLinks, iIndex) and I can keep track of what I'm doing.

in this use case where we are not accessing list element 0 then the comparative test is

integer iMax = n;
do
{

} while (--iMax);

as (~--iMax) will access the 0th list element which we don't want in this use case

edit add:

i also point out that Quartz is making a general point which is true. integer addition is more efficient than subtraction

 

i just add on here as part of the conversation. The general rule that I go by is that a computer algorithm is more efficient when it uses the least number of clock cycles to complete its task. And in a shared environment, like LSL, then the general rule within a clock cycle is the least number of instructions

LSL Mono is a little bit awkward to work with as we don't know much about the hardware that it runs on, and while we can surmise that the LSL CIL produced pretty much conforms with the Microsoft CIL standards, we don't know this for sure. As pretty much every compiler has its own tweaks to optimise performance for its specific task, and we (residents) don't have an indication from Linden of what they might be if there are any. And we don't have the code debugging/profiling tools that other coding language environments provide to peek into this ourselves

hopefully maybe one day. And if that was only a variable watch window then I would be quite happy with that. More happier tho with breakpoints as well

Edited by Mollymews
  • Like 1
Link to comment
Share on other sites

Do keep in mind that the rotation axis will differ when you link or attach prims to other prims. As it says on the Wiki:

Link Sets

  • If the script is attached to the root prim, the entire object rotates around the region axis.
    • If the object is attached then it rotates around the attachment axis.
  • If the script is attached to a child prim, the prim rotates around the local axis.
    • A child prim can rotate around its own axis while the entire object rotates around another axis.
Link to comment
Share on other sites

You are about to reply to a thread that has been inactive for 119 days.

Please take a moment to consider if this thread is worth bumping.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...