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

Every Pixel Counts – Adding Visual Effects
Embedding particles in the world
That was it for the definition of emitters and particle systems. We now add these nodes to the scene graph. First the emitter in the Projectile constructor; we set its position to the tail of the missile (which is half the height away from the center):
if (isGuided()) // if this projectile is a missile
{
std::unique_ptr<EmitterNode> smoke( new EmitterNode(Particle::Smoke));
smoke->setPosition(0.f, getBoundingRect().height / 2.f); attachChild(std::move(smoke));
}
The particle system is attached to a layer node directly under the scene graph's root. Because we want particles and projectiles to appear below the airplanes, we split the existing scene layer Air into two layers LowerAir and UpperAir. In the following, you see an excerpt of World::buildScene():
std::unique_ptr<ParticleNode> smokeNode(
new ParticleNode(Particle::Smoke, mTextures)); mSceneLayers[LowerAir]->attachChild(std::move(smokeNode));
So far we have only considered the smoke effect, but the propellant fire works in exactly the sameway. By adding the fire particle system after the smoke, it is
rendered later, so the burning propellant is still visible and not covered by smoke.
Animated sprites
Alright, now we have got particles, let's accompany them with an animated explosion. So far our aircraft have just disappeared when you shot them down. That's not satisfying. When you destroy something like an aircraft, you expect a huge explosion, don't you?
[ 200 ]
www.it-ebooks.info

Chapter 8
We already had a look at texture rectangles and sprite sheets. This knowledge will be used to build our animation. An animation consists of several frames, and we represent these frames as separate rectangles inside one larger texture, similar to what we now do with entities. Do not confuse animation frames with the game loop frames—the former represents just a state of an animation, which usually lasts for many game loop iterations.
This is the sprite sheet we use for the animation. How does it work? As time elapses, we move the texture rect from one frame to the other, until the animation is finished.
We do not define the animation class as a scene node but only as a drawable and a transformable object. It can then be used by whatever scene node we want, which gives a little more flexibility when and where we can use it.
class Animation : public sf::Drawable, public sf::Transformable
{
public:
...
private:
... |
|
|
|
sf::Sprite |
mSprite; |
||
sf::Vector2i |
mFrameSize; |
||
std::size_t |
mNumFrames; |
||
std::size_t |
mCurrentFrame; |
||
sf::Time |
mDuration; |
||
sf::Time |
mElapsedTime; |
||
bool |
mRepeat; |
||
}; |
|
|
|
|
|
|
|
|
|
[ 201 ] |
|
|
|
|
www.it-ebooks.info

Every Pixel Counts – Adding Visual Effects
We will have a look at the functions later. As you can see we use internally a sprite.
We could do without a sprite if we so desired, but it defines a lot of functions that we want to use, making our lives easier. After that comes the frame size, this vector defines the size of one frame for us. We have the values concerning frames, how many frames and what frame we are currently drawing. Lastly we have the time for the animation, the total duration, and the elapsed time since the last frame change.
So let us start looking at the actual implementation. Most of our logic is performed in the Animation::update() function, so it grows quite big. Because of that the function is explained in the following sections.
void Animation::update(sf::Time dt)
{
sf::Time timePerFrame = mDuration / static_cast<float>(mNumFrames);
mElapsedTime += dt;
sf::Vector2i textureBounds(mSprite.getTexture()->getSize()); sf::IntRect textureRect = mSprite.getTextureRect();
if (mCurrentFrame == 0)
textureRect = sf::IntRect(0, 0, mFrameSize.x, mFrameSize.y);
In the previous code, we already have the sprite sheet divided per frame and we know how big one frame is. We have to calculate how much time needs to elapse before we progress to the next frame. If we are on the first frame, then our texture rect should start at the beginning of the animation sprite sheet:
while (mElapsedTime >= timePerFrame && (mCurrentFrame <= mNumFrames || mRepeat))
{
So while time has elapsed since we last updated, and is enough to count as a new frame, and we haven't reached the end of the animation, we perform an iteration:
textureRect.left += textureRect.width;
if (textureRect.left + textureRect.width > textureBounds.x)
{
textureRect.left = 0;
textureRect.top += textureRect.height;
}
mElapsedTime -= timePerFrame; if (mRepeat)
[ 202 ]
www.it-ebooks.info

Chapter 8
{
mCurrentFrame = (mCurrentFrame + 1) % mNumFrames;
if (mCurrentFrame == 0)
textureRect = sf::IntRect(0, 0, mFrameSize.x,
mFrameSize.y);
}
else
{
mCurrentFrame++;
}
}
mSprite.setTextureRect(textureRect);
}
In each iteration, we move the resulting texture rect quite easily. We move it a step to the right, all the way until we reach the end, if that occurs we move the rect down one line and start again from the start. Lastly is just some book-keeping for what frame we are on, so we know if we have reached the end of the animation.
This is the core of the animation and covers everything we need. It's very much packed together in the update function, but it is all required.
So we add it now to the aircraft such that it explodes when destroyed. Let's start with the Aircraft constructor.
mExplosion.setFrameSize(sf::Vector2i(256, 256)); mExplosion.setNumFrames(16); mExplosion.setDuration(sf::seconds(1));
centerOrigin(mExplosion);
Here we set up our explosion. We define the size of one frame of the animation, the number of frames and the duration we want it to run for. We also center the animation sprite's origin, so that it is easier to position.
We will have to branch in our code to render either the normal aircraft or the explosion. It's not much but we still have to do it:
if (isDestroyed() && mShowExplosion) target.draw(mExplosion, states);
else
target.draw(mSprite, states);
[ 203 ]
www.it-ebooks.info