Game Programming for Teens
[ LiB ] |
In the introduction, I said that we will learn about both sound and music in this chapter, and you might be thinking that they are the same. Nope! Blitz Basic refers to sounds and music as two different entities. Unlike music, sound is played dynamically. What does this mean? The game does not play sound files over and over. Instead, the sound is only played at specific times. Take, for example, a gun. You don't want a gunshot to ring over and over; you want it to make a sound only when the gun actually shoots.
For this book, we will be using the .WAV file format for our sound files. What is the .WAV file format, you may ask? It is a file format that represents a sound on the computer. This sound format does not have any quality loss like some other file formats (which we will discuss later), so the sound from a .WAV file is "cleaner." I have included a number of royalty-free sound files on the CD, most in .WAV format, that you can use in your programs. By the way, WAV stands from "Windows Audio Volume."
Anyway, let's get started. First off, we need to know how to load sounds.
Loading Sounds
Remember images? It was extremely easy to load them, right? We just used the LoadImage() function. Well, Blitz Basic makes loading sounds just as easy: we use the LoadSound() function. LoadSound() is declared as follows :
LoadSound (filename$)
You load sounds just like you load images.
soundfile = LoadSound ("soundfile.wav")
Change soundfile and soundfile.wav to the name of the variable and file of the sound you want to load into your program.
NOTE
From this point on, I will use a cer tain style for my sound files. Whenever I load them, I will call the variable that holds the file xxxxxsound.wav , where xxxxx describes the sound. For example, to load the sound of a laser, I would call the sound lasersound , and I would load it as follows: lasersound = LoadSound (laser.wav) .
By the way, there is something else you may want to know. The name of the variable that contains the sound (in the previous example, soundfile ) is called a handle . Why is it called a handle? Well, basically, you use the handle variable as an address or reference to the object. So a "handle" helps you refer to somethingin this case a sound file. When you want to manipulate a sound file, you need a handle to access it; it's like a key to a lock.
Okay, I hope you understand loading sounds thus far. Before I move on, I want to detail one little function. This function is FreeSound . FreeSound deletes a sound from the memory. After freeing the sound, you can use LoadSound() and load another sound file with the same handle name. Following is the function declaration for FreeSound .
FreeSound sound_variable
You might use this function in a game with numerous levels, perhaps because you use different sounds from one level to the next . By freeing the sound, you are free to load another sound with the same handle that can then be used in place of the old sound.
Let's take one more example of when you might use FreeSound . Say you were writing a game that uses a handgun. Maybe you named the sound that is played when the player's gun is fired playergunsound . Now, imagine that the player receives a silencer that can be placed on the gun. The gun with the silencer, of course, sounds much different than the gun without the silencer. The easiest way to do this would be to delete the old non-silenced sound from memory, and load the silenced sound into the same handle name. That way, the game will continue to play the sound that is loaded in playergunsound , even though the sound file has changed. Take a look at Figure 11.1. In the first frame, playergunsound contains gunshot.wav. This sound file is just the sound of a regular gunshot. In frame two, FreeSound has been called and playergunsound doesn't contain anything. In the third and final frame, LoadSound() was called again and playergunsound now contains silencer.wav.
Figure 11.1. Playergunsound .
Let's try this in code. Your program may look something like this.
;Load the beginning sound for regular gunshot Global playergunsound = LoadSound ("gunshot.wav") ;Begin MAIN LOOP While Not KeyDown(1) If GunshotOccurred() PlayGunshotSound() Endif If SilencerAttached() SwitchSoundFiles() Endif Wend ;END OF MAIN LOOP ;Function SwitchSoundFiles() switches regular gunshot sound file with silenced gunshot sound Function SwitchSoundFiles() FreeSound playergunsound playergunsound = loadsound("silencer.wav") End Function
This code sample, of course, will not work if you copy it straight into Blitz Basic. Most of the functions are user -created functions that we haven't made, but you can probably figure out what they do. GunShotOccurred() would return True if a gunshot occurred; PlayGunshotSound() would play the playergunsound file; SilencerAttached() returns True if a silencer is attached to the gun; and SwitchSoundFiles() (which is also user-defined) deletes the old gun sound and attaches the new, silenced gun sound.
With this technique, you can change sound files without disturbing the main section of your game.
Alright, I hope you understand this. Let's move on to the next section, which teaches you how to actually play the sounds.
Listen CloselyPlaying Sounds
You've gotten this far into the chapter, and you probably want to learn how to actually listen to those beautiful sounds that you just learned to load. Blitz Basic provides us with an easy way to play sounds.
This function is called PlaySound . (Predictable, huh?) It is declared as follows. PlaySound sound_variable
Take a wild guess what sound_variable is. Yep, you got it: sound_variable is the handle of the sound file you loaded using LoadSound() . In other words, you can load a sound clip like this:
explosionsound = LoadSound("explosion.wav")
Then you would play it like this:
PlaySound explosionsound
Okay, let's use this function in a sample program. Let's begin with a scrolling background. (Why? Because it is easy and always fun to do.) Then we can add the nice-looking spaceship we use way too often. Then (and listen closely), because this is the fun part, we allow the player to fire a bullet with the spacebar. The firing of a bullet creates a bullet-firing sound.
Let's also add an enemy spaceship to the mix. This enemy is like me: he moves in a random fashion, is never predictable, and has no self-defense capabilities whatsoever. Anyway, when the bullet hits this crazy mini-me, an explosion sound is played, and the enemy is destroyed . His ship is then reset.
Hey, I thought I might make this program a little weird, so I used a picture of myself for the enemy ship. Well, just watch.
Anyway, Table 11.1 explains the keys for the program.
Table 11.1. Demo11-01.bb's Keys
Key | Action |
---|---|
Esc | Exits the game |
Up arrow | Moves ship up |
Down arrow | Moves ship down |
Right arrow | Moves ship right |
Left arrow | Moves ship left |
Spacebar | Fires bullet |
I was going to show the entire source for the program, but I realized it takes up about five pages. I am in the mood to save some trees, so I will just display the important parts . The first code block I will show you is taken from the initialization section of demo11-01.bb.
;SOUNDS ;Load the sound that is played when the player fires a bullet Global bulletsound = LoadSound("zing.wav") ;Load the sound that is played when the player destroys the enemy Global explosionsound = LoadSound ("explode.wav")
I bet you can guess what this does! This code loads both of the sounds that will be used in the program.
Let's move on to using those loaded sounds. The following source is the UpdateBullets() function.
;Function UpdateBullets() - Moves and tests each bullet for collision Function UpdateBullets() ;Loop through every bullet For bullets.bullet = Each bullet ;Update the bullet's position by moving 5 pixels up bullets\y = bullets\y - 5 ;Draw the bullet at its proper coordinates DrawImage bullets\image, bullets\x, bullets\y ;If the bullet hit the enemy, play the explosion and reset the level If ImagesOverlap(enemy\image,enemy\x,enemy\y, bullets\image,bullets\x,bullets\y) PlaySound explosionsound ;Play the explosion Cls Text 260,300, "You destroyed the enemy! How could you?" Flip Delay 4000 ResetLevel() ;Reset all variables of the level Return ;Go back to main loop EndIf ;If the bullet goes off screen, delete it If bullets\y < 0 Delete bullets EndIf Next ;Move on to next bullet End Function
As you probably guessed, this function updates all of the bullets onscreen. It begins by moving each bullet up five pixels, and then draws the bullet. The bullet then tests for a collision.
The collision test uses the function ImagesOverlap() . As you might remember,
ImagesOverlap() tests two images, here enemy\x and bullets\x , to see if they have
overlapped one another. If they have, the explosion sound is played using the command PlaySound.
NOTE
CAUTION
You might be wondering why I used the Return command to go back to the main loop instead of just letting the function finish going through its instructions. Here is the reason why: Within the function ResetLevel() , all of the bullets are deleted. This includes the bullet that was just being processed . Because the bullet no longer exists, how can we per form the actions of the next line, which tests to see if the bullet has gone offscreen ? There is no way, so in order to fix this situation, we just return to the main loop and start from scratch.
PlaySound explosionsound ;Play the explosion
The rest of the function clears the screen and displays some text. It then resets the level, and, using the Return command, returns back to the main loop.
The last part of the function just tests to see if the bullet is offscreen. If it is, the bullet is deleted.
The PlaySound function is used once more in the program. The following block is ripped from the TestKeys() function.
;Create a new bullet if spacebar is hit If KeyHit(SPACEBAR) bullets.bullet = New bullet ;Create the bullet bullets\x = player\x ;Assign bul let to player's x bullets\y = player\y ;Assign bullet to player's y bullets\image = LoadImage("bullet.bmp") ;Load the bullet image ;Play the bullet sound PlaySound bulletsound EndIf
What does this do? Well, it begins by testing to see if the player has hit the spacebar. If he has, the program then creates a new bullet. The program then assigns the bullet's starting coordinates to the player's starting coordinates. The bullet's image is then loaded.
The block ends by playing bulletsound . This sound is created every time a new bullet is created.
That's it for this crazy program. Figure 11.2 shows the program in its full glory . Wow, I look so bad in that photo. (You try finding a good picture of me in the last few years !)
Figure 11.2. The demo11-01.bb program.
Okay, so we now know how to play a sound. We're not done yet, folks! Blitz Basic provides some really cool tools that make sounds a lot more fun to use.
Blitz Basic gives us three functions: SoundPitch , SoundVolume , and SoundPan . These three functions can be used in conjunction with each of your sound files to produce some absolutely sweet effects. Let's go over each of them in order, beginning with SoundPitch .
SoundPitch: Am I a Devil or a Chipmunk?
What is pitch? In essence, the pitch of a sound determines how high or low the frequency of the sound is. Take the guy at your school who has been held back six or seven years. He probably has a very deep voice. This guy's voice has a very low pitch. Now take that other kid who seems like he is still six years old. That kid has the voice of a chipmunk! His voice has a very high pitch.
Pitch is measured on a scale called hertz. The scale of hertz goes from 0 to infinity, but for humans , 44,000Hz is about the absolute max you could ever hear. (Actually, 22,000Hz is the max, but in computers, we have to use two times the max to be able to sample the sound properly.) That's a large scale, huh? The lower the number, the deeper the sound. Usually, the sound is slower as the pitch becomes lower, and the sound is faster as the pitch increases .
How might you use this in a program? Well, using hertz values in Blitz Basic is a lot different than using hertz values in real life. The values that I give are attributed to sounds in Blitz Basic, and not to sounds in reality.
Think of someone with a really deep, gruff voice. They would have an average hertz value of around 8,000Hz. Now take the should-be-in-3 rd -grade-how-is-he-in-my-class boy with the high-pitched voice. He probably has an average pitch of 44,000Hz.
When you load a sound into your programs, you don't know what the hertz value is. To change the hertz value to a value of your choice, you use the SoundPitch function. SoundPitch is declared like this:
SoundPitch sound_variable, hertz
Okay, let's write a program. This program allows you to create an explosion. If you press the spacebar, the explosion sound is played, and by pressing up or down, you change the hertz value by 1,000. We will also set the hertz of the variable to 22,000 to begin with (so we have a starting point for the tests).
Following is the source from demo11-02.bb:
;demo11-02.bb - Demonstrates SoundPitch Graphics 800,600 ;Make sure backbuffer is drawn on and automidhandle is true SetBuffer BackBuffer() AutoMidHandle True ;IMAGES ;load the player's ship image playerimage = LoadImage ("spaceship.bmp") ;SOUNDS ;Load the bullet sound explosionsound = LoadSound ("explode.wav") ;CONSTANTS ;The following constants are used for key codes Const ESCKEY = 1,SPACEBAR = 57, UPKEY = 200, DOWNKEY = 208 ;create hertz variable hertz = 22000 ;Make sure bullet has hertz value of hertz variable to begin with SoundPitch explosionsound, hertz ;MAIN LOOP While Not KeyDown(1) ;Clear the screen Cls ;Make sure text is drawn in top left hand corner Locate 0,0 Print "Current Hertz Value: " + hertz ;Play explosion sound if player hits spacebar If KeyHit(SPACEBAR) PlaySound explosionsound EndIf ;If up is hit, increment hertz variable If KeyHit (UPKEY) hertz = hertz + 1000 EndIf ;If down is hit, decrement hertz variable If KeyHit(DOWNKEY) hertz = hertz - 1000 EndIf ;Make the explosion have the same pitch as the hertz variable SoundPitch explosionsound, hertz ;Draw the player DrawImage playerimage, MouseX(), MouseY() Flip Wend ;END OF MAIN LOOP
Cool, huh? The program lets the player change the hertz by pressing up or down, and it also synchronizes the explosion sound with the hertz value by using the SoundPitch function.
SoundPitch explosionsound, hertz
Alright, that's it for pitch. Let's learn how to use SoundVolume now.
SoundVolume
I bet you can guess what SoundVolume does. Simply put, changing the volume of a sound adjusts how loud or quiet the sound is.
SoundVolume is used a lot like SoundPitch. Here is the declaration:
SoundVolume sound_variable, volume#
sound_variable is the handle to the sound that you want to change the volume of. Volume# is a floating-point variable between 0 and 1.000. The closer to 1 volume# is, the louder thesound is. Cool? Let's make a program. Demo11-03.bb draws a randomly moving spaceship,and plays a laser sound every time you hit the spacebar. The farther away the enemy ship isfrom your ship, the quieter the sound. If the ship is really close, the sound is played loudly.
This program is a long one, so I am just going to show two parts of it. The first is ripped from the user-defined function FindCurrentVolume() .
;Function FindCorrectVolume - Sets volume# to the correct value depending on distance from player to enemy Function FindCorrectVolume() ;Find distance between player and enemy dist = Distance(player\x,player\y,enemy\x,enemy\y) ;Assign the volume number to volume# depending on how far the distance is. The farther the distance, the quieter the sound If dist < 100 volume# = 1.000 ElseIf dist < 200 volume# = .700 ElseIf dist < 300 volume# = .400 ElseIf dist < 400 volume# = .1000 Else volume# = 0.000 EndIf
The first thing this function does is find the distance between the enemy and the player using the Distance() function we wrote in Chapter 9, "Collision Detection." (I copied the source from the Distance() function into this program.) It then assigns volume# to a number depending on how high dist is. If dist is higher, then the spaceship is further away; therefore, the sound should be quieter. The block of If ElseIf Else statements determines how loud the volume should be.
The next and last part of demo11-03.bb actually uses the SoundVolume function. ;Create a new bullet if spacebar is hit If KeyHit(SPACEBAR) ;Find what volume# should be FindCorrectVolume() ;Assign bulletsound to volume# SoundVolume bulletsound, volume# ;Play the bullet PlaySound bulletsound EndIf
This code is executed when the spacebar is hit. It calls FindCorrectVolume() , which assigns volume# to its correct value. The code then adjusts the volume of bulletsound depending on the volume# variable. The block finally plays the sound clip of the bullet.
Figure 11.3 is a screenshot taken from demo11-03.bb.
Figure 11.3. The demo11-03.bb program.
Alright, that's it for SoundVolume . We only have one more function to learn about before we move on to playing music!
SoundPan
Good things always come in threes, huh? There are three major video game consoles, three months of summer, and three Pepsis on my desk that keep me up late. Well, there are also three sound-editing functions. We have already discussed two of them ( SoundPitch() and
SoundVolume() ). SoundPan is the third and last one. SoundPan offers a very cool effect: it allows you to create the illusion of moving sound by letting you pick which speaker the sound plays out of. You can have the program play sound out of the left speaker, the right speaker, or both. This allows you to make the player feel like the sounds are actually moving around him.
SoundPan is declared like this:
SoundPan sound_variable, pan#
Now this is the cool part: since pan# is a floating-point variable, you can have the sound panned a little to the left but still playing slightly on the right. What do I mean? Well, if you set pan# to -0.75, the sound would play 75 percent out of the left speaker, and 25 percent out of the right speaker.
Sound_variable , as you probably know, is the handle to the sound you want to edit. Pan# contains the amount you want to pan the sound. Pan# can be between -1.000 and 1.000if the number is negative, it will play predominantly out of the left speaker, and if it is positive, it will play out of the right speaker.
Cool? Let's write a program. This thing is going to be easy: an enemy ship moves left and right. When the player hits the spacebar, the sound is played. If the enemy is to the left of the player, the sound is played completely out of the left speaker. If the enemy is to the right of the player, the sound is played out of the right speaker. If the enemy is directly in front of the player, the sound is played out of both speakers . The following is the main loop that is taken from demo11-04.bb.
;MAIN LOOP While Not KeyDown(ESCKEY) ;Tile the background TileBlock backgroundimage, 0, scrolly ;increment the scrolling variable scrolly = scrolly + 1 If scrolly > ImageHeight(backgroundimage) scrolly = 0 EndIf ;Print all text at top-left corner Locate 0,0 Print "Panning variable: " + pan# ;set up player coordinates player\x = MouseX() player\y = MouseY() ;if enemy is to the left of player, make sound come out of left speaker If enemy\x < player\x pan# = -1.000 ;If enemy is to right of player, make sound come out of right speaker ElseIf enemy\x > player\x pan# = 1.000 ;If enemy is in front of player Else pan# = 0 EndIf ;Pan the sound SoundPan bulletsound, pan# ;If player presses spacebar, play the sound If KeyHit (SPACEBAR) PlaySound bulletsound EndIf ;Move the enemy according to his velocity enemy\x = enemy\x + enemy\xv ;If the enemy goes offscreen, reflect his velocity If enemy\x < 0 Or enemy\x > 800 enemy\xv = - enemy\xv EndIf ;Draw the player and the enemy DrawImage player\image,player\x,player\y DrawImage enemy\image,enemy\x,enemy\y Flip Wend
Not that bad, eh? The main part is finding what the pan# variable should be. Pan# is used as a parameter for SoundPan , and it determines how far to the left or right the sound should pan. To find what pan# should be, we use the following code block. ;if enemy is to the left of player, make sound come out of left speaker
If enemy\x < player\x pan# = -1.000 ;If enemy is to right of player, make sound come out of right speaker ElseIf enemy\x > player\x pan# = 1.000 ;If enemy is in front of player Else pan# = 0 EndIf
This code sets pan# to -1 (left speaker) if the enemy is to the left of the player, 1 if the player is to the right, and 0 if he is directly in front of the player. The last part of the main loop I want to show you actually uses the SoundPan function.
;Pan the sound SoundPan bulletsound, pan#
Pretty cool, huh? This function synchronizes the bullet sound with the pan# variable.
Figure 11.4 is a screenshot taken from the program. By the way, this program does not work on everyone's speakers, so if it doesn't seem to pan correctly, it might just be your speaker hardware.
Figure 11.4. The demo11-04.bb program.
Whew! That's it for sounds! Now we get to move on to using music in our games .
[ LiB ] |