Atmosphere Audio Main page

Lets hear it! - That Invisible Trigger & Those Controls

Last time, we looked at some simple scripted sound effects. Now for some trickier tricks, to raise the fun level! The coding is a bit less intuitive, so your 'ole doc is going to dissect this bit of script in some detail for ya.

You scriptophobes should listen up too. Here's another chance to show yourself that you can understand Atmo JavaScript. Believe you me, if the doc can do it, so can you. Just think logic. You know, get into that "Insert tab A into slot B..." frame of mind. Okay then - enough pep talk already.

What we are looking at first is the cool effect of having your visitors trigger sound effects when they get near some object. This trigger object is usually an invisible Atmo anchor, which starts a one-time sound effect. We are not talking about the constantly looping 'positional' and 'proximity triggered' sound of, say a water fountain. as we described last time. Of course you can also use your invisible booby-trap to start an animation, move an object, or any such - at the same time you start your sound.

If you visited my Audio Lab, you saw the red box that starts a train sound when you get within 6 feet. And if you downloaded the script, you may already have seen the code we will dissect next.

By the way, you would usually use an anchor, not a box like I did. You place that anchor in your main world, as your trigger object. The sound trigger script can either be attached to that anchor or to something else. I had to use a box in my demo because anchors are always invisible. Demonstrating an invisible trigger with an invisible trigger is just too invisible, if you ah, see what i mean.

All set then? Here we go!


Invisible Proximity Sound Trigger
The Problem
You have a sound file you want to play one time, whenever a visitor comes within a certain distance ("trigger perimeter") of a trigger object. Actually, you also have to avoid the technical problem of the sound restarting constantly, before the player can leave the trigger perimeter. Scripts run fast, so this will surely happen (as your doc soon discovered), if you don't prevent it. Otherwise, your result will be a stuttering sound, or even silence, as the sound file is restarted constantly before it is even audible.

Uncommented Code Version
---------------------------------------------------
// -- ACTOR DRIVEN PROXIMITY SOUND TRIGGER --

stage1 = stageModel.getSolidObject(0).rootPrimitive;
trigBox = stage1.find(".../sound-trigger");
soundPlaying = false; //Set a flag for our trick to stop sound restarting constantly inside trigger radius.
timestep=function()
{
   if(player.dist(trigBox.position) < 6 && !soundPlaying)
   {
      soundPlaying = true;
      trigSound.play();
   }

   else if (soundPlaying && player.dist(trigBox.position) >= 6)
   {
      soundPlaying = false;
   }
}
---------------------------------------------------

Commented Code Version
---------------------------------------------------

//Earlier in the script this snippet came from, sound file was defined as "trigSound". Its sound properties were set.
//Remember that JavaScript is case sensitive. 'trigBox' and 'trigbox' are too different objects.

stage1 = stageModel.getSolidObject(0).rootPrimitive;
trigBox = stage1.find(".../sound-trigger"); //Find the box "sound-trigger" in main world. Remember it as "trigBox".
soundPlaying = false; //Set a flag so we know when the sound is playing. When we start our script, it is not.
//Now create a function to start the sound playing, but only once, when a player enters the perimeter.

timestep=function()

{
   //Below, if the player is within (< means "less than") the perimeter distance of 6 feet from the box, AND (&& means "and")...
  //...the sound is NOT (! means "not") already playing, then set the flag to 'true' and play the sound:
  
if(player.dist(trigBox.position) < 6 && !soundPlaying)
   {
      soundPlaying = true;
      trigSound.play();
   }

   //But if the sound is already playing, AND (&&) the player is 6 feet or more (>= means "greater than or equal to") from...
   //...the box, set the flag to 'false'.
   
else if (soundPlaying && player.dist(trigBox.position) >= 6)
   {
      soundPlaying = false;
   }
}

How it Works
The logic: conditions and results
Okay, this is simple stuff for the intermediates and experts, but I wrote it all out for the rest of us. Hope I covered all the angles! It goes like this:

1. The player has never been, and is still not, within 6 feet of the trigger box:
Then the 'if' is false and is therefore not executed, because the player is not within 6 feet. Also the 'else' is false and is therefore not executed, because the sound is not already playing.
So, the code does nothing. This is a good thing.

2. The player enters the trigger perimeter:
The 'if' is now true, the player is inside the perimeter and the sound is not already playing.

Therefore, the flag is set to indicate that the sound is playing and indeed the sound is now played. The 'else' is false because the player is still inside the perimeter, so the flag stays at 'sound is playing'. With the flag still set to show that the sound is already playing, it cannot be started again. This is also good.

3. Player stays in the perimeter after the sound finishes:
Even though the sound has actually finished, nothing has happened to change the flag, so the 'if' is false. The 'else' is also false, because the player is still inside the perimeter. So, nothing happens and the sound does not play more than once. excellent - the problem is solved!

4. The player leaves the perimeter:
The 'if' is false, whether the sound is still playing or not, since the player is outside the perimeter. The 'else' is true if the sound is still playing. Therefore the flag is set to 'not playing'. This has no affect on the sound that is already playing. It will complete and then shut off. But it does reset the trigger for the next victim.

If the player leaves the perimeter after the sound completes, neither "if" nor "else" is run and the flag still shows the sound as playing - temporarily. But this causes no problem and the flag is reset to "false" with the next timestep (next displayed frame) of your world. That is what timesteps do for a living. That is why, at the 3rd line, where the flag is created, it is set to false, even before any conditions are tested by the "if" or the "else".

Another detail is the way the distance is handled. In the "if", it is "less than 6 feet". It would be easy to then set the "else" to "more than 6 feet". But that would mean that at exactly 6 feet we are in a never-never land of indecision - a "dead band". To avoid this fate, the "else" was wisely set to "equal or greater than 6 feet".
Thanks to Steven Licciardi, the code guru behind George Lippert's masterful worlds, for coming to my rescue with the above script, when mine had that repeating problem.


The final unfinished biz from my last visit is those cool controls you can script into the "Controls" palette of the Atmosphere plugin. These are easy to copy for your use.

Just copy the control codes from my Audio Lab script, paste them into your own script and change the references to your own. It's a good idea to put the control code right below your code for that individual sound.

Here is an example. You would simply change all the stuff that starts with "my.." to your own names. Don't miss those single quotes on the button label and don't call more than one button by the same name. After you run your script and see how the buttons look, you may want to add spaces (stay inside the quotes) to some button labels to make all your labels line up nicely.

The button names and sound file names must not start with a number or contain spaces or punctuation (except "_" the underscore). Making the first character of each word, after the first word, upper case is a JavaScript programmer's "convention" (an unofficial standard) to make them more readable. Remember that JS is case sensitive, so "myButtonName" is a different name than "mybuttonname".
---------------------------
// - A Typical Sound "Play/Stop" Control -
myButtonName = Button('My Button Label').add();
myButtonName.onClick = function()
{
   if (mySound.active)
   {
      mySound.stop();
   }
   else
   {
      mySound.play();
   }
}
---------------------------

Next time, I'll attempt to attach a sound to a moving object, so that the sound also moves.
Until then, stay healthy by getting plenty of world building exercise!



     Back to Atmo Audio Main page