The BreadAndCheese
game is now quite playable. It has a bit of variety and it can get
quite hectic, with the player having to keep an eye on lots of things at
the same time to stay alive and rack up a big score. However, there is
one thing missing from it, and that is sounds. At the moment, playing
the game is very much like watching the TV with the sound turned off.
Such is the value of sound
to a game that even the very first computer games had sound output,
even if it took the form of primitive beeps. You now need to think about
how sounds can be added to the game. You have seen how easy it is to
load and play sound effects; now you have to bind them into the game
sprites so that when something happens to each sprite, it plays an
appropriate sound effect. But before we can add sounds, we have to
decide how to do it and decide who makes the sounds in the game.
This is actually a profound question. Does the BreadAndCheeseGame
class make the sound, or do the sprites do it themselves? After some
thought, you probably come to the same conclusion that I did, which is
that the sound of a sprite is a bit like the texture that is used to
draw it; it is a property of the sprite. Furthermore, on the principle
of high cohesion being good (that is, it is best if an object can look
after itself and not rely on any other objects), it makes sense for the
sprite to make the sound. Sprites that need to make sounds can be given
the sound effects when they are constructed and play the appropriate
ones as required. You can make the sounds any way you like. I created
mine using a little electronic sound generator. Figure 1 shows the sounds after I had imported them. Note that the name of each sound file directly reflects its purpose.
Each of them is loaded
into the game as content, and the constructor of each sprite class is
modified to accept the sound effect resources when the sprite is
created:
CheeseBall = new BallSprite(
Content.Load<Texture2D>("Images/Cheese"),
0.07f, // a cheese takes 0.07 of the screen width
200, // cheese takes 200 ticks to cross the screen
minDisplayX, maxDisplayX, minDisplayY, maxDisplayY,
displayWidth / 4, // a quarter across the screen
displayHeight / 4, // a quarter down the screen
Content.Load<SoundEffect>("Sounds/BreadHit"),
Content.Load<SoundEffect>("Sounds/TomatoHit"),
Content.Load<SoundEffect>("Sounds/EdgeHit"),
Content.Load<SoundEffect>("Sounds/LoseLife"));
GameSprites.Add(Cheese);
This code creates a new CheeseBall
and passes it all the information it needs, including the sound
effects. The constructor of the class stores the sound effects so that
they can be used to produce the sounds as required:
public BallSprite(Texture2D inSpriteTexture,
float widthFactor, float ticksToCrossScreen,
float inMinDisplayX, float inMaxDisplayX,
float inMinDisplayY, float inMaxDisplayY,
float inInitialX, float inInitialY,
SoundEffect inBatHitSound,
SoundEffect inTargetHitSound,
SoundEffect inEdgeHitSound,
SoundEffect inLoseLifeSound)
: base(inSpriteTexture, widthFactor, ticksToCrossScreen,
inMinDisplayX, inMaxDisplayX,
inMinDisplayY, inMaxDisplayY,
inInitialX, inInitialY)
{
batHitSound = inBatHitSound;
targetHitSound = inTargetHitSound;
edgeHitSound = inEdgeHitSound;
loseLifeSound = inLoseLifeSound;
}
This code actually looks
rather horrible, and for that I apologize. The constructor for the
cheese actually does very little work because most of the heavy lifting
is done by its base constructor, which sets up the moving sprite. All
the constructor does is copy the incoming sound effects into members
inside the BallSprite class, so that they can be used in the Update
method to make the appropriate sounds. I think it is fair to say that
when you understand this lump of code, you properly understand
constructors and class hierarchies.
Here is part of the Update method in the BallSprite class that plays a sound when the cheese ball hits the top of the screen:
if (y <= minDisplayY)
{
// ball has hit the top of the screen.
edgeHitSound.Play();
ySpeed = Math.Abs(ySpeed);
Note:
The
game will run on a Zune but because the Zune has fewer sound channels
than the Xbox or Windows PC it may fail when the bat or the ball
collides with a large number of killer tangerines. This is because the
game will try to play a sound for each collision, and these are all
played on separate sound channels. To fix this problem you could make a
Zune version that does not play a new collision sound if the existing
one is already active. I will leave this for you to sort out.