
- •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 4
Customizing key bindings
A big part with input management in a game is allowing the user to customize how he interacts with it, like the keys. Most of the time, you can find the most popular key bindings and see them written directly in the code. But there will always be people that want to do stuff their way.
We have to provide tools in order to dynamically bind the keys to specific actions.
With the command queue introduced in the previous sections, this becomes a much easier task to accomplish.
With the command queue, we already define specific actions for a specific key. The difference is that right now, we have it hardcoded as follows:
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) commands.push(moveLeft);
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) commands.push(moveRight);
The problem with this code is that it is very inflexible. We have to change a lot in order to allow any key to respond to any action. We have two clearly separate sets of data that we want to link together. We have the actual action that we want to perform, such as moving left or right. Then, we have the input key, the key that is to trigger the action.
Now the keys in sf::Keyboard are just part of an enum. So on top of this, we create an enum in our Player class that represents the different kinds of actions we associate with the pressed keys:
class Player
{
public:
enum Action
{
MoveLeft,
MoveRight,
...
}; |
|
void |
assignKey(Action action, |
|
sf::Keyboard::Key key); |
sf::Keyboard::Key |
getAssignedKey(Action action) const; |
... |
|
[ 109 ]
www.it-ebooks.info

Command and Control – Input Handling
private: |
|
static bool |
isRealtimeAction(Action action); |
private:
std::map<sf::Keyboard::Key, Action> mKeyBinding;
std::map<Action, Command> |
mActionBinding; |
}; |
|
Here, we have divided the input into two abstractions. We have the key binding to a specific action, and we have the binding of an action to a specific command. This is all we need to remove any hardcoded segments of input.
Next step is actually using this to translate an input to a command, which will essentially be our key bindings. We do this best by iterating through our key bindings map and just performing a simple check if the key is being pressed. If it is, we tell the command queue to insert our command we provide it with. The function
Player::isRealtimeAction() returns if the specified action is triggered by the real-time input (as opposed to events).
void Player::handleRealtimeInput(CommandQueue& commands)
{
FOREACH(auto pair, mKeyBinding)
{
if (sf::Keyboard::isKeyPressed(pair.first) && isRealtimeAction(pair.second))
commands.push(mActionBinding[pair.second]);
}
}
Just remember that this function has to be called once every frame, otherwise nothing will happen.
The two functions assignKey() and getAssignedKey() set and get the key mapped to a specific action. Their implementations perform map operations as expected, the only notable thing is that assignKey() checks that no two keys map to the same action.
The command stored under an action in mActionBinding knows exactly what it is supposed to do, so you don't have to provide any extra code to handle the key
press, since it has been abstracted out into its own entity. You have already done this previously in the chapter. The difference now is that we store the commands in the map together instead of unique variables.
[ 110 ]
www.it-ebooks.info

Chapter 4
The following is the constructor, where we set the default settings that you expect and prepare everything to work:
Player::Player()
{
mKeyBinding[sf::Keyboard::Left] = MoveLeft; mKeyBinding[sf::Keyboard::Right] = MoveRight;
mActionBinding[MoveLeft].action = [] (SceneNode& node, sf::Time dt)
{
node.move(-playerSpeed * dt.asSeconds(), 0.f);
};
mActionBinding[MoveRight].action = [] (SceneNode& node, sf::Time dt)
{
node.move(playerSpeed * dt.asSeconds(), 0.f);
};
FOREACH(auto& pair, mActionBinding) pair.second.category = Category::PlayerAircraft;
}
We assign the actions to the keys and achieve our initial key binding. After that we create the commands and implement the lambda function to be executed. The last iteration is just to ensure that the commands are only applied on the player aircraft. It should all look familiar.
Now this itself is not really dynamic. All we have provided is the possibility to make it dynamic. We don't have a setting screen where you can manipulate the bindings or a way to parse the data from a config file. We will actually create a settings screen later in the book in the GUI chapter. This would have been a way too big topic to cover in this chapter as well.
Why a player is not an entity
Well, the Player class can very much be considered as an entity, but in our case it is just a controller of entities. The class represents the player's input in the world of the game. In our gameplay, this entails only to manipulating a node in our scene graph.
Take this with a grain of salt. This is something that people do differently, we could have created a player entity and a player controller or have everything in the player entity.
Our opinion while writing was that this is a much cleaner way to do it and gives a nice separation of the external input signals from the player and the game logic. We felt much more comfortable working like this, instead of potentially having a giant blob inside the hierarchy of entities.
[ 111 ]
www.it-ebooks.info