- •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
Command and Control – Input Handling
Simple, isn't it? Once you have understood the concepts of functions and lambda expressions, you can quickly build complex functionalities. Note that you don't have to use lambda expressions, you can still stick to functors such as AircraftMover. But in some situations, lambdas allow you to express semantics far more compactly.
Now that was it, our command system is complete!
Commands in a nutshell
We have discussed many components in our command-based system; easy for someone to get lost. The following diagram should give you an overview about the whole functionality. Basically, the system is split into an input handling and
a game logic side. SFML events are polled in Game and forwarded to Player, which transforms the events to commands and feeds CommandQueue with them. The same process is followed for the real-time input, Player checks the current input state and pushes corresponding commands to the queue. The CommandQueue class stores a queue of commands and acts as the bridge between input handling and game logic. On the game logic side, the World class pops commands from the CommandQueue class and sends them to the root of the scene graph, inside which the commands are distributed depending on their receiver categories. Eventually, the functions stored in each command are applied to the correct game objects.
The following diagram gives an overview of the command system and the way it is integrated into our game architecture:
Player |
|
CommandQueue |
|
World |
Scene graph |
|
Command |
Command |
Command |
||||
|
|
|
||||
Event |
Real-time input |
|
|
|
|
|
Game |
|
|
|
|
||
|
Input handling |
|
|
Game logic |
|
[ 106 ]
www.it-ebooks.info
Chapter 4
Implementing the game logic
You certainly remember the basic example of the aircraft floating from left to right and back from Chapter 3, Forge of the Gods – Shaping Our World. As we now use player input, we do not need that code any more. However, we write some new code; for example we want to ensure that the player's plane remains inside the view, now that it can move freely.
All this is done in the function World::update(). In the following paragraphs, we advance through it step by step, explaining each piece of code. We begin with scrolling the view and setting the player's velocity to zero. Note that we really use zero, not the scrolling speed, so that the player falls behind relative to the moving view. The scrolling speed is addressed later in this function:
void World::update(sf::Time dt)
{
mWorldView.move(0.f, mScrollSpeed * dt.asSeconds()); mPlayerAircraft->setVelocity(0.f, 0.f);
Next, all commands from the queue are forwarded to the scene graph. This makes our plane react to the user input. When an arrow key is held down, the plane's velocity will be changed:
while (!mCommandQueue.isEmpty()) mSceneGraph.onCommand(mCommandQueue.pop(), dt);
Now, the basic velocity has been set. If right and top arrow keys are both held down, the plane will fly diagonally to the upper-right corner, but the velocity will be faster than the velocity when either the right or top key is pressed. To correct diagonal velocities, we divide the velocity by the square root of two. We also add the scroll speed. Now it is evident why we have not done this before; we could not have checked for diagonal movements if the velocity had not been zero:
sf::Vector2f velocity = mPlayerAircraft->getVelocity();
if (velocity.x != 0.f && velocity.y != 0.f) mPlayerAircraft->setVelocity(velocity / std::sqrt(2.f));
mPlayerAircraft->accelerate(0.f, mScrollSpeed);
In the next step, the regular update step of the scene is performed:
mSceneGraph.update(dt);
[ 107 ]
www.it-ebooks.info
Command and Control – Input Handling
The only thing left to do is to handle the case where the plane leaves the visible area of the screen. First, we compute the rectangle of the current view, and then we keep all X and Y coordinates inside the boundaries, given some distance to the screen's border. Last, we call Aircraft::setPosition() to apply the corrected position:
sf::FloatRect viewBounds(
mWorldView.getCenter() - mWorldView.getSize() / 2.f, mWorldView.getSize());
const float borderDistance = 40.f;
sf::Vector2f position = mPlayerAircraft->getPosition(); position.x = std::max(position.x,
viewBounds.left + borderDistance); position.x = std::min(position.x,
viewBounds.left + viewBounds.width - borderDistance); position.y = std::max(position.y,
viewBounds.top + borderDistance); position.y = std::min(position.y,
viewBounds.top + viewBounds.height - borderDistance); mPlayerAircraft->setPosition(position);
}
A general-purpose communication mechanism
What you have seen in the previous sections are commands—how they are used for input. Note that during the design process of our message-passing system, we have paid attention to encapsulate input handling and to make the other components generic and reusable. Only the Player class is directly coupled to the input; the classes Command and CommandQueue can deal with any functions that affect scene nodes for a given amount of time.
This genericity will be a great advantage, as it makes it possible to use our command system for other sources of control, like the network or artificial intelligence. We can even use the command system for in-game events, to notify entities about happenings in the world. All we need to do is to set up the corresponding command, push it to the queue, and it will be automatically delivered to the related scene nodes.
[ 108 ]
www.it-ebooks.info