Flash Game Development with Flex and Actionscript - Music and Sound FX

Flash Game Development with Flex and Actionscript - Music and Sound FX
Page content

In part 7 we added the ability to play animations, which then gave us some nice explosions. At this point we almost have something resembling a game, except for one: sound. In this article we will add some sound and music to the game.

Fortunately for us Flex makes embedding, playing and transforming sounds very easy. The first logical place to start is by adding some sounds to our ResourceManager.

Pump Up the Volume

package

{

import flash.display.*;

import mx.core.*;

public final class ResourceManager

{

[Embed(source="../media/brownplane.png")]

public static var BrownPlane:Class;

public static var BrownPlaneGraphics:GraphicsResource = new GraphicsResource(new BrownPlane(), 3, 20);

[Embed(source="../media/smallgreenplane.png")]

public static var SmallGreenPlane:Class;

public static var SmallGreenPlaneGraphics:GraphicsResource = new GraphicsResource(new SmallGreenPlane(), 3, 20);

[Embed(source="../media/smallblueplane.png")]

public static var SmallBluePlane:Class;

public static var SmallBluePlaneGraphics:GraphicsResource = new GraphicsResource(new SmallBluePlane(), 3, 20);

[Embed(source="../media/smallwhiteplane.png")]

public static var SmallWhitePlane:Class;

public static var SmallWhitePlaneGraphics:GraphicsResource = new GraphicsResource(new SmallWhitePlane(), 3, 20);

[Embed(source="../media/bigexplosion.png")]

public static var BigExplosion:Class;

public static var BigExplosionGraphics:GraphicsResource = new GraphicsResource(new BigExplosion(), 7, 20);

[Embed(source="../media/smallisland.png")]

public static var SmallIsland:Class;

public static var SmallIslandGraphics:GraphicsResource = new GraphicsResource(new SmallIsland());

[Embed(source="../media/bigisland.png")]

public static var BigIsland:Class;

public static var BigIslandGraphics:GraphicsResource = new GraphicsResource(new BigIsland());

[Embed(source="../media/volcanoisland.png")]

public static var VolcanoIsland:Class;

public static var VolcanoIslandGraphics:GraphicsResource = new GraphicsResource(new VolcanoIsland());

[Embed(source="../media/twobullets.png")]

public static var TwoBullets:Class;

public static var TwoBulletsGraphics:GraphicsResource = new GraphicsResource(new TwoBullets());

[Embed(source="../media/cloud.png")]

public static var Cloud:Class;

public static var CloudGraphics:GraphicsResource = new GraphicsResource(new Cloud());

[Embed(source="../media/gun1.mp3")]

public static var Gun1Sound:Class;

public static var Gun1FX:SoundAsset = new Gun1Sound() as SoundAsset;

[Embed(source="../media/explosion.mp3")]

public static var ExplosionSound:Class;

public static var ExplosionFX:SoundAsset = new ExplosionSound() as SoundAsset;

[Embed(source="../media/track1.mp3")]

public static var Track1Sound:Class;

public static var Track1FX:SoundAsset = new Track1Sound() as SoundAsset;

}

}

Here we embed the sound files just like we do for our graphics. This packages the sounds in the final SWF file giving us a convenient way to distribute and access them.

Now that we have made the sounds available we need to play them. Using the sound effects here we have an explosion that will be played when the player or an enemy dies, a sound for when the player fires weapons, and some background music to play while in the game.

Lets take a look at the Enemy class to see how to play a sound effect.

Enemy.as

package

