
- •Credits
- •Foreword
- •About the Authors
- •About the Reviewers
- •www.PacktPub.com
- •Table of Contents
- •Preface
- •Introducing SFML
- •Downloading and installation
- •A minimal example
- •A few notes on C++
- •Developing the first game
- •The Game class
- •Game loops and frames
- •Input over several frames
- •Vector algebra
- •Frame-independent movement
- •Fixed time steps
- •Other techniques related to frame rates
- •Displaying sprites on the screen
- •File paths and working directories
- •Real-time rendering
- •Adapting the code
- •Summary
- •Defining resources
- •Resources in SFML
- •Textures
- •Images
- •Fonts
- •Shaders
- •Sound buffers
- •Music
- •A typical use case
- •Graphics
- •Audio
- •Acquiring, releasing, and accessing resources
- •An automated approach
- •Finding an appropriate container
- •Loading from files
- •Accessing the textures
- •Error handling
- •Boolean return values
- •Throwing exceptions
- •Assertions
- •Generalizing the approach
- •Compatibility with sf::Music
- •A special case – sf::Shader
- •Summary
- •Entities
- •Aircraft
- •Alternative entity designs
- •Rendering the scene
- •Relative coordinates
- •SFML and transforms
- •Scene graphs
- •Scene nodes
- •Node insertion and removal
- •Making scene nodes drawable
- •Drawing entities
- •Connecting entities with resources
- •Aligning the origin
- •Scene layers
- •Updating the scene
- •One step back – absolute transforms
- •The view
- •Viewport
- •View optimizations
- •Resolution and aspect ratio
- •View scrolling
- •Zoom and rotation
- •Landscape rendering
- •SpriteNode
- •Landscape texture
- •Texture repeating
- •Composing our world
- •World initialization
- •Loading the textures
- •Building the scene
- •Update and draw
- •Integrating the Game class
- •Summary
- •Polling events
- •Window events
- •Joystick events
- •Keyboard events
- •Mouse events
- •Getting the input state in real time
- •Events and real-time input – when to use which
- •Delta movement from the mouse
- •Playing nice with your application neighborhood
- •A command-based communication system
- •Introducing commands
- •Receiver categories
- •Command execution
- •Command queues
- •Handling player input
- •Commands in a nutshell
- •Implementing the game logic
- •A general-purpose communication mechanism
- •Customizing key bindings
- •Why a player is not an entity
- •Summary
- •Defining a state
- •The state stack
- •Adding states to StateStack
- •Handling updates, input, and drawing
- •Input
- •Update
- •Draw
- •Delayed pop/push operations
- •The state context
- •Integrating the stack in the Application class
- •Navigating between states
- •Creating the game state
- •The title screen
- •Main menu
- •Pausing the game
- •The loading screen – sample
- •Progress bar
- •ParallelTask
- •Thread
- •Concurrency
- •Task implementation
- •Summary
- •The GUI hierarchy, the Java way
- •Updating the menu
- •The promised key bindings
- •Summary
- •Equipping the entities
- •Introducing hitpoints
- •Storing entity attributes in data tables
- •Displaying text
- •Creating enemies
- •Movement patterns
- •Spawning enemies
- •Adding projectiles
- •Firing bullets and missiles
- •Homing missiles
- •Picking up some goodies
- •Collision detection and response
- •Finding the collision pairs
- •Reacting to collisions
- •An outlook on optimizations
- •An interacting world
- •Cleaning everything up
- •Out of view, out of the world
- •The final update
- •Victory and defeat
- •Summary
- •Defining texture atlases
- •Adapting the game code
- •Low-level rendering
- •OpenGL and graphics cards
- •Understanding render targets
- •Texture mapping
- •Vertex arrays
- •Particle systems
- •Particles and particle types
- •Particle nodes
- •Emitter nodes
- •Affectors
- •Embedding particles in the world
- •Animated sprites
- •The Eagle has rolled!
- •Post effects and shaders
- •Fullscreen post effects
- •Shaders
- •The bloom effect
- •Summary
- •Music themes
- •Loading and playing
- •Use case – In-game themes
- •Sound effects
- •Loading, inserting, and playing
- •Removing sounds
- •Use case – GUI sounds
- •Sounds in 3D space
- •The listener
- •Attenuation factor and minimum distance
- •Positioning the listener
- •Playing spatial sounds
- •Use case – In-game sound effects
- •Summary
- •Playing multiplayer games
- •Interacting with sockets
- •Socket selectors
- •Custom protocols
- •Data transport
- •Network architectures
- •Peer-to-peer
- •Client-server architecture
- •Authoritative servers
- •Creating the structure for multiplayer
- •Working with the Server
- •Server thread
- •Server loop
- •Peers and aircraft
- •Hot Seat
- •Accepting new clients
- •Handling disconnections
- •Incoming packets
- •Studying our protocol
- •Understanding the ticks and updates
- •Synchronization issues
- •Taking a peek in the other end – the client
- •Client packets
- •Transmitting game actions via network nodes
- •The new pause state
- •Settings
- •The new Player class
- •Latency
- •Latency versus bandwidth
- •View scrolling compensation
- •Aircraft interpolation
- •Cheating prevention
- •Summary
- •Index

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