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

Making a Game Tick

And with this you should still get the same result as you would in the SFML minimal example: a cyan-colored circle in a window with a black background. Nothing fancy yet, but we are well on our way.

Game loops and frames

Now talking a little more in-depth about the loop we have placed in the run() function. This loop is most often called the main loop or the game loop because it controls the lifetime of an application. As long as this one continues to iterate, the application will stay alive. In our case, we would like our application to terminate its execution as soon as the window ceases to exist.

Now what do we do during an iteration of this loop? First we process the events from the window, then we update the game, and finally we render the results on the screen. An iteration of the game loop is most often called a frame or a tick. You might have heard of the term frames per second (FPS). This is a measurement of how many loop iterations the game can do during a second. Sometimes, the concept of FPS only accounts for rendering times, but it is not unusual for it to encompass the input processing and logic updates as well.

We can explain this visually with a flow chart to further help you see clearly the logic of our loop.

Application Start

 

Game setup

 

Update Game

 

Render game state onto window

Process events from window

Is window still open?

Yes

 

No

 

Application Shutdown

 

[ 16 ]

 

www.it-ebooks.info

Chapter 1

It accurately describes what our application does at the moment. The only thing left out is the event processing. That functionality could have its own flow chart. But it does one task only in our basic example. It tells the window to close itself if the user requests it.

Now, the way we work with the computer in C++ is very linear. Everything is done in the set order that we give it, and the computer does nothing for us unless we explicitly tell it to. So, if we don't tell it to draw the circle, it won't draw it. If the state of the game somehow changes, and we don't tell the computer how to render a new frame, then nothing will change on the screen because the computer won't know that the graphics have changed.

Now that we got this sorted out, let's see if we can get something to happen over several frames. We make the circle move by pressing keys on our keyboard.

Input over several frames

First we have to be able to detect that the user is pressing down a key on his keyboard. SFML provides this functionality in several ways, but for now we will settle with input detection by responding to events.

What are events? The word itself implies something that is happening with our window, emitting a notice of the happening. As soon as the user somehow interacts with our window, the operating system sends an event that we can process. For our convenience, SFML translates events from the underlying operating systems to a uniform structure that we can use with ease: sf::Event. Once the window internally detects that some kind of input has happened, it will store an sf::Event object containing information about that input. We will then poll all those events as fast as we can, in order to respond to them.

SFML supports a wide variety of events, but there are two event types that interest us here: sf::Event::KeyPressed and sf::Event::KeyReleased. They represent a key being pressed and released respectively.

So let's change our code so that it can handle this. We again poll the window for events, and have a case differentiation on the event type.

void Game::processEvents()

{

sf::Event event;

while (mWindow.pollEvent(event))

{

switch (event.type)

{

[ 17 ]

www.it-ebooks.info

Making a Game Tick

For each time the while loop iterates, it means a new event that was registered by the window is being handled. While there can be many different events, we will only check for some types of events, which are of our interest right now.

case sf::Event::KeyPressed: handlePlayerInput(event.key.code, true); break;

case sf::Event::KeyReleased: handlePlayerInput(event.key.code, false); break;

case sf::Event::Closed: mWindow.close(); break;

}

}

}

In the handlePlayerInput() function, we check which key on the keyboard has been pressed or released. To store this information, we use four Boolean member

variables: mIsMovingUp, mIsMovingDown, mIsMovingLeft, and mIsMovingRight. We set the corresponding variable depending on the key being pressed or released.

void Game::handlePlayerInput(sf::Keyboard::Key key, bool isPressed)

{

if (key == sf::Keyboard::W) mIsMovingUp = isPressed;

else if (key == sf::Keyboard::S) mIsMovingDown = isPressed;

else if (key == sf::Keyboard::A) mIsMovingLeft = isPressed;

else if (key == sf::Keyboard::D) mIsMovingRight = isPressed;

}