{

import flash.geom.Point;

import mx.core.*;

public class Enemy extends AnimatedGameObject

{

static public var pool:ResourcePool = new ResourcePool(NewEnemy);

protected var logic:Function = null;

protected var speed:Number = 0;

static public function NewEnemy():Enemy

{

return new Enemy();

}

public function Enemy()

{

super();

}

public function startupBasicEnemy(graphics:GraphicsResource, position:Point, speed:Number):void

{

super.startupAnimatedGameObject(graphics, position, ZOrders.PlayerZOrder);

logic = basicEnemyLogic;

this.speed = speed;

this.collisionName = CollisionIdentifiers.ENEMY;

}

override public function shutdown():void

{

super.shutdown();

logic = null;

}

override public function enterFrame(dt:Number):void

{

super.enterFrame(dt);

if (logic != null)

logic(dt);

}

protected function basicEnemyLogic(dt:Number):void

{

if (position.y > Application.application.height + graphics.bitmap.height )

this.shutdown();

position.y += speed * dt;

}

override public function collision(other:GameObject):void

{

var animatedGameObject:AnimatedGameObject = AnimatedGameObject.pool.ItemFromPool as AnimatedGameObject;

animatedGameObject.startupAnimatedGameObject(

ResourceManager.BigExplosionGraphics,

new Point(

position.x + graphics.bitmap.width / graphics.frames / 2 - ResourceManager.BigExplosionGraphics.bitmap.width / ResourceManager.BigExplosionGraphics.frames / 2,

position.y + graphics.bitmap.height / 2 - ResourceManager.BigExplosionGraphics.bitmap.height / 2),

ZOrders.PlayerZOrder,

true);

this.shutdown();

ResourceManager.ExplosionFX.play();

}

}

}

With just one call to the play function we can play a sound effect. I told you Flex made playing sound easy. The Player class makes similar calls to the play function when creating a new weapon (during the enterFrame function) and also when it is destroyed (during the collision function). Download the source code at the end of the article to view those changes.

The process for playing background music is similar, but there are a few extra steps we need to take because we need to be able to manually stop the music when we exit the level (i.e. when we leave the Game state). Lets take a look at the changes to the Level class now.

Level.as

package

