Managing Sound Effects


There are two ways to use sound effects in Flash. You can place a sound directly on a frame, or you can pull it from the library with ActionScript using attachSound(). Using frames to hold your sounds is easy and predictable; the sound plays when that frame is reached. The advantage of using this technique is that you can control the volume and other sound properties in the authoring environment. With attachSound(), the sound can be started or stopped using ActionScript. Further, you have control over the volume and panning of the sound in real time. With sounds placed on a frame, however, you cannot change these properties (pan and volume) of the sound after they have been set in the authoring environment the game player's actions are not going to affect the properties. In short, as always, there are advantages and trade-offs either way. In this section we discuss both ways of using sound effects in your games through the use of several example files.

Sound Placed on Frames

I've seen the source files for many Flash games over the last few years, and one thing I often find is that people put their sounds all over the place. For example, in one of these files, the sound for each object was inside the movie clip for that object a character-hopping sound was inside the character movie clip, the gunfire sound was inside the gun movie clip, and so on. There is nothing wrong with using sounds in this way, but it's not very efficient. Keeping track of your sounds is difficult, and it's not always easy to add new sounds.

Here is an easy way to keep all of your sounds in one location so that you can easily add multiple sound effects to your game. (This is also going to make it easy to create a sound on/off toggle.)

The technique is simple. We create a movie clip whose sole purpose is to hold sounds. There is one sound on each frame. Each frame has a frame label and an action that sends the playhead in that movie clip back to frame 1. We then create a function outside of this movie clip, which I usually call playSound(). This function accepts a parameter that should be the name of the sound or the label of the frame that needs to be played. It then tells the sound-effects movie clip to play that specific frame. That's it!

graphics/tip_icon.gif

The sound and the frame label do not have to have the same name, but it's always good to have a descriptive name for the label to make it easy to remember and to associate with the proper sound. For instance, the sound itself could be gunfire.wav, but the label could be shoot.


Now since it's so easy, and since I want to introduce you to it before we jump into the example file-let's set up the toggle. As you can guess, a sound on/off toggle is a button that a game player can click to turn the game sound on and off. If you spend a lot of time finding the best sounds for your game, then you probably want people to hear them (and they probably want to, too). But the truth is (sssshhh!) that many players will play your game over the Internet when they are in the office, and they won't want anyone to hear that they aren't working. There are plenty of other situations in which a person might not want the sound to play. So all in all, it's a good idea to provide the game player a way to turn the sound on and off.

graphics/cd_icon.gif

Let's look at an example. Open missiles.fla in the Chapter12 directory. This is a simple game (never completed). The triangle at the bottom of the screen is a ship. You can move the ship left and right with the left and right arrow keys, and you can make it fire a shot vertically by pressing the spacebar. The object is to shoot as many bubbles as possible. In this file we only have two sounds the sound of the ship firing and the sound of a bubble popping. To the left of the stage you will see a movie clip called soundFX.

graphics/12fig01.gif

Double-click this movie clip to look inside. There are three layers in it. You can ignore the bottom layer, Text. It is only there so that we can find the movie clip on the stage. Without the text inside this movie clip, we would have a hard time locating it. The top layer, Labels, contains two frame labels, shoot and pop. The layer in the middle, Actions, contains actions on three frames. On frame 1 there is just a stop() action, so the movie clip doesn't play until told to. The actions on the two labeled frames are the same: this.gotoAndStop(1). When a sound plays, the playhead immediately moves back to frame 1 and waits for further instructions.

At the top of frame 1 on the main timeline you'll find the function playSound():

 1   function playSound(name) { 2      if (soundOn) { 3         soundFX.gotoAndPlay(name); 4      } 5   } 

This function accepts a parameter called name. This is also the label of the frame we wish to play. An if statement in this function checks to see if the sound toggle is on or off. The action soundOn = true is on frame 1 just before this function. That sets the sound toggle to on. If that value ever becomes false, the sound will no longer play. In line 3 of the function above we tell the soundFX movie clip to go to a certain frame to play a sound. This is a very useful technique. We can now add as many sounds as we would like to the soundFX movie clip, and then play those sounds very easily.

Select the Ship movie clip at the bottom of the stage, and if it's not already open, open the Actions panel. On line 19 of the ActionScript, you will see there is the action _root.playSound("shoot"). This action is executed when the script shoots a missile (that is, when the spacebar is pressed). Now select the instance of the Bubble movie clip and open the Actions panel. On line 12 of the ActionScript you will see _root.playSound("pop"). This calls for the pop sound to be played when the bubble detects that it has been hit with a missile.