In Game::handlePlayerInput()we receive the enumerator describing the key that was pressed or released. The flag describing whether a press or release occurred is passed as the second argument. So we check what key the user is manipulating, and change our state depending on that.

[ 18 ]

www.it-ebooks.info

Chapter 1

Now, we have a way to perceive that the user is pressing a key. We know when we want to move up, down, left, and right. We know at each iteration of the main loop exactly, what the user wants; we just have to update the circle with a new position depending on this input. This method gives us a great advantage. So finally we can write something in our update() function, namely, the movement of our player.

We check which of the four Boolean member variables is true, and determine the movement accordingly. By using += (instead of =) and if (instead of else if), we implicitly handle the case where two opposite keys, such as right and left are pressed at the same time—the movement stays zero. The update() function is shown in the following code snippet:

void Game::update()

{

sf::Vector2f movement(0.f, 0.f); if (mIsMovingUp)

movement.y -= 1.f; if (mIsMovingDown)

movement.y += 1.f; if (mIsMovingLeft)

movement.x -= 1.f; if (mIsMovingRight)

movement.x += 1.f;

mPlayer.move(movement);

}

We introduce two new things here: a vector and the move() function on the circle shape. The move() function does what its name says, it moves the shape by the amount we provide it.

Vector algebra

Vectors are an important part of algebraic mathematics. They imply lots of rules and definitions, which go beyond the scope of our book. However, SFML's sf::Vector2 class template is way more practical, both in concept and functionality. To be as simple as we could possibly be, we know that a coordinate in a two-dimensional Cartesian system would need two components: x and y. Because in graphics all coordinates are expressed with the decimal float data type, sf::Vector2 is instantiated as sf::Vector2<float>, which conveniently has a typedef named sf::Vector2f. Such an object is made to contain two member variables, x and y. This makes our life simpler, because now we don't need to pass two variables to functions, as we can fit both in a single sf::Vector2f object. sf::Vector2f also defines common vector operations, such as additions and subtractions with other vectors, or multiplications and divisions with scalars (single values), effectively shortening our code.

[ 19 ]

www.it-ebooks.info

Making a Game Tick

To be a little more precise in the explanation, vectors are not only used to define positions, but they also are a perfect fit to define orientations. So, a vector is great to store a two-component coordinate, be it an absolute or relative position, or even to express a direction to follow, or to shoot a bullet towards. There is a little more to know about two-dimensional vectors, especially if they are directions, such as the concept of normalization or unit vector. This operation applies only to directions, as it makes no sense in positions. We consider a vector normalized if it has length one (hence the term unit vector) and the vector still expresses the same direction as before normalization. The following figure visualizes the vector (2, 3). This vector represents a translation of 2 units to the right and 3 units down.

y=3

x=2

Please do not confuse sf::Vector2f with std::vector. While their names are similar, the first refers to the mathematical concept; the latter is simply a dynamically allocated array from the standard C++ library.

In our case, our vector called movement expresses a movement from the origin of the current coordinate system. For us, this origin is the shape's position. It might be a bit tricky getting into the whole way of thinking in different spaces if you don't like math.

Vector algebra is very interesting, and definitely something very useful if you know it. So we recommend you study it. Mathematics is your friend as soon as you stop fighting it; it really makes a lot of things easier for you in programming. It is almost safe to claim that this subsection of math is the single most important topic when we need to implement gameplay mechanics. A wide range of problems that you will face in almost any kind of game are already solved and well-studied before, so

you're better off learning this subject than reinventing the wheel every time. To avoid leaving you hanging, here's an example: Let's say you have point A and point B, which represent two characters in an action game. When the enemy at point A wants to shoot our player at point B, it needs to know the direction in which to shoot the projectile. Why waste your brains thinking on how to solve this problem if this field of math defines this operation as one of its most basic rules? All you need is to find the direction vector C, which is obtained by calculating B minus A. The difference between two positions gives us the direction between the two. Yes, that easy!

[ 20 ]

www.it-ebooks.info

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