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

Cranking Up the Bass – Music and Sound Effects

Now, we must make sure that non-spatialized sounds (such as a button click) are still played correctly. If we move the listener, all sounds will be affected, which is not always what we want. In our first play() overload, we make sure the sound is played directly in front of the listener, so it will have maximum volume. The getListenerPosition() function transforms the 3D listener position back to 2D graphics coordinates:

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

{

play(effect, getListenerPosition());

}

Use case – In-game sound effects

That was a lot of theory; let's get back to the interesting stuff. We want to embed sound effects in our game and play them whenever appropriate. Before we can do that, we need access to the sound player; we let GameState pass a SoundPlayer reference to our World class.

Inside the world, we would like to have a dedicated scene node for sounds, so that we can use our command system to play sounds. We'll add a SoundNode class, which is a simple adapter to the SoundPlayer class:

class SoundNode : public SceneNode

{

public:

 

 

explicit

SoundNode(SoundPlayer& player);

void

playSound(SoundEffect::ID sound,

 

 

sf::Vector2f position);

virtual unsigned int

getCategory() const;

private:

 

 

SoundPlayer&

 

mSounds;

};

The function definitions are not particularly interesting. The getCategory() method returns a new SoundEffect category, while playSound() forwards its arguments to the mSounds sound player. The sound node is inserted into the scene graph in

World::buildScene().

[ 230 ]

www.it-ebooks.info

Chapter 9

Anyway, this allows us to define a playLocalSound() method for entities, which sends a command to the sound node. As all of our current sound effects are related to airplanes, we define playLocalSound() in the Aircraft class; but it would also be possible to have it in the Entity or SceneNode base classes:

void Aircraft::playLocalSound(CommandQueue& commands, SoundEffect::ID effect)

{

Command command;

command.category = Category::SoundEffect; command.action = derivedAction<SoundNode>( std::bind(&SoundNode::playSound,

_1, effect, getWorldPosition()));

commands.push(command);

}

The std::bind() call might look more confusing than it actually is. It converts the SoundNode::playSound() function to a functor, using the following parameters:

_1: This is the first parameter of Command::action (namely SceneNode&), which is interpreted as the this pointer of SoundNode::playSound()

effect: This is the sound effect ID

getWorldPosition(): This is the position where the sound is played

Now we've written this method once, so we don't have to fiddle with commands every time we want to play a sound. Next, we implement the sound effects for launching a missile and firing the machine gun. In the case of the machine gun, we create different sound effects for the player and enemies. In total, we only have to add two playLocalSound() calls:

void Aircraft::checkProjectileLaunch(sf::Time dt, CommandQueue& commands)

{

...

if (mIsFiring && mFireCountdown <= sf::Time::Zero)

{

playLocalSound(commands, isAllied() ? SoundEffect::AlliedGunfire : SoundEffect::EnemyGunfire);

...

}

if (mIsLaunchingMissile)

{

playLocalSound(commands, SoundEffect::LaunchMissile);

...

}

}

[ 231 ]

www.it-ebooks.info

Cranking Up the Bass – Music and Sound Effects

For the explosions, the approach is very similar. In order to add variety, we randomly choose one of two possible sound effects. For the pickup collection, which is performed in World::handleCollisions(), we proceed in a slightly different manner. Since we are in the World class and not a scene node, we invoke playLocalSound() on the player's aircraft:

else if (matchesCategories(pair, Category::PlayerAircraft, Category::Pickup))

{

auto& player = static_cast<Aircraft&>(*pair.first); auto& pickup = static_cast<Pickup&>(*pair.second);

pickup.apply(player);

pickup.destroy();

player.playLocalSound(mCommandQueue, SoundEffect::CollectPickup);

}

Because we are in the World class, we could directly call SoundPlayer::play(); however, we still use SoundNode for symmetry reasons. If the implementation

of Aircraft::playLocalSound() or SoundNode::playSound() changes, the modifications will still be applied to all spatial sound effects.

The last thing we need to do is to update the listener and remove the sounds that have finished playing. We'll set the listener to the player's aircraft position, so that the player shares the audial position of its pilot. We'll add a function which is invoked in World::update():

void World::updateSounds()

{

mSounds.setListenerPosition( mPlayerAircraft->getWorldPosition());

mSounds.removeStoppedSounds();

}

[ 232 ]

www.it-ebooks.info

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