Jump to content
Sign in to follow this  
Rubberene

Multi sound loop with anims help

Recommended Posts

I've done up a script that loops a sequence of sounds in an attachment:

default
{
    state_entry()
    {
         llSetTimerEvent(1);
    }

    timer()
    {
        llPlaySound("One",1);
        llSleep(10);
        llPlaySound("Two",1);
        llSleep(10);
        llPlaySound("Three",1);
        llSleep(10);
        llPlaySound("Four",1);
        llSleep(10);
        
    }
}

 I want to have it also start and stop animations, but am having trouble with the whole permission to trigger animations thing. This is where I'm at so far, but obviously it doesn't work:

default
{
    state_entry()
 {
        llRequestPermissions(llDetectedKey(0), PERMISSION_TRIGGER_ANIMATION);
    }
    run_time_permissions(integer perm)
    {
        if (perm & PERMISSION_TRIGGER_ANIMATION)
        {
            
       
            llSetTimerEvent(1.0);
        }
        }
    timer()
    {
        llPlaySound("One",1);
        llSleep(10);
        llStartAnimation("express_open_mouth");
        llPlaySound("Two",1);
        llSleep(10);
        llPlaySound("Three",1);
        llSleep(10);
        llPlaySound("Four",1);
        llSleep(10);
        
        
        }  }    

 I know my feeble attempt probably looks like a joke to a real scripter, but I would truly appreciate it if someone would point me in the right direction, since I seem to be facing the wrong way at this point. :) 

I need the sequence to loop indefinitely until another script sets this script's state to FALSE. Which so far works, as long as I don't go messing with animations. ;)

Share this post


Link to post
Share on other sites

You need to decide what is supposed to cause all this sound and animation to start, and on whose avatar you'll be playing those animations.

Simplest case, you might trigger it all by a touch_end event, which is handy because then, in the touch_end() handler, the llDetectedKey(0) will refer to something useful: the toucher. That way you'll know of whom to request permissions, and once you've got permissions, llGetPermissionsKey() will have the avatar you want.

