Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
SFML Game Development.pdf
Скачиваний:
194
Добавлен:
28.03.2016
Размер:
4.19 Mб
Скачать

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]