- •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
Forge of the Gods – Shaping Our World
But if we handle exceptions nowhere, the program will crash. We put an exception handler into the main() function, giving the user a meaningful error description:
int main()
{
try
{
Game game; game.run();
}
catch (std::exception& e)
{
std::cout << "\nEXCEPTION: " << e.what() << std::endl;
}
}
Building the scene
Now comes the interesting part: building up the scene, which is done in the World::buildScene() method. First, we initialize the different scene layers. We iterate through our array of layer node pointers and initialize each element. std::unique_ptr::get() returns a raw pointer to the stored object, we do not transfer ownership to the array. Finally, we attach the new node to the scene graph's root node as shown in the following code:
for (std::size_t i = 0; i < LayerCount; ++i)
{
SceneNode::Ptr layer(new SceneNode()); mSceneLayers[i] = layer.get();
mSceneGraph.attachChild(std::move(layer));
}
After the background texture for the desert is loaded, we configure it to repeat itself. We also create a texture rect (with int coordinates) using the conversion from the world bounds (which have the type sf::FloatRect and thus store float coordinates).
sf::Texture& texture = mTextures.get(Textures::Desert); sf::IntRect textureRect(mWorldBounds); texture.setRepeated(true);
[ 78 ]
www.it-ebooks.info
Chapter 3
Then, we create our SpriteNode class that links to the desert texture. We pass to its constructor the texture rectangle. This grants that our sprite is as big as the whole world. While the desert image is only one screen wide, it is tiled along the whole level in -y direction.
std::unique_ptr<SpriteNode> backgroundSprite( new SpriteNode(texture, textureRect));
backgroundSprite->setPosition( mWorldBounds.left, mWorldBounds.top);
mSceneLayers[Background] ->attachChild(std::move(backgroundSprite));
Finally, we have come to the point where we add the airplanes! First, we create the player's airplane. We set the world's pointer mPlayerAircraft to the newly created scene node. Then, we specify an initial position (the spawn position) and velocity.
The forward velocity equals the scroll speed. We also introduce a sideward velocity with value 40, to show a more interesting movement pattern. At last, we attach the plane to the Air scene layer.
std::unique_ptr<Aircraft> leader(
new Aircraft(Aircraft::Eagle, mTextures)); mPlayerAircraft = leader.get(); mPlayerAircraft->setPosition(mSpawnPosition); mPlayerAircraft->setVelocity(40.f, mScrollSpeed); mSceneLayers[Air]->attachChild(std::move(leader));
Since single planes are boring, we give our leader an escort of two other airplanes, one on each side. After construction, we set a position and attach the escort to the leader's scene node. The specified position is thus interpreted relative to the leader. In the following example, the first escort plane is located 80 units to the left and 50 units behind the leader:
std::unique_ptr<Aircraft> leftEscort(
new Aircraft(Aircraft::Raptor, mTextures)); leftEscort->setPosition(-80.f, 50.f); mPlayerAircraft->attachChild(std::move(leftEscort));
For the second escort, the code is almost the same, except that the relative x position is now positive, meaning that this plane is located to the right.
std::unique_ptr<Aircraft> rightEscort(
new Aircraft(Aircraft::Raptor, mTextures)); rightEscort->setPosition(80.f, 50.f); mPlayerAircraft->attachChild(std::move(rightEscort));
[ 79 ]
www.it-ebooks.info
Forge of the Gods – Shaping Our World
That was it, now our scene is ready! Now, our scene graph looks as shown in the following diagram. Below the description of each node, you see the most derived class type.
Scene Graph Root |
|
|
[SceneNode] |
|
|
Layer: Background |
Layer: Air |
|
[SceneNode] |
[SceneNode] |
|
Desert |
|
|
[SpriteNode] |
Leader Aircraft |
|
|
|
[Aircraft] |
Escort Aircraft 1 |
Escort Aircraft 2 |
|
[Aircraft] |
|
[Aircraft] |
Update and draw
The update() and draw() methods bring the encapsulated scene graph functionality to the API of the World class.
The function to draw sets the current view and delegates the work to the scene graph:
void World::draw()
{
mWindow.setView(mWorldView);
mWindow.draw(mSceneGraph);
}
[ 80 ]
www.it-ebooks.info