{

import flash.events.*;

import flash.geom.*;

import flash.media.*;

import flash.net.*;

import flash.utils.*;

import mx.collections.*;

import mx.core.*;

public class Level

{

protected static var instance:Level = null;

protected static const TimeBetweenLevelElements:Number = 2;

protected static const TimeBetweenEnemies:Number = 3;

protected static const TimeBetweenClouds:Number = 2.5;

protected static const TimeToLevelEnd:Number = 2;

protected var timeToNextLevelElement:Number = 0;

protected var levelElementGraphics:ArrayCollection = new ArrayCollection();

protected var timeToNextEnemy:Number = 0;

protected var enemyElementGraphics:ArrayCollection = new ArrayCollection();

protected var timeToNextCloud:Number = 0;

protected var timeToLevelEnd:Number = 0;

protected var backgroundMusic:SoundChannel = null;

public var levelEnd:Boolean = false;

static public function get Instance():Level

{

if ( instance == null )

instance = new Level();

return instance;

}

public function Level(caller:Function = null )

{

if ( Level.instance != null )

throw new Error( “Only one Singleton instance should be instantiated” );

levelElementGraphics.addItem( ResourceManager.SmallIslandGraphics);

levelElementGraphics.addItem( ResourceManager.BigIslandGraphics);

levelElementGraphics.addItem( ResourceManager.VolcanoIslandGraphics);

enemyElementGraphics.addItem( ResourceManager.SmallBluePlaneGraphics);

enemyElementGraphics.addItem( ResourceManager.SmallGreenPlaneGraphics);

enemyElementGraphics.addItem( ResourceManager.SmallWhitePlaneGraphics);

}

public function startup():void

{

timeToNextLevelElement = 0;

new Player().startupPlayer();

timeToLevelEnd = TimeToLevelEnd;

levelEnd = false;

backgroundMusic = ResourceManager.Track1FX.play(0, int.MAX_VALUE);

}

public function shutdown():void

{

backgroundMusic.stop();

backgroundMusic = null;

}

public function enterFrame(dt:Number):void

{

// add a background element

timeToNextLevelElement -= dt;

if (timeToNextLevelElement <= 0)

{

timeToNextLevelElement = TimeBetweenLevelElements;

var graphics:GraphicsResource = levelElementGraphics.getItemAt(MathUtils.randomInteger(0, levelElementGraphics.length)) as GraphicsResource;

var backgroundLevelElement:BackgroundLevelElement = BackgroundLevelElement.pool.ItemFromPool as BackgroundLevelElement;

backgroundLevelElement.startupBackgroundLevelElement(

graphics,

new Point(Math.random() * Application.application.width, -graphics.bitmap.height),

ZOrders.BackgoundZOrder,

50);

}

// add an emeny

timeToNextEnemy -= dt;

if (timeToNextEnemy <= 0)

{

timeToNextEnemy = TimeBetweenEnemies;

var enemygraphics:GraphicsResource = enemyElementGraphics.getItemAt(MathUtils.randomInteger(0, enemyElementGraphics.length)) as GraphicsResource;

var enemy:Enemy = Enemy.pool.ItemFromPool as Enemy;

enemy.startupBasicEnemy(

enemygraphics,

new Point(Math.random() * Application.application.width, -enemygraphics.bitmap.height),

55);

}

// add cloud

timeToNextCloud -= dt;

if (timeToNextCloud <= dt)

{

timeToNextCloud = TimeBetweenClouds;

var cloudBackgroundLevelElement:BackgroundLevelElement = BackgroundLevelElement.pool.ItemFromPool as BackgroundLevelElement;

cloudBackgroundLevelElement. startupBackgroundLevelElement(

ResourceManager.CloudGraphics,

new Point(Math.random() * Application.application.width, -ResourceManager.CloudGraphics.bitmap.height),

ZOrders.CloudsBelowZOrder,

75);

}

if (levelEnd)

{

timeToLevelEnd -= dt;

var scale:Number = timeToLevelEnd / TimeToLevelEnd;

if (scale < 0) scale = 0;

var transform:SoundTransform = backgroundMusic.soundTransform;

transform.volume = scale;

backgroundMusic.soundTransform = transform;

}

if (timeToLevelEnd <= 0)

Application.application.currentState = “MainMenu”;

}

}

}

In order to manipulate a sound when it is playing we need to keep a reference to the SoundChannel that is returned with the SoundAsset play function. To this end we have added a backgroundMusic property, which is assigned a value in the startup function. In the shutdown function we call stop to stop the music from playing. In the enterFrame function we have added some code that allow us to fade the music out once levelEnd has been set to true (i.e. the level has ended because the Player has died). This is done by assigning the transform variable to the backgroundMusic soundTransform property, modifying the volume property on the transform variable, and then setting backgroundMusic soundTransform property back to our edited transform variable.

While displaying graphics required jumping through some hoops, Flex makes playing sounds a trivial task. With only a dozen lines of code we have embedded the sound effects and then played them during the game.

With the sound effects implemented we have all the basic elements of a game. Fighting endless waves of randomly positioned enemies will get pretty boring though. In part 9 of the series look at implementing a way to define a level structure i.e. defining where and when enemies, background elements and any other game element are created.

Go back to Flash Game Development with Flex and ActionScript

This post is part of the series: Game programming with Flex

Learn how to create a Flash game using Flex with this step by step series.

  1. Flash Game Development with Flex and Actionscript - Getting Started
  2. States &amp; Double Buffer Rendering in Flex 4.0
  3. Flex &amp; Actionscript Tutorial: Adding Game Actions
  4. Creating an Interactive Animated Background With Flex and Actionscript
  5. Adding Weapons to Your Game: Flex &amp; Actionscript Tutorial
  6. Collision Detection With Actionscript
  7. Adding Bitmap Animations With Flex and Actionscript
  8. Adding Music and Sound FX With Flex and Actionscript
  9. Defining Levels: Game Design With Flex and Actionscript
  10. Actionscript Tutorial: Add a Scrolling Tiled Background