Warning: Declaration of thesis_comment::start_lvl(&$output, $depth, $args) should be compatible with Walker::start_lvl(&$output, $depth = 0, $args = Array) in /nfs/c03/h08/mnt/50298/domains/gamedev.michaeljameswilliams.com/html/wp-content/themes/thesis_18/lib/classes/comments.php on line 0

Warning: Declaration of thesis_comment::end_lvl(&$output, $depth, $args) should be compatible with Walker::end_lvl(&$output, $depth = 0, $args = Array) in /nfs/c03/h08/mnt/50298/domains/gamedev.michaeljameswilliams.com/html/wp-content/themes/thesis_18/lib/classes/comments.php on line 0

Warning: Declaration of thesis_comment::start_el(&$output, $comment, $depth, $args) should be compatible with Walker::start_el(&$output, $object, $depth = 0, $args = Array, $current_object_id = 0) in /nfs/c03/h08/mnt/50298/domains/gamedev.michaeljameswilliams.com/html/wp-content/themes/thesis_18/lib/classes/comments.php on line 0

Warning: Declaration of thesis_comment::end_el(&$output, $comment, $depth, $args) should be compatible with Walker::end_el(&$output, $object, $depth = 0, $args = Array) in /nfs/c03/h08/mnt/50298/domains/gamedev.michaeljameswilliams.com/html/wp-content/themes/thesis_18/lib/classes/comments.php on line 0
AS3 Avoider Game Tutorial, Part 9: Music and Sound Effects — Michael James Williams

AS3 Avoider Game Tutorial, Part 9: Music and Sound Effects

by Michael James Williams on March 3, 2009 · 129 comments

in Articles,Avoider Game Base,Tutorial

In this part of my AS3 and Flash CS3 conversion of Frozen Haddock’s avoider game tutorial, we’ll add background music and sound effects.

Click the preview image below to see how this will look (or, rather, hear how this will sound):

screenshot

If you’ve not been following the tutorial, grab the zip file of the game so far here and extract it somewhere. Otherwise, copy the files you’ve been working on so far to a new folder, as usual.

If you have been following along, you’ll probably have noticed that the graphics in this part look a little different. I realised that I’ve barely made any graphical changes to the game since Part 5, and people have been sending me these really awesome-looking versions of the game that they’ve made, so I wanted to make a few small changes. No code has been added; only the FLA has been modified. I’ve made a zip file that is identical to the one from the end of the last part, except it uses these new graphics — feel free to download it from here.

Either way, open the FLA file, and let’s get started.

###Finding Music

Remember in Part 8 I mentioned 8bitrocket was a great site? Well, as further proof, check out their Music page. There are about a hundred great music loops there, all designed to work with Flash, and each one has a 11kHz preview that you can use for free (the high-quality versions only cost $10). Thanks, 8bitrocket!

For the background music of my version of the game, I’m going to use their “8bit 1” loop (it’s near the bottom of the page). I think it fits, though it does remind me of some other song

Besides 8bitrocket, Flash Kit has some good loops, and the Newgrounds Audio Portal has some great stuff, too. Just remember to follow the licensing rules for any piece of music you want to use (and if you’re not sure, ask the creator!)

If you want to have something unique to your game, SomaTone (the team behind music for games such as Peggle, Medal of Honor: Frontline, and World of Warcraft) are getting into Flash games, so check them out if you have the money to spend.

And if none of those work for you, you can always create your own music. Just bear in mind that in this tutorial I am assuming that your music is a short piece that can loop, so if that’s not the case you may find some of it won’t apply to you.

Flash supports MP3, WAV, AIFF, and a few of the more obscure sound formats. Click here for a full list.

When you’ve got your sound file, create a new folder called *Sounds* inside your main game folder, and put the music in there.

###Getting the Music into the Game

In order to get the music into the final SWF, we need to get the sound file into the FLA. This is simple; just click *File > Import > Import to Library*. Then, navigate to the *Sounds* folder, select your music, and hit *Open*.

