Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
SFML Game Development.pdf
Скачиваний:
194
Добавлен:
28.03.2016
Размер:
4.19 Mб
Скачать

Chapter 9

If the application switches to one of these two states, the MusicPlayer::play() function will be called. This stops the current theme and results in a new theme being played.

When the user pauses the game, we would like the music to be paused as well. This can be handled in the PauseState constructor. We also define the PauseState destructor which resumes the music. As soon as the pause state is over, the music shall no longer pause.

PauseState::PauseState(StateStack& stack, Context context) : State(stack, context)

, ...

{

getContext().music->setPaused(true);

}

PauseState::~PauseState()

{

getContext().music->setPaused(false);

}

At this point, playing different music themes in our game is completely operational. There are many ways to extend the current functionality: Playlists that play a sequence of themes in order; or smooth theme transitions implemented by continuous adaption of the music volume. Some modern games play different

channels of a theme depending on the situation: In a passage where you have to pay attention, the music builds up tension; during an action scenario, the bass kicks in.

Sound effects

We have many gameplay events that can be represented by sounds: Fired machine guns, launched missiles, explosions, collection of pickups, and so on. Unlike music, sound effects are mostly very short. As a consequence, they can be loaded completely into memory, and we can also use the raw WAV format for these files without wasting too much memory. We are going to use the sf::SoundBuffer resource class to store the audio samples for our sound effects.

[ 221 ]

www.it-ebooks.info

Cranking Up the Bass – Music and Sound Effects

The following enumeration of sound effects is used in our game. We'll also create a typedef for the resource holder of sf::SoundBuffer.

namespace SoundEffect

{

enum ID

{

AlliedGunfire,

EnemyGunfire,

Explosion1,

Explosion2,

LaunchMissile,

CollectPickup,

Button,

};

}

typedef ResourceHolder<sf::SoundBuffer, SoundEffect::ID> SoundBufferHolder;

We implement a class for the sound effects, one similar to the MusicPlayer class:

class SoundPlayer : private sf::NonCopyable

{

public:

 

SoundPlayer();

void

play(SoundEffect::ID effect);

void

play(SoundEffect::ID effect,

 

sf::Vector2f position);

void

removeStoppedSounds();

void

setListenerPosition(sf::Vector2f position);

sf::Vector2f

getListenerPosition() const;

private:

 

SoundBufferHolder

mSoundBuffers;

std::list<sf::Sound>

mSounds;

};

 

The class contains a resource holder for the sound buffers and a list of currently active sound effects. Since more than one sound effect may be active at the same time, we need a container.

[ 222 ]

www.it-ebooks.info

Chapter 9

Loading, inserting, and playing

In the constructor, we load all of the sound effects. We do this by calling the SoundBufferHolder::load() function, in a similar fashion to the textures and fonts we loaded in earlier chapters. The first argument we pass is a SoundEffect::ID enumerator, the second is the filename:

SoundPlayer::SoundPlayer() : mSoundBuffers()

, mSounds()

{

mSoundBuffers.load(SoundEffect::AlliedGunfire,

"Media/Sound/AlliedGunfire.wav");

mSoundBuffers.load(SoundEffect::EnemyGunfire,

"Media/Sound/EnemyGunfire.wav");

...

}

How do we play a sound? First, we have to look up the correct sound buffer by

calling SoundBufferHolder::get(). We add a new sf::Sound instance that uses this sound buffer to the list of sounds. Then, we obtain a reference to the sound and call sf::Sound::play() to play the sound:

void SoundPlayer::play(SoundEffect::ID effect)

{

mSounds.push_back(sf::Sound(mSoundBuffers.get(effect))); mSounds.back().play();

}

Instead of the sf::Sound constructor, you can also use the setBuffer() method to initialize the sound buffer.

You might wonder why we took the std::list STL container. The problem with std::vector is that it may relocate existing sounds as we add new ones, thus invalidating them mid-play. Also, we cannot efficiently remove random elements from a std::vector container without changing the element order. It is also important that we first insert the sound and then play it. Otherwise, a copy would be inserted, and the local sound object would stop playing as soon as it left scope.

[ 223 ]

www.it-ebooks.info

Cranking Up the Bass – Music and Sound Effects

Removing sounds

As soon as a sound effect has finished playing, there is no point of keeping it in the list any longer. We therefore provide a removeStoppedSounds() method which removes all sounds that have stopped. As soon as sf::Sound finishes playing, it automatically switches to the Stopped state. The method is written in a simple way, thanks to the std::list::remove_if() method and lambda expressions:

void SoundPlayer::removeStoppedSounds()

{

mSounds.remove_if([] (const sf::Sound& s)

{

return s.getStatus() == sf::Sound::Stopped;

});

}

Use case – GUI sounds

Our SoundPlayer object is instantiated as a member of the Application class, similar to MusicPlayer. It is also added to the State::Context structure:

class State

{

public:

struct Context

{

...

MusicPlayer* music; SoundPlayer* sounds;

};

};

If a button is clicked, we would like to play an appropriate sound effect. We'll add a member SoundPlayer& mSounds to the Button class. We'll then adapt its constructor to take an entire Context object and initialize the reference to the

sound player:

Button::Button(State::Context context) : ...

, mSounds(*context.sounds)

{

...

}

[ 224 ]

www.it-ebooks.info

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]