There is a button in the bottom-right part of the screen called Toggle Sound. This button has been programmed to toggle the sound on and off. Remember that the sound is considered on if soundOn has a value of true, and off if soundOn has a value of false. Here is the ActionScript on this button:

 1   on (press) { 2      soundOn = soundOn ? false : true 3   } 

The action in line 2 is executed when the Toggle Sound button is clicked. The action used here is a quick shortcut that is commonly used for toggling Boolean values. What you see to the right of the = sign is called a ternary operator. That means it's an operator with three operands, which are (in this case) soundOn, false, and true. This is a conditional statement. If the first operand has a value of true, then the entire statement takes the value of the second operand, else it takes the value of the third operand. This is equivalent to our having written an if statement like this:

 if (soundOn) {    soundOn = false; } else {    soundOn = true; } 

The one-line conditional statement syntax used here is used occasionally through the games programmed in the third section of this book. It's an easy way to toggle a value in one line of code rather than five.

You have just seen a very simple technique for adding sounds to games. In the next section we'll show another technique for adding sounds to games using ActionScript.

Sound Controlled with ActionScript

To control a sound with ActionScript, you pull it from the library and attach it to a sound object using attachSound(). To pull a sound from the library, you must give it a linkage identifier, just as you would do with a movie clip when it's to be used with the attachMovie() function (as we discussed in Chapter 5, "Collision Detection"). The advantage of using ActionScript to control sounds rather than placing them on a frame is that ActionScript gives you much more control. You can change the sound's volume or pan at any time. Also, you do not have to start playing a sound at its beginning you can start it anyplace. For instance, you can start a sound that is 4 seconds long at the third second, and as a result you'll hear just the last second of the sound. In this section we will look at three example uses of controlling sounds with ActionScript.

Controlling sound based on object speed

graphics/cd_icon.gif

For our first example, open ball.fla from the Chapter12 directory on the CD. In this file you'll see a ball and four walls. The ball has been given an initial velocity, and it falls under gravity. The result is a ball that bounces off the walls, much as a basketball would, eventually stopping. This file uses a sound effect called bounce.wav, with a linkage identifier named bounce. The sound's volume depends on how hard the ball hits each wall. The pan of the sound that is, the amount and volume that the sound plays in each speaker depends on the ball's x position. You've already seen the ActionScript used to bounce the ball around in Chapter 4, "Basic Physics"; Chapter 5, "Collision Detection"; and Chapter 6, "Collision Reactions." What is new in this file is the function called playSound(), for which the ActionScript is shown below.

 1   function playSound(x, speed) { 2      if (soundOn) { 3         var ballX = x-left; 4         var factor = ballX/(right-left); 5         var pan = -100+factor*200; 6         var maxSpeed = 15; 7         var minSpeed = 1; 8         var speed = Math.abs(speed); 9         var factor = speed/(maxSpeed-minSpeed); 10        if (speed<minSpeed) { 11           var factor = 0; 12        } 13        var volume = factor*100; 14        if (volume>0) { 15           bounce = new Sound(); 16           bounce.attachSound("bounce"); 17           bounce.setPan(pan); 18           bounce.setVolume(volume); 19           bounce.start(); 20        } 21     } 22  } 

This function is called whenever a collision is detected. The x position of the ball is passed into this function so that we can determine how much to pan the sound. The speed of the ball when it collided with the wall is also passed into this function, so that we can determine the volume needed. The speed passed in is the speed affected by the collision. For example, when the ball hits the floor, we pass in the y speed (not shown), since the x speed is unaffected. In line 3 we set a variable called ballX. The x position passed into this function is of the ball on the main stage. What we need to know in order to properly set the pan is the ball's x position with respect to the walls. The variable ballX stores the position of the ball with respect to the left wall. In line 4 we set a variable called factor. The concept illustrated here is a very useful one called normalization. The variable factor is a normalized number; that means it will always be between 0 and 1. We are looking for the ball's distance from the left wall in normalized terms. If factor is 1, then the ball is all the way over on the right wall; if it is 0, then the ball is on the left wall. If it is between 0 and 1, then the ball is somewhere in between the two walls. You find a normalized value by taking the value you want to normalize, in this case ballX, and dividing it by its maximum possible value, which in this case is the total distance between the left and right walls.

