38
}
x -= theCanvas.offsetLeft;
y -= theCanvas.offsetTop;
mouseX=x;
mouseY=y; player.x = mouseX;
player.y = mouseY;
}
The eventMouseUp() handler is called when the player presses and releases the left
mouse button. When this event occurs, a missile will fire. The missile object is almost
identical to the alien object, because it includes speed, x, y, width, and height prop‐
erties. Because the player is firing the missile, we set the missile’s x and y positions to
the center of the player’s ship on the x-axis (player.x+.5*playerImage.width) and to
the y position of the player’s ship, minus the height of the missile (player.y - mis
sileImage.height):
function eventMouseUp(event) {
missiles.push({speed:5, x: player.x+.5*playerImage.width,
y:player.y-missileImage.height,width:missileImage.width,
height:missileImage.height});
Next is the first really critical line of code for the subject at hand: audio. For this first
iteration of Space Raiders, we simply call the play() function of shootSound. This will
play the shoot sound as often as the player presses the left mouse button (in theory):
shootSound.play();
}
Bounding box collision detection
Before we get to the main part of the game logic, we should discuss bounding box
collision detection. We need to detect collisions between the missiles the player fires
and the aliens the player is firing upon. To do this, we will create a function that tests to
see whether two objects are overlapping. For lack of a better name, we call this function
hitTest().
The type of hit test we are going to perform is called a bounding box collision test. This
means that we are going to ignore the intricate details of the bitmapped graphics and
simply test to see whether an invisible “box” drawn around the bounds of each object
overlaps with a similar box drawn around the other objects.
Recall that both the alien and missile dynamic objects were created with similar
properties: x, y, width, height. This was so that the hitTest() function could test them
as generic objects, unspecific to the type of on-screen object that they represent. This
means that we can add any other type of object to this game (boss alien, power-ups,
Case Study in Audio: Space Raiders Game | 423
VB.NET PDF - Convert PDF with VB.NET WPF PDF Viewer Data. Data: Auto Fill-in Field Data. Field: Insert PDF, VB.NET Word, VB.NET Excel, VB.NET part illustrates some conversion tabs and features for PDF exporting.
extract data from pdf to excel; how to extract data from pdf to excel
41
enemy missiles, and so on), and if it is created with similar properties, we can use the
same function to test collisions against it.
The function works by finding the top, left, bottom, and right values for each object and
then testing to see whether any of those values overlap. Bounding box collision detection
will be discussed in detail in Chapter 8, but we just wanted to give you a preview of what
it looks like for Space Raiders:
function hitTest(image1,image2) {
r1left = image1.x;
r1top = image1.y;
r1right = image1.x + image1.width;
r1bottom = image1.y + image1.height;
r2left = image2.x;
r2top = image2.y;
r2right = image2.x + image2.width;
r2bottom = image2.y + image2.height;
retval = false;
if ( (r1left > r2right) || (r1right < r2left) || (r1bottom < r2top) ||
(r1top > r2bottom) ) {
retval = false;
} else {
retval = true;
}
return retval;
}
Playing the game
Now the game is ready to play. STATE_PLAYING calls the drawScreen() function, which
is the heart of Space Raiders. The first part of this function simply moves the missiles
and aliens on the screen. Moving the missiles is quite easy. We loop through the array
(backward), updating the y property of each with the speed property. If they move off
the top of the screen, we remove them from the array. We move through the array
backward so that we can splice() array elements out of the array and not affect loop
length. If we did not do this, elements would be skipped after we splice() the array:
for (var i=missiles.length-1; i>= 0;i−−) {
missiles[i].y −= missiles[i].speed;
if (missiles[i].y < (0-missiles[i].height)) {
missiles.splice(i,1);
}
}
Drawing the aliens is similar to drawing missiles—with a few exceptions. Aliens move
left and right, and when they reach the side of the canvas, they move down 20 pixels
424 | Chapter 7: Working with Audio
45
and then reverse direction. To achieve the reversal in direction, multiply the speed
property by −1. If the aliens are moving to the right (speed = 2), this will make the
speed property equal to −2, which will subtract from the x position and move the aliens
to the left. If the aliens hit the left side of the canvas, the speed property will again be
multiplied by −1 (−2 * −1), which will equal 2. The alien will then move to the right
because 2 will be added to the x value for the alien each time drawScreen() is called:
//Move Aliens
for (var i=aliens.length−1; i>= 0;i−−) {
aliens[i].x += aliens[i].speed;
if (aliens[i].x > (theCanvas.width-aliens[i].width) ||
aliens[i].x < 0) {
aliens[i].speed *= −1;
aliens[i].y += 20;
}
if (aliens[i].y > theCanvas.height) {
aliens.splice(i,1);
}
}
The next step in drawScreen() is to detect collisions between the aliens and the missiles.
This part of the code loops through the missiles array backward while nesting a loop
through the aliens array. It will test every missile against every alien to determine
whether there is a collision. Because we have already covered the hitTest() function,
we need to discuss only what happens if a collision is detected. First, we call the play()
function of the explodeSound. This is the second critical line of code in this iteration of
Space Raiders, because it plays (or attempts to play) the explosion sound every time a
collision is detected. After that, it splices the alien and missile objects out of their
respective arrays and then breaks out of the nested for:next loop. If there are no aliens
left to shoot, we set the appState to STATE_RESET, which will add more aliens to the
canvas so that the player can continue shooting:
missile: for (var i=missiles.length−1; i>= 0;i−−) {
var tempMissile = missiles[i]
for (var j=aliens.length-1; j>= 0;j−−) {
var tempAlien =aliens[j];
if (hitTest(tempMissile,tempAlien)) {
explodeSound.play();
missiles.splice(i,1);
aliens.splice(j,1);
break missile;
}
}
if (aliens.length <=0) {
appState = STATE_RESET;
}
}
Case Study in Audio: Space Raiders Game | 425
30
The last few lines of code in drawScreen() loop through the missiles and aliens arrays
and draw them onto the canvas. This is done using the drawImage() method of the
context object and using the x and y properties we calculated earlier. Finally, it draws
the playerImage on the canvas, and the function is finished:
//Draw Missiles
for (var i=missiles.length−1; i>= 0;i−−) {
context.drawImage(missileImage,missiles[i].x,missiles[i].y);
}
//draw aliens
for (var i=aliens.length−1; i>= 0;i−−) {
context.drawImage(alienImage,aliens[i].x,aliens[i].y);
}
//Draw Player
context.drawImage(playerImage,player.x,player.y);
As we stated previously, Space Raiders is not a full game. We have implemented only
enough to get the player to shoot missiles so that we can play the shoot sound and to
detect collisions so that we can play the explode sound.
Iteration #1: Playing Sounds Using a Single Object
We just described the first iteration of the dynamic audio code. It works by attempting
to call the play() function of both shootSound and explodeSound as often as necessary.
This appears to work at first, but if you listen carefully (and this is apparent on some
browsers more than others), the sounds start to play “off,” or not play at all. This is
because we are using a single object and attempting to play and replay the same sound
over and over. A single HTMLAudioElement was not designed to operate this way. You
can test this example in the code distribution by running CH7EX6.html in your HTML5-
compliant web browser. Press the fire button as quickly as possible, and listen to when
and how the sounds play. After a bit, they start to play at the wrong time, don’t finish,
or don’t play at all. Figure 7-7 shows what the first iteration of Space Raiders looks like
in a web browser.
426 | Chapter 7: Working with Audio
18
Figure 7-7. Space Raiders playing sounds from two objects
Iteration #2: Creating Unlimited Dynamic Sound Objects
So, we almost got what we wanted with the first iteration, but we ran into some oddities
when calling the play() function on a single HTMLAudioElement multiple times before
the sound had finished playing.
For our second iteration, we are going to try something different. Let’s see what happens
when we simply create a new HTMLAudioElement object every time we want to play a
sound. If this doesn’t sound like an efficient use of memory or resources in the web
browser, you are a keen observer. It’s actually a horrible idea. However, let’s proceed just
to see what happens.
In canvasApp(), we will create a couple variables that represent the filenames of the
sounds we want to play, but without the associated extension. We will still retrieve the
extension with a call to supportedAudioFormat(), just as we did in the first iteration,
and store that value in the audioType variable.
We will also create an array named sounds that we will use to hold all the HTMLAudioEle
ment objects we create. This array will tell us how many objects we have created so that
we can visually see when all heck breaks loose:
Case Study in Audio: Space Raiders Game | 427
38
var SOUND_EXPLODE = "explode1";
var SOUND_SHOOT = "shoot1";
var sounds = new Array();
Instead of calling the play() function of each sound directly, we are going to create a
function named playSound(). This function accepts two parameters:
sound
One of the variables we created above that contains the name of the sound file
volume
A number between 0 and 1 that represents the volume of the sound to play
The function here creates a new sound object every time it is called by calling the
createElement() function of the document DOM object. It then sets the properties
(src, loop, volume) and attempts to play the sound. Just for fun, let’s push the object
into the sounds array:
function playSound(sound,volume) {
var tempSound = document.createElement("audio");
tempSound.setAttribute("src", sound + "." + audioType);
tempSound.loop = false;
tempSound.volume = volume;
tempSound.play();
sounds.push(tempSound);
}
To play the sounds, we call playSound(), passing the proper parameters.
The call in eventMouseUp() looks like this:
playSound(SOUND_SHOOT,.5);
And in drawScreen(), it looks like this:
playSound(SOUND_EXPLODE,.5);
To display on the canvas how many sounds we have created, we add this code to the
drawScreen() function:
context.fillStyle = "#FFFFFF";
context.fillText ("Active Sounds: " + sounds.length, 200 ,480);
Now, go ahead and try this example (CH7EX7.html in the code distribution). Figure 7-8
shows what Space Raiders iteration #2 looks like. Notice that we have added some display
text at the bottom of the screen to show how many sounds are in the sounds array. You
will discover two issues with this iteration:
1. The sounds play with almost no pauses when loaded from a local drive. But when
the page is loaded from a remote website, there is a defined pause before each sound
is loaded and played.
428 | Chapter 7: Working with Audio
Documents you may be interested
Documents you may be interested