
- •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 2
That is already it. The whole method looks now as follows:
void TextureHolder::load(Textures::ID id, const std::string& filename)
{
std::unique_ptr<sf::Texture> texture(new sf::Texture()); if (!texture->loadFromFile(filename))
throw std::runtime_error("TextureHolder::load - Failed to load " + filename);
auto inserted = mTextureMap.insert(std::make_pair(id, std::move(texture)));
assert(inserted.second);
}
In our method get(), there are things that may go wrong too. The requested texture might not exist in the map. This is also a logic error; we are supposed to load the textures before we access them. Consequently, we verify whether the texture has been found, again using assert:
sf::Texture& TextureHolder::get(Textures::ID id)
{
auto found = mTextureMap.find(id); assert(found != mTextureMap.end());
return *found->second;
}
Generalizing the approach
We have implemented everything we need for textures, but we would like to handle other resources such as fonts and sound buffers too. As the implementation looks extremely similar for them, it would be a bad idea to write new classes FontHolder and SoundBufferHolder with exactly the same functionality. Instead, we write a class template, which we instantiate for different resource classes.
We call our template ResourceHolder and equip it with two template parameters:
•Resource: The type of resource, for example, sf::Texture. We design the class template to work the SFML classes, but if you have your own resource class which conforms to the required interface (providing loadFromFile() methods), nothing keeps you from using it together with ResourceHolder.
•Identifier: The ID type for resource access, for example, Textures::ID. This will usually be an enum, but the type is not restricted to enumerations. Any type that supports an operator< can be used as identifier, for example, std::string.
[ 43 ]
www.it-ebooks.info

Keeping Track of Your Textures – Resource Management
The transition from TextureHolder to ResourceHolder<Resource, Identifier> is straightforward. We replace the used types with the generic
template parameters: sf::Texture becomes Resource, and Textures::ID becomes
Identifier. Furthermore, we rename some variables to reflect the fact that we are talking about resources in general, not only textures. We also adapt the member functions accordingly.
One thing we have to note when using templates is that the complete implementation needs to be in the header. We cannot use .cpp
files for the method definitions anymore, but we would still like to separate interface and implementation. That is why we use a file ResourceHolder.hpp for the class definition, and a file
ResourceHolder.inl for the method definitions. At the end of the
.hpp file, we include the .inl file containing the implementation.
.inl is a common file extension for inline template implementations.
The generalized class definition has the following interface:
template <typename Resource, typename Identifier> class ResourceHolder
{
public: |
|
void |
load(Identifier id, |
|
const std::string& filename); |
Resource& |
get(Identifier id); |
const Resource& |
get(Identifier id) const; |
private:
std::map<Identifier,
std::unique_ptr<Resource>> mResourceMap;
};
The load() method can be written in the following way. Like before, we attempt to load the resource from a file, and then we insert the unique pointer into the map and make sure the insertion was successful:
template <typename Resource, typename Identifier>
void ResourceHolder<Resource, Identifier>::load(Identifier id, const std::string& filename)
{
std::unique_ptr<Resource> resource(new Resource()); if (!resource->loadFromFile(filename))
throw std::runtime_error("ResourceHolder::load - Failed to load " + filename);
[ 44 ]
www.it-ebooks.info

Chapter 2
auto inserted = mResourceMap.insert( std::make_pair(id, std::move(resource)));
assert(inserted.second);
}
The two overloaded get() member functions are generalized using the same principle, that is why they are not listed here. You can refer to the online code base for a complete implementation.
Compatibility with sf::Music
As initially mentioned, the class sf::Music has semantics that are very different from other resource types. This begins already with its openFromFile() method that is not compatible to the loadFromFile() call in our implementation. Because of its streaming nature, a music object cannot be shared between multiple clients—each object represents one point in time of a certain music theme. No object contains the whole music data at once.
Instead of forcing sf::Music into a concept it does not fit, we decide to not store it inside ResourceHolder instances. This does not imply that there will be no encapsulation for music; we are rather going to cover music handling in-depth during Chapter 9, Cranking Up the Bass – Music and Sound Effects.
A special case – sf::Shader
There is one resource type we have yet to cover: shaders. Since a SFML shader object can consist of a fragment and/or a vertex shader, the interface of sf::Shader deviates slightly from the other resource classes in SFML. sf::Shader provides two methods to load from a file:
bool loadFromFile(const std::string& filename, sf::Shader::Type type); bool loadFromFile(const std::string& vertexShaderFilename,
const std::string& fragmentShaderFilename);
The first function loads either a fragment or a vertex shader (which one is specified by the second parameter). The second function loads both a vertex and a fragment shader.
This interface is an issue for our generic implementation, because ResourceHolder::load() contains the following expression:
resource->loadFromFile(filename)
[ 45 ]
www.it-ebooks.info

Keeping Track of Your Textures – Resource Management
Which assumes that loadFromFile() is invoked with one argument. For sf::Shader, we have to specify two instead.
The solution is simple: we write an overloaded ResourceHolder::load() function that takes an additional parameter and forwards it directly as the second argument to sf::Shader::loadFromFile(). This parameter can have the type sf::Shader::Type or const std::string&. In order to cope with both types,
we add a function template parameter. Our new load() function has the following declaration:
template <typename Parameter>
void load(Identifier id, const std::string& filename, const Parameter& secondParam);
The function definition is listed in the following code. Do not confuse yourself by the two template parameter lists; the first one is required for the class template ResourceHolder and the second one for the function template
ResourceHolder::load(). In the function body, only the loadFromFile() call is different from before.
template <typename Resource, typename Identifier> template <typename Parameter>
void ResourceHolder<Resource, Identifier>::load(Identifier id, const std::string& filename, const Parameter& secondParam)
{
std::unique_ptr<Resource> resource(new Resource()); if (!resource->loadFromFile(filename, secondParam))
throw ...;
... // insertion like before
}
A nice side effect of this additional overload is that it enables other argument combinations for loadFromFile() too. The method of sf::Texture actually looks as shown in the following line of code:
bool loadFromFile(const std::string& filename, const IntRect& area = IntRect())
The default parameter can be used if we want the texture to load only a rectangular area of the image. Usually, we do not specify it and load the whole file, but thanks to our second load() overload, we have now the possibility to use this parameter.
In our complete implementation, we have written a separate function that inserts resources into the map, in order to reduce code duplication.
[ 46 ]
www.it-ebooks.info