- •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
Warfare Unleashed – Implementing Gameplay
Concerning collision response, if there are many cases to consider, the dispatching could be done using a two-dimensional lookup table. The categories of both partners would serve as indices, and the table entries are function objects that implement the collision response for a concrete collider pair.
As nice as these optimizations sound, there is a price to pay—the implementation becomes more complicated. A decent amount of book keeping is required to keep everything synchronous, for example, the grid. Each time an entity moves, it might move to another cell, so we have to keep track of it. Newly created entities must be inserted, and destroyed entities must be removed from the right cell.
In conclusion, such optimizations are not only nice to have, but a bare necessity when the world and the number of entities grow. However, the implied book-keeping overhead does not pay off for smaller scenarios, which is a reason why we kept things simple in our game.
An interacting world
A lot of game logic has been implemented in the different entities, now we look at functionality that is defined in the World class. You have already seen the collision in the last section.
Cleaning everything up
During the game, entities are destroyed in battle, and have to be removed from the scene graph. We do not remove them instantly. Once in a frame, we iterate through the scene graph, check which nodes have been destroyed, and detach them from their parents. To find out whether a node has been destroyed, we write the virtual function SceneNode::isDestroyed(). By default, it returns false. A derived entity may specify a condition under which it returns true. Usually, this will be the case when the hitpoints are zero or less (that is, the entity is destroyed).
bool Entity::isDestroyed() const
{
return mHitpoints <= 0;
}
In addition, we add a virtual function that checks if a scene node should be removed from the scene graph. By default, this is true as soon as the node is destroyed.
bool SceneNode::isMarkedForRemoval() const
{
return isDestroyed();
}
[ 180 ]
www.it-ebooks.info
Chapter 7
However, this need not always be the case. Imagine an entity that has been destroyed, but still needs to reside for some time in the world, in order to drop a pickup, show an explosion animation, or similar. While isDestroyed() tells whether entities are logically dead and therefore, don't interact with the world anymore, isMarkedForRemoval() tells whether the scene node can be removed from the scene graph. The Aircraft class itself delays removal after destruction, to let enemies drop their pickups in the update() function. There, a special flag determines the return value.
bool Aircraft::isMarkedForRemoval() const
{
return mIsMarkedForRemoval;
}
The removal is performed by the following method. In the first part, std::remove_ if() rearranges the children container, so that all active nodes are at the beginning, and the ones to remove at the end. The call to erase() actually destroys these SceneNode::Ptr objects. In the second part, the function is recursively called for all child nodes. std::mem_fn() creates a function object which returns true, if and only if, the member function passed as argument returns true.
void SceneNode::removeWrecks()
{
auto wreckfieldBegin = std::remove_if(mChildren.begin(), mChildren.end(), std::mem_fn(&SceneNode::isMarkedForRemoval)); mChildren.erase(wreckfieldBegin, mChildren.end());
std::for_each(mChildren.begin(), mChildren.end(), std::mem_fn(&SceneNode::removeWrecks));
}
This function can now be called in World::update(), and we automatically get rid of all nodes that request their removal.
Out of view, out of the world
Most entities that leave the current view become meaningless. Launched projectiles that have missed their enemy unwaveringly follow their path in the endless void.
Enemies that fly past the screen continue to fly, although the player will never see them again, which can be costly performance-wise.
[ 181 ]
www.it-ebooks.info
Warfare Unleashed – Implementing Gameplay
In order to reduce the amount of unnecessary entities, especially having our collision algorithm in mind, we want to remove entities that are located outside the view. Remember that getBattlefieldBounds() returns sf::FloatRect, which is slightly bigger than getViewBounds(). It also contains the area beyond the view, inside which the enemies spawn. We create a command that destroys all entities, of which the bounding rectangle doesn't intersect with the battlefield's bounding rectangle
(that is, they are outside).
void World::destroyEntitiesOutsideView()
{
Command command;
command.category = Category::Projectile
| Category::EnemyAircraft; command.action = derivedAction<Entity>( [this] (Entity& e, sf::Time)
{
if (!getBattlefieldBounds()
.intersects(e.getBoundingRect()))
e.destroy();
});
mCommandQueue.push(command);
}
The final update
A lot of new logic code has found its way into the World class; the different functions are invoked from World::update(), which currently looks as follows. The function names are self-explanatory.
void World::update(sf::Time dt)
{
mWorldView.move(0.f, mScrollSpeed * dt.asSeconds()); mPlayerAircraft->setVelocity(0.f, 0.f);
destroyEntitiesOutsideView();
guideMissiles();
while (!mCommandQueue.isEmpty()) mSceneGraph.onCommand(mCommandQueue.pop(), dt);
adaptPlayerVelocity();
[ 182 ]
www.it-ebooks.info