(You may later want to handle the case of that avatar leaving the sim before you play an animation on them, and I suspect you may end up with a more elaborate timing loop instead of the llSleep()s, but you'll want to get something working first.)

Share this post


Link to post
Share on other sites

I want it to start when the script's state is set to TRUE, sorry if that wasn't clear. The script with only the sounds in it already works the way I want, I just need it to be able to also trigger anims. No touch events. And I'm not sure what you mean about the avatar leaving the sim; this is for an attachment. 

Share this post


Link to post
Share on other sites

In that case, it's a lot easier because you automatically know who is supposed to be animated --- llGetOwner() ---  and you don't have to worry about leaving the sim because you carry the scripted object with you.  The easiest thing to do is to request PERMISSION_TRIGGER_ANIMATION when your scripted object is attached.  Or, if the script in this object is not active when you attach the object, test for llGetAttached as soon as it is activated, and then request permission.  Your call to llStartAnimation, then, goes in the run_time_permissions event, where it is executed as soon as permission is granted (automatically, in the case of an attachment).

Frankly, though, I wouldn't use two scripts to do this.  You can do everything in one script.  After all, any action that you have to perform to turn this second script ON could just as easily turn the sounds and animations on in the main script instead. A switch is a switch.

Share this post


Link to post
Share on other sites

Hmm. The script I posted here is just an example, not the actual full-blown script. What I want to do is play facial expressions in sequence with the sounds, which is why I put the open mouth one where I did, between two sounds. I am not sure I understand, but it sounds like you are telling me that "the" animation would trigger just once, as soon as the permission is granted. If this is the case, this will not work for my application. What I was hoping for was a simple way for permission to be granted when the script was turned on, and then the loop with the sounds and anims could just play until it was stopped.

And yes, a switch is a switch, but in this case when I stop and then restart the sequence by changing script states, the sequence starts from where it left off, which is what I want it to do.rather than start from the beginning, 

Share this post


Link to post
Share on other sites

That's fine.  I assumed there was only one anim.  If there's more than one, it doesn't make any difference.  You only need to request PERMISSION_TRIGGER_ANIMATION one time.  Once it's granted, you can turn animations on and off at will.  The only difference from what I said before is that instead of triggeriing the anims in run_time_permissions, you can leave that event almost empty and then simply do a test before starting or stopping an anim elsewhere:

if (llGetPermissions() & PERMISSION_TRIGGER_ANIMATION){    llStopAnimation(gCurrentAnim);    llStartAnimation(gNextAnim);    llPlaySound("Funky Sound",1.0);}else{    llRequestPermissions(llGetOwner(),PERMISSION_TRIGGER_ANIMATION);}

The stuff you put in run_time_permissions is just whatever it takes to start your looping sequence.

Oh, and if you are concerned about where you left off last time, just save that information when you flip the switch OFF so that you remember it when you turn it back ON again.

Share this post


Link to post
Share on other sites

In general, llSleep is not a good way to handle timing.  The function does exactly what it says... it puts the entire script to sleep for the designated period.  During that time, the script cannot respond to any environmental stimuli so, for example, you cannot expect it to receive chat messges or respond to collisions or touches.  It's really asleep.  Sometimes llSleep is exactly the right function -- to give the script 0.2 seconds to wait before sending a message to a newly rezzed child, for example.  I woiuld avoid it for most longer delays and certainly for any repeated delays.

Instead, use a timer.  If you have a looping series of actions, track their progress with a global counter, as in

timer(){    gCount = (++gCount)%4;    llPlaySound(llList2String(["One","Two","Three","Four"],gCount),1.0);    if (gCount == 1)    {        llStartAnimation("Squinty Eye");    }}

where gCount is your global integer counter.  I'm assuming that your anim is not a looping anim but a single shot, so it doesn't need to be stopped. So, with llSetTimerEvent(10.0), the sequence will fire every ten seconds and gCount will take successive values 1, 2, 3, 0, 1, 2, 3, 0, 1, 2....  A different sound will trigger with each time around the loop and the anim will only trigger when gCount = 1.

There are lots of ways to configure a timing loop like this, but this one's nice and simple.

 EDIT:  BTW, notice that if you stopped the timer for some reason, the current value of gCount would be preserved, so that if you start it up again it would pick up right where it left off.

Share this post


Link to post
Share on other sites

Yes, since the last bit of code you posted I have played with it, and I have discovered that llSleep is not the best of options, as the internal animations for facial expressions I am trying to use will not persist through the sleep phase, and in any event are very short. It seems in order to get an extended open mouth anim I will have to trigger "express_open_mouth" repeatedly, Unless you know of a better way to do this?

Thanks for posting the timer info, but I am afraid it is just plain beyond me at this point, I looked up llList2String to try to puzzle it out, but was unable to come up with the proper syntax to make the script compile and run, At my level I feel lucky to have guessed correctly where to plug in the last code snippet you posted. 

Here is what I have so far that works but has the trouble with llSleep:

default{    state_entry()    {         llSetTimerEvent(10);    }   timer()    {        if (llGetPermissions() & PERMISSION_TRIGGER_ANIMATION){               llPlaySound("ed124764-705d-d497-167a-182cd9fa2e6c",1);        llSleep(10);        llStartAnimation("express_open_mouth");        llPlaySound("ed124764-705d-d497-167a-182cd9fa2e6c",1);        llStartAnimation("express_open_mouth");        llSleep(10);        llStartAnimation("express_open_mouth");        llPlaySound("ed124764-705d-d497-167a-182cd9fa2e6c",1);        llSleep(10);        llStartAnimation ("express_open_mouth");        llPlaySound("ed124764-705d-d497-167a-182cd9fa2e6c",1);        llSleep(10);//Etc.       }else{    llRequestPermissions(llGetOwner(),PERMISSION_TRIGGER_ANIMATION);}}}

This is shortened considerably; the actual script has 29 llPlaySound calls in it. (And of course the actual UUID's are all different.) 

 

As I am using UUID's rather than sounds from the prim's inventory, would I put those in the list llList2String references?

Also, I must admit the way timers function has me baffled at this point. I see timer() in your code above, and gCount seems to keep track of the current place in the sequence, but what tells the script to wait until sound One is done before triggering sound Two? The timer, obviously, but I mean how? (EDIT) Oh, it's in the Indexes, I guess, but that whole business is rather opaque to me just now.

Also, when using UUID's, is this the best (meaning easiest) way to do this, or can I just put the UUID's in sequence after the llPlaySound call?

Thanks for all your patience with a scripting noob. ;)

Share this post


Link to post
Share on other sites

The proper syntax is already there.  :smileywink:  The best plan for right now is not to focus so closely on syntax that you lose sight of what you are trying to do.  This is all about logic, designing a path that the computer can follow unambiguously to produce the effect you want.  Syntax is just your toolbox.

So, examine the last bit that I posted.  When you issue a command to llSetTimerEvent(10.0), you are telling the computer to keep repeating whatever is in the timer event every ten seconds.  A global counter is just an integer that you add to each time the script enters the timer event: 1, 2, 3, 4, 5, 6, 7, 8, 9, ......  In this particular case, we want to play a sequence of four different sounds -- a different one each time the timer event fires -- but we want to keep repeating the sequence endlessly.  So, we break the logic prolem into two parts:

1. How do we tell the script to do a sequence of four things?  We could look at the counter and then write

gCount = gCount +1;

if (gCount == 0) {//Do thing 1}

else if (gCount == 1) (//Do thing 2}

else if (gCount == 2) {//Do thing 3}

else if (gCount == 3) {//Do thing 4 and, Oh by the way, reset gCount to zero}

The script would go around and around, making gCount go 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3... and executing a different condition each time.

All I did was to shorten that.  In place of gCount = gCount +1, I wrote gCount = (++gCount)%4.  That means, literally, "add one to gCount, then divide the result by 4 and keep the 'leftover' part as a new value of gCount." So ....

if gCount = 0, (++gCount)%4 = (1)%4 or "4 goes into 1 zero times with 1 left over"

if gCount = 1, (++gCount)%4 = (2)%4 or "4 goes into 2 zero times with 2 left over"

and so on.  Each time I only care what's left over.  That's the new value of gCount.  If you follow that around a few times, you'll see that gCount will go 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, .....

2.  The other half of the logic is to answer "What do you want the script to DO each one of those times?"  Well, that's easy: Play a different sound.  So we'll want to put the four sounds into a list and grab a different one each time.  Here's the list:  ["One","Two","Three","Four"] .  Those are the names of the four different sounds.  If I had UUIDs instead, I could put them in the list.  So how do I grab the right one and play it when I want to?  Use the counter.  If gCount = 0, we want to play the first sound, "One".  If it's = 1, we want to play the second sound, "Two", and so on.  In other words, the sound we want to play is

llList2String(["One","Two","Three","Four"],gCount)

That's the sound name (or UUID) that we put into llPlaySound.

llPlaySound(llList2String(["One","Two","Three","Four"],gCount), 1.0);

Bingo!

So yes, there's some syntax to learn, but what's really important is digging into the logic of your task and getting that clear in your head.  The syntax is just the tool you use to express the logic in the end, whether you do it the long, expanded way or my more shorthand way.

Now, if you look though that carefully and compare it to your script, you'll see one big logical difference right off.  Your script does one BIG stack of things each time it goes through the timer event.  Then ten seconds later it does the same BIG stack of things.  In that mix are a number of ten second llSleeps. Um..... All in ten seconds.  Not logical.  And each of those llSleeps locks up the script so that you can't pass it any important information -- like a link message that says "STOP".

Share this post


Link to post
Share on other sites

When speaking of syntax, I meant the syntax required to get the script to compile and run. I realize it is important to understand the logic of what is going on, which is why I asked about how counters function. Thanks for answering that, I now understand at least a tiny bit better. 

From what you posted, I came up with this: 

default{    state_entry()    {         llSetTimerEvent(10);    }   timer()   {    gCount = (++gCount)%4;    llPlaySound(llList2String(["One","Two","Three","Four"],gCount),1.0);    if (gCount == 1)    {        llStartAnimation("Squinty Eye");    }    }

 However, I think I still need to put in a list for the llList2String to reference, right? In any event, this will not compile. I see now how it is supposed to work, and how to make it work for me, but I am still missing one key line of code, it seems.

Share this post


Link to post
Share on other sites

Yeah, I knew what you meant.  It's the same distinction I was trying to make, separating what you want to do from how to do it.  If you get the first part clear, the second part is a lot easier.  :smileywink:

Anyway.....  You have the basics down, almost. You do still need to declare gCount as a global integer, and you need to count your { open and closed curly brackets } to be sure that you have the same number of each (and in the right places).  Other than that, your script should run.  To make it play your actual sounds, you need to provide their names or UUIDs in place of "One", "Two", "Three", Four", which are obviously placeholder examples.  You have two options there.  One is to use the script exactly as written and put your own sound file names or UUIDs in tthe llList2String function.  The other way, more flexible, is to create a global list and then call it by name.  The mild advantage of doing that is that you can use the length of the list itself as a variable for controlling your timer event.  Study this:

integer gCount;list gSounds = ["One","Two","Three", "Four"];default{    state_entry()    {        llSetTimerEvent(10.0);    }   timer()   {        gCount = (++gCount)%(llGetListLength(gSounds));        llPlaySound(llList2String(gSounds,gCount),1.0);        if (gCount == 1)        {            llStartAnimation("Squinty Eye");        }    }}

If you compare this with what you had, you'll see that the logic is virtually the same.  Making the list into a global variable makes it a little easier to modify at the top of the script if you need to, and the number of steps in the timer cycle will be adjusted automatically when you do. 

From here, your broad plan should be (1) to spend time with basic LSL tutorials and (2) to spend even more time doing fearless experiments, turning a script that works into one that dances and does magic tricks the way you really want it to.  Be bold.

Share this post


Link to post
Share on other sites

Ah, I knew it was something like that, I just wasn't sure where it was supposed to go. Thanks so much for all your help and patience. ;)

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this  

×
×
  • Create New...