- •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
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