After a few seconds, it’ll appear in your Library:

screenshot

You can click that triangular play button to hear it. If you right-click the file and select *Properties*, you’ll get a dialog that’s a little different from the properties of a MovieClip or Button:

screenshot

There’s a lot on this Window, so let’s take a quick look.

– The first box contains the name of the *symbol* — just as our Avatar movieclip is named, well, Avatar. By default, this is set to the sound’s filename.
– Below that is some basic information about the sound: its location on the hard drive, the date it was created, the audio quality, length of the sound, and size of the file.
– To the left of that is an image of the sound’s wave representation.
– **Update** tells Flash to reload the sound file from the hard drive, which is useful if you’re editing the file externally.
– **Import…** allows us to swap the sound out for a different file, while keeping the other settings intact.
– **Test** and **Stop** can be used to preview the sound.
– **Device sound**, in the **Export Settings** section, is used if you want to specify a different sound for when your game is played on PDAs or mobile phones that use Flash Lite.
– The **Compression** drop-down list, also in the **Export Settings** section, allows you to modify how the sound is compressed when the SWF is created — this is useful if you want to reduce the size of a very large, high-quality sound file in order to make your game load faster on the web.
– The **Linkage** section is pretty much the same as for MovieClips and Buttons.

Leave the *export settings* as their default values, but change the name of the sound to *BackgroundMusic*. Also, export it for ActionScript, with a class name also of *BackgroundMusic*. (For now, we’re going to export it in the first frame as well.)

###Playing the Music

The music is included in the SWF now (if you save and test it, you might notice that the SWF has a larger file size than it did before), but we haven’t told it to play. We’ll use code to add the music to the play screen, just as we do to add the avatar.

Open *AvoiderGame.as*. We’re going to use two objects to play the sound:

1. The actual sound itself (i.e. *BackgroundMusic*, from the library).
2. A *sound channel*, which controls the sound.

These should be class-level variables, so that we can control them from anywhere within the play screen, so define them near the top, with the others:

public var backgroundMusic:BackgroundMusic;
public var bgmSoundChannel:SoundChannel;	//bgm for BackGround Music

*BackgroundMusic* is in the library, so that’s fine, but we do need to import *SoundChannel*:

import flash.media.SoundChannel;

Let’s make the music start when the action does by putting the setup code in the constructor function:

