- •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 5
Navigating between states
So far we have our state machine in place and running smoothly, the title screen starts up the program, but how to make the title screen call another state to its place when someone hits a key?
That is exactly what the StateStack class's delayed push and pop mechanism is for. Inside a state handleEvent() and update(), you are given three methods to control the execution and transitions of states: requestStackPush(), requestStackPop(),
and requestStackClear().
It is appropriate to use these methods to request new states to be pushed, or to show and replace the current one, as you will verify that our example states do throughout this chapter.
Creating the game state
So far we have covered the theory and practice for inserting the state stack into our sample game. It is fully functional but yet empty, so, it is finally time to create our first state, the game state.
For this, we create a class named GameState and we proceed to relocate the code that could be found in the Game class related to the actual aircraft gameplay to its new home:
class GameState : public State
{
public:
|
GameState(StateStack& stack, |
|
Context context); |
virtual void |
draw(); |
virtual bool |
update(sf::Time dt); |
virtual bool |
handleEvent(const sf::Event& event); |
private: |
|
World |
mWorld; |
Player& |
mPlayer; |
}; |
|
[ 123 ]
www.it-ebooks.info
Diverting the Game Flow – State Stack
The title screen
Because a good place to start is always the beginning, we are about to create the title screen; that initial screen you sometimes see in games. Before you enter the main menu of the game, it asks you to press any key.
We decided to go with the name TitleState, and define it as follows:
class TitleState : public State
{
public:
|
TitleState(StateStack& stack, |
|
Context context); |
virtual void |
draw(); |
virtual bool |
update(sf::Time dt); |
virtual bool |
handleEvent(const sf::Event& event); |
private: |
|
sf::Sprite |
mBackgroundSprite; |
sf::Text |
mText; |
bool |
mShowText; |
sf::Time |
mTextEffectTime; |
}; |
|
Our implementation of this screen is not very different from what you are used to see. It shows a background with a little information about the game, besides its title and then blinks a big old Press any key to continue message.
Here's how we detect the key stroke and use our state system to trigger a new state:
bool TitleState::handleEvent(const sf::Event& event)
{
if (event.type == sf::Event::KeyPressed)
{
requestStackPop();
requestStackPush(States::Menu);
}
return true;
}
[ 124 ]
www.it-ebooks.info
Chapter 5
The background is merely an image covering the whole window and the blinking effect on the sf::Text object is achieved through this little trick:
bool TitleState::update(sf::Time dt)
{
mTextEffectTime += dt;
if (mTextEffectTime >= sf::seconds(0.5f))
{
mShowText = !mShowText; mTextEffectTime = sf::Time::Zero;
}
return true;
}
The magic happening here is simple. The variable mShowText determines the visibility of the sf::Text object, so we toggle it every half second, achieving the blinking effect.
Every time the state updates, we have a time counter mTextEffectTime that increments with the elapsed time. Then, when that elapsed time is greater than half a second, we just toggle the mShowText variable and restart the counter.
Main menu
Okay, our title screen just finished, the user pressed a key and it is time to launch another screen, the famous main menu!
This is probably the most common state you will find in virtually every game, it is responsible for presenting the user its options and what can be done with the game. This is usually the point where you change settings, start or continue your game, watch videos and artwork, or simply exit the game.
For simplicity, we created the most basic main menu possible. It only presents two options: play and exit. Then, you can select which one you want by pressing the return key or alternate between the options with the up and down arrow keys.
Please notice this is a basic form of graphical user interface, which is the topic in the next chapter, therefore you can expect interesting improvements to it when we introduce the user interfaces in more depth!
[ 125 ]
www.it-ebooks.info
Diverting the Game Flow – State Stack
This state is not so different from the title screen but it does implement the option selection, and here is how we did it:
enum OptionNames
{
Play,
Exit,
}; |
|
std::vector<sf::Text> |
mOptions; |
std::size_t |
mOptionIndex; |
First we declare the containers of our options in the MenuState class, as well as the enumerator of the available options.
Then, we setup and push to the mOptions array the sf::Text objects, in the constructor, as follows:
sf::Text playOption; playOption.setFont(font); playOption.setString("Play"); centerOrigin(playOption);
playOption.setPosition(context.window->getView().getSize() / 2.f); mOptions.push_back(playOption);
The mOptionIndex integer variable is present so that we can track which is the currently selected option between all those in the mOptions array; it will have a value between 0 and n-1, n being the number of options in the menu, which is two in our example.
Finally, we define the most important function that helps controlling this menu:
void MenuState::updateOptionText()
{
if (mOptions.empty()) return;
//White all texts FOREACH(sf::Text& text, mOptions)
text.setColor(sf::Color::White);
//Red the selected text mOptions[mOptionIndex].setColor(sf::Color::Red);
}
[ 126 ]
www.it-ebooks.info
Chapter 5
This function is called once after constructing mOptions and again every time the mOptionIndex value changes. It ensures that only the selected option is highlighted in red, and the remaining is in white.
About what makes the mOptionIndex value actually change, it is merely simple handling of key presses in the handleEvent() function:
if (event.key.code == sf::Keyboard::Up)
{
if (mOptionIndex > 0) mOptionIndex--;
else
mOptionIndex = mOptions.size() - 1;
updateOptionText();
}
else if (event.key.code == sf::Keyboard::Down)
{
if (mOptionIndex < mOptions.size() - 1) mOptionIndex++;
else
mOptionIndex = 0;
updateOptionText();
}
Now that we handled the selection of an action between multiple options, it is time to actually make them push their own states and give the game continuity:
if (event.key.code == sf::Keyboard::Return)
{
if (mOptionIndex == Play)
{
requestStackPop();
requestStackPush(States::Game);
}
else if (mOptionIndex == Exit)
{
requestStackPop();
}
}
[ 127 ]
www.it-ebooks.info