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

Chapter 3

for (const SceneNode* node = this;

node != nullptr; node = node->mParent) transform = node->getTransform() * transform;

return transform;

}

sf::Vector2f SceneNode::getWorldPosition() const

{

return getWorldTransform() * sf::Vector2f();

}

The constant sf::Transform::Identity represents the identity transform, which does not have any effect on the object. Strictly speaking, it is not necessary in this code, but it clarifies the way how transforms are applied from the beginning.

The view

A view is a concept that allows us to select the part of our world we want to see. You can imagine a view to work like an image recording device. Essentially, every game programmer needs to understand views in depth, simply because every graphical game will require that knowledge to be applied directly. Sometimes it is called view, camera, or differently. Be it a two-dimensional or three-dimensional simulation, this concept will always be present.

Talking about graphics in two dimensions, we can describe a view as a rectangle that represents the subset of our world that we want to see at a particular time.

Such a simple and yet powerful concept gives us the possibility to perform many interesting tasks, such as an animated camera following the player around, a spy camera that allows us to take a peek at a remote location, a combination of two views to create a split-screen multiplayer experience, as well as many other applications.

It is not hard to grasp the power of the view; you may begin by knowing SFML, which provides us this utility in the conveniently named class sf::View. This class acts as a viewing lens, which ensures that anything you attempt to draw to the screen that is outside the view's rectangle will remain unseen.

Besides the viewing rectangle, another parameter that defines the sf::View class is the viewport, which gives us control over how to map the viewing rectangle to the actual application window.

[ 67 ]

www.it-ebooks.info

Forge of the Gods – Shaping Our World

Viewport

It is very easy to visualize what a viewport does. It is no more than a rectangle, in unit-length coordinates, that defines what region of the application's window is going to be used. So, a viewport rectangle starting at (0, 0) and ending in (1, 1) means essentially that the entire window area will be used to render the world. This is the most common use of viewports.

Another special use case for this concept is to provide split-screen multiplayer support. In order to do that, one would only need to have two sf::View objects, one for the top half of the screen, and another for the bottom. Then, each view would be configured to take its respective portion of the screen, having their viewport rectangles defined as, respectively, (left=0, top=0, width=1, height=0.5) and (left=0, top=0.5, width=1, height=0.5).

This would immediately mean that we would have two distinct "eyes" looking at the world, and that they would only render in their own viewports. Each of these eyes may be looking at any region of the world without any conflicts. After having those two views configured, having a split-screen support would be as simple as activating the first view, rendering the world, then activating the second, and rendering it again. But always beware that rendering the world twice will effectively consume twice as much rendering power.

View optimizations

Requesting an object to draw itself to the screen, also known as a draw call, is one of the most expensive operations you can do in a game, therefore, for any game that goes beyond only a few sprites being drawn, we need to worry about rendering performance by reducing draw calls as much as we can.

One way to do this is to use culling, which is a term that encompasses a wide range of techniques. Using culling techniques is always a great optimization to do, and the benefits will almost always be worth the extra work. For example, checking if every object is within the viewing rectangle and only draw them if they are, is a very simple form of efficient culling.

Of course, for big scenes with lots of objects, the simple fact that we are iterating potentially thousands or millions of objects every frame will be a drag in the performance. Because of this, game developers usually implement some kind of spatial subdivision. A spatial subdivision system can be done in many ways, but it always consists on dividing the scene in multiple cells, which group all objects that reside within that given cell. This way it's possible to cull a group of objects altogether, avoiding expensive iterations and tests on those objects.

[ 68 ]

www.it-ebooks.info

Chapter 3

One of the most popular ways to subdivide space is to use a quad tree. A quad tree is a hierarchical tree of cells. Only leaf nodes can contain objects and they subdivide again when a predetermined number of objects are present. Every cell is at least big enough to contain all its children cells. The hierarchical approach allows us to stop iterating through every object and only perform tests down the tree if each cell

passes the test. This effectively reduces the complexity of the algorithm from O(n) to O(log n), which is a very good optimization already. A quad tree is also much more efficient to find objects within the scene.

An alternative is a circle tree, which is similar to the quad tree, but instead each cell is a circle. This allows a different distribution of the objects and cheaper to compute tests. Whether to use one algorithm or the other depends on many factors. For each scene you may want to pick the most appropriate algorithm that will give you the most efficiency.

Resolution and aspect ratio

We have seen how viewports work, even though they are not always used explicitly. Now, it is very important to know how the viewing rectangle interacts with the specified viewport, and what pitfalls can we avoid by knowing that, along with what effects can we achieve by exploiting these notions to the limit. The resolution is the number of pixels a monitor or application window displays in each dimension. It is usually specified as width times height, for example, 1024 x 768. The aspect ratio is the ratio of the width to the height, for example, 1024:768 = 4:3.

The simplest possible use case of views is to have a window with a fixed size, and a view with the exact same dimensions, which implicitly means, the aspect ratio of view and window will be equal. This ratio is very important, because if the view rectangle is not proportional to the window size, we will experience a case of shrinking or stretching of our world.

Let's create an example by saying we have a window with size (640, 320) and a view with an equal rectangle. Then, we draw a circle in the middle of that screen, and when we verify, we see a perfect circle. However, as soon as we increase the view's rectangle height, without growing its width, as a result of trying to map more pixels to the very same space, we will notice that the circle is now shrunk vertically. It maintains the same width as before, but the height is smaller, turning the circle into an ellipse. This does not mean that we drew an incorrect circle, but rather that our view rectangle is not proportional and therefore shows distorted results.

[ 69 ]

www.it-ebooks.info

Forge of the Gods – Shaping Our World

It is very difficult to the programmer to predict view sizes that will fit perfectly to every screen, while keeping the same aspect ratio. There are tiny screens as well as very wide ones, and sometimes there is no perfect solution to this issue. However, there are tricks that can help handling this situation, to provide a better gaming experience to the audience, such as the "widescreen" technique, which leaves a bar on top of the screen and another on bottom without rendering, just so the remaining area is more alike the window size the programmer idealized. This kind

of techniques is outside the scope of the book, but it is likely that after understanding the concept of viewports and the viewport rectangle, the reader is able to implement them by intuition.

This mapping of the view to the window can also be exploited beyond preventing graphical glitches. It can be used to apply effects to the world, by animating the view's rectangle, to create shrinking and stretching illusions in many ways.

View scrolling

Now that we covered the most important parameters of sf::View, we will talk about how we use it in the context of the chapter's sample game.

After knowing how to define the viewport, and mapping to it an arbitrary region of the world with sf::View::setRect() with correct aspect ratio, we now want to move the view around, to create movement along the game world.

Since our game's action occurs in a vertical corridor, with no horizontal movement apart from moving the plane inside the fixed width window we see, we don't need to consider the x axis.

In every step of the game loop, when updating the state of the world, we also scroll the view a little. We do this at a constant speed towards the negative y axis (up), so we see a little further at each time step. Of course, we also move the player's aircraft by the exact same distance, so it won't get left behind, but remains in action.

This incremental and automatic scrolling operation of the view is simply done in the update code, by doing:

mWorldView.move(0.f, mScrollSpeed * dt.asSeconds());

It is always important to multiply our values by the elapsed time in seconds, so we ensure that the speed of n pixels per second is guaranteed, independent of the simulation frame rate.

[ 70 ]

www.it-ebooks.info

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