Next we set a variable called pan to store the pan amount (line 5). A pan of 100 means that all of the sound comes out of the left speaker, and a pan of 100 means that the sound comes completely out of the right speaker, for a total possible difference of 200 between left pan and right pan. We can set the pan value by starting with 100 and adding to it the value of factor*200. If factor is 1, then pan is 100, so the sound will play from the right speaker. If factor is 0, then pan is 100, and the sound will play completely from the left speaker.

In lines 6 9 we determine another factor variable, this one for speed. We will use this normalized number to determine the volume settings. If factor is 1, then the volume is maximum; if factor is 0, the volume is minimum. We use the current speed and the maximum speed allowed to determine the factor variable. Finally, in lines 15 19 we create a sound object (line 15), attach the bounce sound to that object (line 16), set the pan of the sound (line 17), set the volume of the sound (line 18), and finally play the sound (line 19).

graphics/12fig02.gif

Generate a SWF from this file. You can see that the volume is dependent on the speed at which the ball collides with the wall. Also notice how the pan changes with the position of the ball.

Controlling sound based on factors other than speed

graphics/cd_icon.gif

For the next example, open billiard_ball.fla in the Chapter12 directory. This file was taken directly from Chapter 6, "Collision Reactions," and modified to play a sound when a collision occurs. In this section I'm going to show how you can dynamically change the volume of the sound based on information other than speed. In the case of a ball bouncing off a wall, the volume is dependent on the speed of the ball. But imagine two billiard balls moving in the same direction very fast. If ball 1 is just a little bit faster than ball 2, then it may catch up and collide with ball 2. But even though these two balls are moving very fast, that doesn't mean the collision should be loud, since they may just barely collide. Instead of using speed, we will look at the change in speed. The volume of the sound is completely based on the difference between the speed before the collision and the speed after the collision.

Here is the playSound() function:

 1   function playSound(speedDiff) { 2      if (soundOn) { 3         var maxSpeed = 5; 4         var factor = speedDiff/maxSpeed; 5         var volume = 100*factor; 6         hit = new Sound(); 7         hit.attachSound("hit"); 8         hit.setVolume(volume); 9         hit.start(); 10     } 11  } 

In line 3 we set a variable called maxSpeed with a value of 5. The value tells the script to play the sound at maximum volume when the speed difference hits that amount (and of course you can alter that value to suit yourself). Right now, if a speed difference of 5 occurs on a collision, then we will hear the volume at its maximum. Let's look at the two lines of code that call this function, inside the ball2BallReaction() function:

 1   var speedDiff = Math.sqrt((b1.xmov-xVel1)*(b1.xmov-xVel1)       +(b1.ymov-yVel1)*(b1.ymov-yVel1)); 2   playSound(speedDiff); 

In line 1 we use the Pythagorean theorem (the distance equation) to find out the difference between the speed before the collision and the speed after the collision. This value is then passed into the playSound() function (line 2).

Generate a SWF to verify for yourself that this works properly. Go back to the FLA file and change some of the velocity variables; keep retesting to convince yourself that this method works well. You can use this dynamic volume technique with a game of pool or any other game where two moving objects collide at potentially varying speeds.

Volume controlled through acceleration

Up until now we've been talking about simple speed. Now, in our third example, we'll add acceleration into the mix, and outline a simple technique used for acceleration and deceleration in racing games. Imagine a car sitting at the starting line in a racing game. When it's time to start driving, you've got to accelerate, and an acceleration sound plays. What happens if during the middle of the acceleration you decide to decelerate? Well, you need to play a deceleration sound. Sounds easy enough, right? The problem is that the sound of the deceleration would be different depending on how fast the car was moving when you started to decelerate.

graphics/cd_icon.gif

Open car.fla in the Chapter12 directory. In this example we show how to handle race car driving sounds from idle (not moving) to maximum speed, and how to handle the sounds for acceleration or deceleration at any time. This is done with four sounds:

  • A loop played for the idle

  • A loop played for the car when it is at maximum speed

  • An acceleration sound that plays from idle all the way up to maximum speed

  • A deceleration sound that plays from maximum speed all the way down to idle

The acceleration and deceleration sounds are the same length. (In fact, the deceleration sound was actually made by reversing the acceleration sound in a sound-editing program.)

When the car accelerates, we play the acceleration sound. When the player instructs the car to decelerate, we check to see the position of the acceleration sound. We then use this information to figure out where to start the deceleration sound. Likewise, if while decelerating the player instructs the car to accelerate, we check the current position of the deceleration sound and use that information to determine where to start the acceleration sound.

Got that? Now let's inspect the ActionScript. First we use the following actions to create a sound object for each of the four sounds, and to attach a sound to each object:

 1   accel = new Sound();  2   accel.attachSound("accel"); 3   decel = new Sound(); 4   decel.attachSound("decel"); 5   hi_loop = new Sound(); 6   hi_loop.attachSound("hi_loop"); 7   low_loop = new Sound(); 8   low_loop.attachSound("low_loop"); 9   accel = new Sound(); 10  accel.attachSound("accel"); 11  decel.attachSound("decel"); 

And here are the functions that play the sounds:

 1   function playAccel(offset) { 2      accel.setVolume(vol); 3      accel.start(offset); 4      accel.onSoundComplete = accelDone; 5      playing = "accel"; 6   } 7   function playDecel(offset) { 8      decel.setVolume(vol); 9      decel.start(offset); 10     decel.onSoundComplete = decelDone; 11     playing = "decel"; 12  } 13  function accelDone() { 14     accel.stop(); 15     hi_loop.setVolume(vol); 16     hi_loop.start(0, 100000); 17  } 18  function decelDone() { 19     decel.stop(); 20     low_loop.setVolume(vol); 21     low_loop.start(0, 100000); 22  } 

The first function, playAccel(), is used to play the acceleration sound. All of these functions accept a parameter called offset that is used to offset the starting point of the sound they will play. If the duration of the sound is 10 seconds and the offset is 3 seconds, then the ActionScript will skip the first 3 seconds of the sound and start playing it immediately. In line 4 we set an event handler for the acceleration sound. When that sound is finished playing, it will call the function accelDone(). This allows us to easily tell when the acceleration has finished playing, so we know when to start playing the maximum-speed sound loop. On line 7, the next function, playDecel(), plays the deceleration sound. When it has finished playing, it calls the function decelDone() so that the idle sound loop can start playing. These onSoundComplete events only fire if the sound is allowed to reach completion. If, for instance, the acceleration sound is stopped before it has finished playing, then accelDone() will not be called.

The final part of the ActionScript in this file captures all the user actions and handles the starting and stopping of sounds that result from those user actions. It also includes the controls for the bar, which is not really related to the functioning of the sounds.

 1   _root.onEnterFrame = function() { 2      if (playing == "decel" || playing == null) { 3         bar._yscale = (1-decel.position/decel.duration)*100; 4         if (Key.isDown(Key.UP)) { 5            if (playing == null) { 6                var start = 0; 7            } else { 8               var start = (accel.duration-                   decel.position)/1000; 9            } 10           decel.stop(); 11           low_loop.stop(); 12           playAccel(start); 13        } 14     } 15     if (playing == "accel" || playing == null) { 16         bar._yscale = accel.position/accel.duration*100; 17         if (Key.isDown(Key.DOWN)) { 18            if (playing == null) { 19               var start = 0; 20            } else { 21               var start = (decel.duration-                   accel.position)/1000; 22            } 23            accel.stop(); 24            hi_loop.stop(); 25            playDecel(start); 26         } 27     } 28  }; 

This is an onEnterFrame event that executes two main conditional statements in every frame. They check to see if the car is currently accelerating or decelerating. If the car is decelerating (line 2), then the bar movie clip on the stage is scaled to show what the car is doing. This movie clip has nothing to do with the sounds themselves; it is just giving us a visual idea of what is happening. Then the script checks to see if the up arrow key is being pressed. If it is, then the script starts the sound at position 0, or if the car is already in motion, it calculates what the sound offset should be. The sound offset is the reverse of the position of the deceleration sound. If the deceleration is 10 seconds long and is at the position of the third second, then the acceleration offset is 7 seconds. In lines 10 and 11 we stop sounds that could be playing, and in line 12 we start the deceleration sound by calling playAccel(). The second if statement (line 15) does the same thing as the first one, except that it checks to see if the car is currently accelerating. If the car is accelerating and the down arrow key is pressed, then the deceleration sound is played.

Generate a SWF from this file. Press the up arrow key. You should hear the car start to accelerate. If you let the acceleration reach maximum, then you will hear the maximum speed loop, hi_loop, start playing. At any time you can press the down arrow key to have the deceleration sound kick in. If you let the deceleration continue to completion, the idle sound, low_loop, will play.



Macromedia Flash MX Game Design Demystified(c) The Official Guide to Creating Games with Flash
Macromedia Flash MX Game Design Demystified: The Official Guide to Creating Games with Flash -- First 1st Printing -- CD Included
ISBN: B003HP4RW2
EAN: N/A
Year: 2005
Pages: 163
Authors: Jobe Makar

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net