- •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 8
Low-level rendering
Besides the high-level convenience classes sf::Sprite, sf::Text and sf::Shape, SFML provides a low-level graphics API which is more complicated to use, but allows more flexibility. In the next section, we are going to look behind the scenes of rendering and discuss corresponding techniques as they are implemented in SFML.
OpenGL and graphics cards
The graphics card architecture consists of many components. Notable are the graphics processing unit (GPU), which performs computations on the graphics card, and the video memory, which stores data such as textures. In contrast to their counterparts CPU and RAM, graphics cards' components are highly optimized to process 2D and 3D graphics.
SFML is built on top of the Open Graphics Library (OpenGL). OpenGL is, like DirectX, a specification of an interface to the graphics card. Operating systems provide an API written in C that allows applications to access graphic card functionality. Newer graphics cards support higher OpenGL versions and thus have the benefit of more modern features.
The way SFML is designed is heavily influenced by the underlying OpenGL functionality. SFML itself uses an object-oriented approach and abstracts low-level accesses away; therefore users can work with the library without even knowing OpenGL. It is, however, advantageous to be aware of basic underlying techniques, in order to see the whole picture. For specific requirements, it is also possible to mix
SFML and OpenGL.
Understanding render targets
A render target defines the place where 2D objects such as sprites, texts, or shapes are rendered. In SFML, this boils down to the abstract base class sf::RenderTarget. Apart from clear() and draw() methods, the class provides functionality to manipulate the current view.
A render window is a concrete implementation of a render target. Render windows represent application windows to which you can render graphical objects. In addition, they provide facilities for input handling and configurations such as
V-Sync. The class sf::RenderWindow, which we have been using all the time, inherits sf::RenderTarget and sf::Window.
[ 189 ]
www.it-ebooks.info
Every Pixel Counts – Adding Visual Effects
A render texture is another realization of the render target concept. Here, you do not draw objects to a window, but to a texture. Render textures can be used to render a scene that is not immediately displayed, but can be further processed—for example, saved to a file or edited as a whole. SFML provides the class sf::RenderTexture which derives from sf::RenderTarget. Notable is the method getTexture() which returns const sf::Texture& with the render texture's current contents. As with render windows, you must call display() before you can actually use that texture. This step is often forgotten.
Texture mapping
We have worked a lot with textures in the game, but not explained how they are actually displayed on the screen. Texel (texture element) is the term used for pixels in texture space. The case where every texel in the texture corresponds to a pixel on the window is an exception. In general, transforms of the graphical object and the view affect the way how pixels are displayed on the screen.
Every graphical object on the screen consists of vertices. A vertex is a point that defines the geometry of the object. Multiple vertices are grouped to geometric primitives such as lines, triangles, rectangles, and so on. In most cases, we have rectangular objects (such as sf::Sprite) that have four vertices, namely the four corners of the rectangle. Polygons (modeled by sf::Shape) allow a different number of vertices.
Each vertex consists of target coordinates (the position of the point on the render target, in world units) and texture coordinates (the position in the source texture, in texels). Texture coordinates are sometimes also called UV coordinates, because the variables u and v are often used instead of x and y. The process of texture mapping specifies how target coordinates are mapped onto texture coordinates, in order
to know which pixels have to be drawn where. This mapping is clarified in the following figure:
[ 190 ]
www.it-ebooks.info
Chapter 8
This figure only shows an aligned rectangle in the texture, the proportions of which are evenly maintained in the render target. In fact, you have a lot of freedom to place your vertices. This may result in distorted textures, but, you should just experiment yourself.
SFML provides the class sf::Vertex that represents a vertex of the geometric object. It has the following public member variables:
•sf::Vector2f position: the target coordinates (x, y)
•sf::Vector2f texCoords: the texture coordinates (u, v)
•sf::Color color: used to colorize the vertex
Vertex arrays
All geometric primitives except points consist of more than one vertex. A vertex array is a collection of vertices that are drawn together. A vertex array need not necessarily represent a single geometric object; it may also store the vertices of many objects.
In SFML, the class sf::VertexArray is used to model vertex arrays. It is a thin
wrapper around std::vector<sf::Vertex> and derives from sf::Drawable. We can add new vertices to the end of the array, and access existing vertices using the index operator.
The primitive type determines how the vertices are interpreted to form a geometric primitive. For example, the primitive type sf::Triangles interprets three subsequent vertices as one triangle, the next three vertices as another triangle, and so on. sf::Quads interprets four subsequent vertices as a quadrilateral. When we work with rectangles, we will be using the sf::Quads primitive type.
A small, incomplete example should give you a rough idea how vertices, vertex arrays and render targets interact:
sf::Vertex v;
v.position = sf::Vector2f(x, y); v.texCoords = sf::Vector2f(u, v); v.color = sf::Color::Blue;
sf::VertexArray vertices; vertices.setPrimitiveType(sf::Quads); vertices.append(v);
...
[ 191 ]
www.it-ebooks.info