public function AvoiderGame() 
{
	backgroundMusic = new BackgroundMusic();
	bgmSoundChannel = backgroundMusic.play();

Line 28 initialises the sound clip from the library, just like the lines that say “avatar = new Avatar();” and “enemy = new Enemy( randomX, randomY );”. Line 29 assigns this clip to this channel, and plays it. Later on, we’ll see how to use multiple channels to handle different kinds of sound.

If you save and run the game, you may notice that the music plays once and then stops, without looping. We’ll get to this in a minute, but first let’s set the sound up with our preloader.

Things are different in CS4! If you run into trouble preloading your sounds with any version of Flash, I recommend you read this comprehensive tutorial on Activetuts+. It’s great, and it covers everything.

Much like with MovieClips, since we don’t want the background music to be a part of the preloader, we have to untick that “export in first frame” box in the Properties. (Do that now.) But, again like MovieClips, if we do this the sound won’t be a part of our SWF, and so won’t be able to play during the game. So we need to add it to the *AssetHolder* we made in Part 8.

Double-click the *AssetHolder* in the library to edit it. Now, we can just drag and drop the sound on top of the pile of stuff that’s already in there, but it makes it very hard to deal with things later. Instead, right-click *Layer 1* in the timeline and select *Insert Layer*:

screenshot

Call this layer something like *BackgroundMusic* (it doesn’t matter what exactly, because we never need to refer to it in code). Now, make sure this new layer is selected, and then drag and drop the *BackgroundMusic* from the library. You’ll notice that the keyframe in the timeline changes slightly, from this:

screenshot

to this:

screenshot

If you right-click any frame later on in the timeline on that layer and select *Insert Frame*, you’ll see why:

screenshot

It’s showing a visual representation of the sound’s waveform in the timeline. Neat.

Note: Make sure you undo this change; Flash can freeze if you try to test the game while it has compiler errors and the layer with the graphical assets is only one frame long but another layer is longer. I have no idea why this is. If you make sure the layers here are all one frame long, you should be fine.

If you save and run the game and simulate a slow download, you should find that it takes a lot longer, but that the preloader’s percentage appears fairly quickly. This is great! It shows that the sound is being included in the SWF but is not forcing the player to wait until it’s downloaded before the preloader can start.

Unfortunately, you’ll also find that the music is being played as soon as the menu appears. Dang. Why is this happening before the “new BackgroundMusic()” code is being run? Well, just as when you drag a MovieClip to the stage it appears without the need for any code, when you add a Sound to the stage it starts playing without any code. It’s simple to fix though. Edit your *AssetHolder*, and click on any frame in the timeline that contains the sound. In the Properties panel, the following options will be visible:

screenshot

All you need to do is change the *Sync* box to the option marked *Stop*:

screenshot

The appearance of the waveform in the timeline will turn blank to reflect this. If you test it again, you’ll find the music doesn’t start until the game does.

###Looping the Loop

So how can we make the tune play more than once? The simplest way to do it is by modifying our original code like so:

public function AvoiderGame() 
{
	backgroundMusic = new BackgroundMusic();
	bgmSoundChannel = backgroundMusic.play( 0, 10 );

Notice that line 29 has changed. That first argument, 0, tells the music to play from the beginning; if it said 30 it would start from 30 milliseconds in; 5000 would make it start from five seconds in, and so on. The second argument, 10, tells Flash how many times to loop the tune.

We could, therefore, just tell the music to loop 99,999 times, and be fairly confident that anyone playing the game will not still be playing after this time (the eight-second clip I’m using could be played continuously for nine days before reaching 99,999 loops). This is a solution, but it’s not a very flexible one.

Instead, what we can do is use an event listener (yes, another one!) to detect when the music has finished, and use an associated event handler to start it playing again. The event we need to detect is called *Event.SOUND_COMPLETE*, and we add the event listener to the sound channel like so:

public function AvoiderGame() 
{
	backgroundMusic = new BackgroundMusic();
	bgmSoundChannel = backgroundMusic.play();
	bgmSoundChannel.addEventListener( Event.SOUND_COMPLETE, onBackgroundMusicFinished );

No *import* is needed this time, since we already imported *flash.events.Event* before 🙂

Now for the event handler:

public function onBackgroundMusicFinished( event:Event ):void
{
	bgmSoundChannel = backgroundMusic.play();
	bgmSoundChannel.addEventListener( Event.SOUND_COMPLETE, onBackgroundMusicFinished );
}

[Note we’ve added the event listener to the sound channel again. This is because the line “bgmSoundChannel = backgroundMusic.play();” causes all event listeners on *bgmSoundChannel* to be lost.]

This is more flexible; for example, if we had several different pieces of music, we could play a different one each time the current one finished.

But now we have another problem. When you get game over, the music doesn’t stop — and if you click Restart, another sound channel is created, and the music is played twice at once! To solve this, we just have to tell the music to stop when the avatar hits the enemy (or whatever you’re using to trigger game over):

if ( avatarHasBeenHit )
{
	bgmSoundChannel.stop();
	dispatchEvent( new AvatarEvent( AvatarEvent.DEAD ) );
}

Line 164 shows how to do this. It’s really simple, just call *stop()* on the sound channel. This won’t dispatch an *Event.SOUND_COMPLETE*, by the way.

###Adding Sound Effects

Sound effects are just as easy to add as music clips. I’m going to make a short sound play every time a new enemy is created, as a demonstration.

The sound I’m going to use is called Synth Bleep 4. I found it on Flash Kit, and it was made by xk, which I think stands for XKrew. Thanks, xk!

Just as before, we need to:

1. Save the file to the *Sounds* folder
2. Import the file to the library
3. Give the Sound a different name (I picked *EnemyAppearSound*)
4. Export it for ActionScript but *not* in first frame
5. Add it to a new layer of the *AssetHolder*, but set the *Sync* to *Stop*

Phew. Now for some code. First, let’s create the Sound object itself, and a new sound channel to play it. Put these with the other sound variables we’ve created:

public var backgroundMusic:BackgroundMusic;
public var bgmSoundChannel:SoundChannel;	//bgm for BackGround Music
public var enemyAppearSound:EnemyAppearSound;
public var sfxSoundChannel:SoundChannel;	//sfx for Sound FX

Also, initialise the sound in the constructor function:

public function AvoiderGame() 
{
	backgroundMusic = new BackgroundMusic();
	bgmSoundChannel = backgroundMusic.play();
	bgmSoundChannel.addEventListener( Event.SOUND_COMPLETE, onBackgroundMusicFinished );
	enemyAppearSound = new EnemyAppearSound();

We need to make the sound actually play at the point in the code where the new enemies are being created. That’s this part in the *onTick()* function:

if ( Math.random() < 0.1 )
{
	var randomX:Number = Math.random() * 400;
	var newEnemy:Enemy = new Enemy( randomX, -15 );
	army.push( newEnemy );
	addChild( newEnemy );
	gameScore.addToValue( 10 );
}

Just add the line to play the sound, like so:

if ( Math.random() < 0.1 )
{
	var randomX:Number = Math.random() * 400;
	var newEnemy:Enemy = new Enemy( randomX, -15 );
	army.push( newEnemy );
	addChild( newEnemy );
	gameScore.addToValue( 10 );
	sfxSoundChannel = enemyAppearSound.play();
}

Save it and run it, and you'll hear a cacophony of thumping sounds, like being near any road ever in a city at night. Awesome!

One thing to note is that the *sfxSoundChannel* can clearly handle playing sounds that overlap. So, why bother using two sound channels at all? Why not just have a single channel that plays all of the music and effects?

There are two main reasons. First, having the background music separate like this means we can loop it easily with that event listener/handler. Second, different sound channels can be given different levels of volume, which means we can make the music quieter and the effects louder, within the game, without having to change the volume of the individual sounds.

###Going Further

Sound is really important in a game, and has a huge effect on the way it feels. There is so much you can add to a game, even with just these simple commands I've shown you.

If you want to take this much further, read up on the SoundChannel class, in particular the parts that explain how to use a SoundTransform.

Below are some ideas for challenges to help you to get to grips with using sounds in Flash.

A mute button. "Click here or press 'm' to mute." Not too hard, just use *.stop()* on all playing channels.
Building on this, how about separate mute buttons/keys for the music and effects?
Also, can you figure out how to make the mute button pause the music, rather than making it stop and then having to start from the beginning again? (Here's a hint: you can use *bgmSoundChannel.position* to find out how many milliseconds through the music you are at any given time.)

More sounds. A "ding" when the game has preloaded. A sad noise when you get game over. Different music tracks -- perhaps selected at random, perhaps selected by the player. Different sound effects for different enemies. Special noises for when you get 1,000, 5,000, 10,000 (etc) points.

Playing to the beat of the game. The sound effect I've used could sound like part of the background music, if only the timing were right. How about making the enemies appear in time with the music? I think you'll have to be more musically-inclined than I am to do this 🙂

Any other ideas? Post them in the comments!

###Wrapping Up

So that's music. Thanks again to 8bitrocket for letting me use their track. Thanks also to Flash Kit and xk for the sound effects.

As always, you can download a zip with all the files relating to this part of the tutorial here.

In the next part, we'll add multiple levels to the game.

{ 50 comments… read them below or add one }

Leave a Comment

Writing code? Write <pre> at the start and </pre> at the end to keep it looking neat.

Previous post:

Next post: