У нас уже есть карта, но когда персонаж двигается – игра за ним не следит, а он уходит за края экрана. Такого быть не должно и для этого в sfml есть возможность работать с камерой вида.
Видеоверсия:
Я буду передавать координаты персонажа для нашей камеры. Можно было просто передать типа “view.setCenter(p.x,p.y);” и всё будет норм, но мы с камерой будем работать сейчас по крупному и как помните из урока я говорил, что к свойствам обращаться напрямую не стоит. Короче говоря – в классе Player допишем метод, после метода void update пишем:
1 2 3 4 5 6 |
float getplayercoordinateX(){ //этим методом будем забирать координату Х return x; } float getplayercoordinateY(){ //этим методом будем забирать координату Y return y; } |
А две переменные x , y в начале класса сделаем приватными. Так выглядит теперь начало класса Player:
1 2 3 4 |
class Player { private: float x, y;//объявили переменные, в конструкторе Player ниже дадим им значения public: float w, h, dx, dy, speed; // dx dy speed будут = 0 в конструкторе класса ниже |
Это принцип инкапсуляции ООП. Когда вы работаете в крупной компании по разработке игр – вам не нужно знать свойства объекта , какие то там х, у и тд. Вам здесь и сейчас нужно использовать его координаты и вы используете метод get…чётотам(что разрабы ваши предложили). Короче говоря , – у вас есть машина в реале (в ООП – класс машина) и вам не важно как там устроен двигатель, какие процессы в ней происходят, как она едет и прочее. Вы просто используете готовый инструмент (руль, газ, кпп) и едете! По сути то , что вы едете и есть использование публичных методов класса. А то что она сосёт бензин при этом, работает двигатель, коробка передач что то там шестеренки бла-бла-бла – вас не волнует вобще – вы едете и юзаете эту коробку передач, эту машину. так вот приватный метод (функция) это и есть тот процесс, который происходит, но вас он не касается. оно просто работает. В крупных проектах, повторюсь, – та же басня. Вам пофиг что там ваш коллега накатал и по какой логике оно работает – вы просто пользуетесь и не лезете в эти внутренности кода. Я думаю можно будет сделать дополнение к уроку 8 и рассказать про ООП подробнее , если попросите.
Идём далее:
Создадим новый заголовочный файл view.h. (забыли как создать? см пример в предыдущем уроке)
В нем напишем:
1 2 3 4 |
#include <SFML/Graphics.hpp> using namespace sf; sf::View view;//объявили sfml объект "вид", который и является камерой |
Теперь напишем функцию “слежения камеры за игроком”:
1 2 3 4 5 6 7 |
void getplayercoordinateforview(float x,float y) { //функция для считывания координат игрока view.setCenter(x + 100, y); //следим за игроком, передавая его координаты камере. +100 - сместили камеру по иксу вправо. эксперементируйте } |
Перейдем в файл main.cpp , подключите заголовочник:
#include “view.h”//подключили код с видом камеры
А там где объявляются переменные функции, (хоть сразу после RenderWindow window(sf::VideoMode(640, 480), “Lesson 10. kychka-pc.ru”);) Напишем:
1 |
view.reset(sf::FloatRect(0, 0, 640, 480));//размер "вида" камеры при создании объекта вида камеры. (потом можем менять как хотим) Что то типа инициализации. |
(Так же уберем управление на W,S,A,D и оставим только стрелочки. На W,S,A,D у нас теперь другие планы )
И теперь нам нужно по нажатию каждой клавиши передвигать камеру, следя за игроком:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
///////////////////////////////////////////Управление персонажем с анимацией//////////////////////////////////////////////////////////////////////// if (Keyboard::isKeyPressed(Keyboard::Left)) { p.dir = 1; p.speed = 0.1; CurrentFrame += 0.005*time; if (CurrentFrame > 3) CurrentFrame -= 3; p.sprite.setTextureRect(IntRect(96 * int(CurrentFrame), 96, 96, 96)); getplayercoordinateforview(p.getplayercoordinateX(), p.getplayercoordinateY());//передаем координаты игрока в функцию управления камерой } if (Keyboard::isKeyPressed(Keyboard::Right)) { p.dir = 0; p.speed = 0.1; CurrentFrame += 0.005*time; if (CurrentFrame > 3) CurrentFrame -= 3; p.sprite.setTextureRect(IntRect(96 * int(CurrentFrame), 192, 96, 96)); getplayercoordinateforview(p.getplayercoordinateX(), p.getplayercoordinateY());//передаем координаты игрока в функцию управления камерой } if (Keyboard::isKeyPressed(Keyboard::Up)) { p.dir = 3; p.speed = 0.1; CurrentFrame += 0.005*time; if (CurrentFrame > 3) CurrentFrame -= 3; p.sprite.setTextureRect(IntRect(96 * int(CurrentFrame), 288, 96, 96)); getplayercoordinateforview(p.getplayercoordinateX(), p.getplayercoordinateY());//передаем координаты игрока в функцию управления камерой } if (Keyboard::isKeyPressed(Keyboard::Down)) { p.dir = 2; p.speed = 0.1; CurrentFrame += 0.005*time; if (CurrentFrame > 3) CurrentFrame -= 3; p.sprite.setTextureRect(IntRect(96 * int(CurrentFrame), 0, 96, 96)); getplayercoordinateforview(p.getplayercoordinateX(), p.getplayercoordinateY());//передаем координаты игрока в функцию управления камерой } |
Можно было вызвать функцию сразу после управления и она бы работала, но у нас задел будет на то, чтобы передвигать камеру по карте на W,S,A,D и если оставить чуть ниже , вне управления – то скроллинг карты будет перебиваться нашим героем, потому что камера всегда будет хотеть встать по координатам персонажа. А так она встает только когда мы нажимаем стрелки.
Теперь перед очисткой экрана (window.clear), пишем:
1 |
window.setView(view);//"оживляем" камеру в окне sfml |
Запустите, должно работать
Теперь вопрос, как двигать камеру для игры типа стратегии? Ну, так же, как и персонажа, примерно
В файле view.h пишем:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
void viewmap(float time) { //функция для перемещения камеры по карте. принимает время sfml if (Keyboard::isKeyPressed(Keyboard::D)) { view.move(0.1*time, 0);//скроллим карту вправо (см урок, когда мы двигали героя - всё тоже самое) } if (Keyboard::isKeyPressed(Keyboard::S)) { view.move(0, 0.1*time);//скроллим карту вниз (см урок, когда мы двигали героя - всё тоже самое) } if (Keyboard::isKeyPressed(Keyboard::A)) { view.move(-0.1*time, 0);//скроллим карту влево (см урок, когда мы двигали героя - всё тоже самое) } if (Keyboard::isKeyPressed(Keyboard::W)) { view.move(0, -0.1*time);//скроллим карту вправо (см урок, когда мы двигали героя - всё тоже самое) } } |
Смотрим комменты. Чтобы заработало – пишем в main.cpp в месте например после p.update(time):
1 |
viewmap(time);//функция скроллинга карты, передаем ей время sfml |
Запускаем и пробуем двигать на wsad. Если вы хотите двигать камеру вручную и чтобы она не следила за персонажем – удаляете строки слежения за игроком в нашей первой функции камеры. (там где set.center…)
Теперь о возможностях камеры sfml. Её можно повернуть , масштабировать, двигать(выше двинули уже:-)) и даже сделать несколько камер, каждая из которых показывает определенный участок в игре. Так, например, можно сделать игру на двоих с раздельным экраном. Или какую-то миникарту. В файлы view.h напишите ф-цию:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
void changeview(){ if (Keyboard::isKeyPressed(Keyboard::U)) { view.zoom(1.0100f); //масштабируем, уменьшение //view.zoom(1.0006f); //тоже самое помедленнее соответственно } if (Keyboard::isKeyPressed(Keyboard::R)) { //view.setRotation(90);//сразу же задает поворот камере view.rotate(1);//постепенно поворачивает камеру (отрицательное значение - в обратную сторону) } if (Keyboard::isKeyPressed(Keyboard::I)) { view.setSize(640, 480);//устанавливает размер камеры (наш исходный) } if (Keyboard::isKeyPressed(Keyboard::P)) { view.setSize(540, 380);//например другой размер } if (Keyboard::isKeyPressed(Keyboard::Q)) { view.setViewport(sf::FloatRect(0, 0, 0.5f, 1));//таким образом делается раздельный экран для игры на двоих. нужно только создать ещё один объект View и привязывать к нему координаты игрока 2. } } |
А в файле main.cpp вызовите её примерно в том же месте, где мы карту двигали:
1 |
changeview();//прикалываемся с камерой |
Запустите и потыкайте на кнопки, которые написаны в коде.
На этом всё, у нас остался один глюк после этого – мы видим края карты. Самое простое решение – сделать бордюр игры (у меня это кирпичи с символом ‘0’, см урок 9 ) и очистку экрана window.clear () , – одного цвета. Можно передать этой функции параметр, узнав приблизительный код цвета текстуры бордюра (как узнать код цвета в конце этой статьи) .
Я написал
1 |
window.clear(Color(128,106,89)); |
И смотрите результат:
Выглядит лучше, чем черный фон.
Ещё один способ, который не даст камере просто показать то , что находится снаружи бордюра – проверка координат. Что-то типа – если мы зашли левее такого то икса, то установить камеру вот так то и всё. Так же по игреку и во все четыре стороны. Вернуть черный цвет , чтобы лучше видеть разницу и методом подбора искать край. Для моего этого примера код такой (переписал нашу первую функцию):
1 2 3 4 5 6 7 8 9 10 |
void getplayercoordinateforview(float x, float y) { //функция для считывания координат игрока float tempX = x; float tempY = y;//считываем коорд игрока и проверяем их, чтобы убрать края if (x < 320) tempX = 320;//убираем из вида левую сторону if (y < 240) tempY = 240;//верхнюю сторону if (y > 554) tempY = 554;//нижнюю сторону view.setCenter(tempX, tempY); //следим за игроком, передавая его координаты. } |
Код работает на три стороны, кроме правой. Одно условие надо добавить – если вам надо попробуйте сами. Карта всё равно разрастется и придется это действие делать по новой, так что не парьтесь пока.
Вот мы слева вверху, а черных краёв не видно:
На рисунке ниже клавишами повернул, отдалил камеру:
Нашли ошибку в статье? Сообщите в комментарии!
На следующей уроке научимся взаимодействовать персонажем с самой картой.
Листинг урока:
main.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
#include <SFML/Graphics.hpp> #include "map.h" #include "view.h"//подключили код с видом камеры using namespace sf; ////////////////////////////////////////////////////КЛАСС ИГРОКА//////////////////////// class Player { private: float x, y; public: float w, h, dx, dy, speed; int dir = 0; String File; Image image; Texture texture; Sprite sprite; Player(String F, float X, float Y, float W, float H){ dx=0;dy=0;speed=0; File = F; w = W; h = H; image.loadFromFile("images/" + File); image.createMaskFromColor(Color(41, 33, 59)); texture.loadFromImage(image); sprite.setTexture(texture); x = X; y = Y; sprite.setTextureRect(IntRect(0, 0, w, h)); } void update(float time) { switch (dir) { case 0: dx = speed; dy = 0; break; case 1: dx = -speed; dy = 0; break; case 2: dx = 0; dy = speed; break; case 3: dx = 0; dy = -speed; break; } x += dx*time; y += dy*time; speed = 0; sprite.setPosition(x, y); } float getplayercoordinateX(){ //этим методом будем забирать координату Х return x; } float getplayercoordinateY(){ //этим методом будем забирать координату Y return y; } }; int main() { RenderWindow window(sf::VideoMode(640, 480), "Lesson 10. kychka-pc.ru"); view.reset(sf::FloatRect(0, 0, 640, 480));//размер "вида" камеры при создании объекта вида камеры. (потом можем менять как хотим) Что то типа инициализации. Image map_image; map_image.loadFromFile("images/map.png"); Texture map; map.loadFromImage(map_image); Sprite s_map; s_map.setTexture(map); Player p("hero.png", 250, 250, 96.0, 96.0); float CurrentFrame = 0; Clock clock; while (window.isOpen()) { float time = clock.getElapsedTime().asMicroseconds(); clock.restart(); time = time / 800; sf::Event event; while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) window.close(); } ///////////////////////////////////////////Управление персонажем с анимацией//////////////////////////////////////////////////////////////////////// if (Keyboard::isKeyPressed(Keyboard::Left)) { p.dir = 1; p.speed = 0.1; CurrentFrame += 0.005*time; if (CurrentFrame > 3) CurrentFrame -= 3; p.sprite.setTextureRect(IntRect(96 * int(CurrentFrame), 96, 96, 96)); getplayercoordinateforview(p.getplayercoordinateX(), p.getplayercoordinateY());//передаем координаты игрока в функцию управления камерой } if (Keyboard::isKeyPressed(Keyboard::Right)) { p.dir = 0; p.speed = 0.1; CurrentFrame += 0.005*time; if (CurrentFrame > 3) CurrentFrame -= 3; p.sprite.setTextureRect(IntRect(96 * int(CurrentFrame), 192, 96, 96)); getplayercoordinateforview(p.getplayercoordinateX(), p.getplayercoordinateY());//передаем координаты игрока в функцию управления камерой } if (Keyboard::isKeyPressed(Keyboard::Up)) { p.dir = 3; p.speed = 0.1; CurrentFrame += 0.005*time; if (CurrentFrame > 3) CurrentFrame -= 3; p.sprite.setTextureRect(IntRect(96 * int(CurrentFrame), 288, 96, 96)); getplayercoordinateforview(p.getplayercoordinateX(), p.getplayercoordinateY());//передаем координаты игрока в функцию управления камерой } if (Keyboard::isKeyPressed(Keyboard::Down)) { p.dir = 2; p.speed = 0.1; CurrentFrame += 0.005*time; if (CurrentFrame > 3) CurrentFrame -= 3; p.sprite.setTextureRect(IntRect(96 * int(CurrentFrame), 0, 96, 96)); getplayercoordinateforview(p.getplayercoordinateX(), p.getplayercoordinateY());//передаем координаты игрока в функцию управления камерой } p.update(time); viewmap(time);//функция скроллинга карты, передаем ей время sfml changeview();//прикалываемся с камерой вида window.setView(view);//"оживляем" камеру в окне sfml window.clear(); /////////////////////////////Рисуем карту///////////////////// for (int i = 0; i < HEIGHT_MAP; i++) for (int j = 0; j < WIDTH_MAP; j++) { if (TileMap[i][j] == ' ') s_map.setTextureRect(IntRect(0, 0, 32, 32)); if (TileMap[i][j] == 's') s_map.setTextureRect(IntRect(32, 0, 32, 32)); if ((TileMap[i][j] == '0')) s_map.setTextureRect(IntRect(64, 0, 32, 32)); s_map.setPosition(j * 32, i * 32); window.draw(s_map); } window.draw(p.sprite); window.display(); } return 0; } |
view.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
#include <SFML/Graphics.hpp> using namespace sf; sf::View view;//объявили sfml объект "вид", который и является камерой void getplayercoordinateforview(float x, float y) { //функция для считывания координат игрока float tempX = x; float tempY = y;//считываем коорд игрока и проверяем их, чтобы убрать края if (x < 320) tempX = 320;//убираем из вида левую сторону if (y < 240) tempY = 240;//верхнюю сторону if (y > 554) tempY = 554;//нижнюю сторону view.setCenter(tempX, tempY); //следим за игроком, передавая его координаты. } void viewmap(float time) { //функция для перемещения камеры по карте. принимает время sfml if (Keyboard::isKeyPressed(Keyboard::D)) { view.move(0.1*time, 0);//скроллим карту вправо (см урок, когда мы двигали героя - всё тоже самое) } if (Keyboard::isKeyPressed(Keyboard::S)) { view.move(0, 0.1*time);//скроллим карту вниз (см урок, когда мы двигали героя - всё тоже самое) } if (Keyboard::isKeyPressed(Keyboard::A)) { view.move(-0.1*time, 0);//скроллим карту влево (см урок, когда мы двигали героя - всё тоже самое) } if (Keyboard::isKeyPressed(Keyboard::W)) { view.move(0, -0.1*time);//скроллим карту вправо (см урок, когда мы двигали героя - всё тоже самое) } } void changeview(){ if (Keyboard::isKeyPressed(Keyboard::U)) { view.zoom(1.0100f); //масштабируем, уменьшение //view.zoom(1.0006f); //тоже самое помедленнее соответственно } if (Keyboard::isKeyPressed(Keyboard::R)) { //view.setRotation(90);//сразу же задает поворот камере view.rotate(1);//постепенно поворачивает камеру (отрицательное значение - в обратную сторону) } if (Keyboard::isKeyPressed(Keyboard::I)) { view.setSize(640, 480);//устанавливает размер камеры (наш исходный) } if (Keyboard::isKeyPressed(Keyboard::P)) { view.setSize(540, 380);//например другой размер } if (Keyboard::isKeyPressed(Keyboard::Q)) { view.setViewport(sf::FloatRect(0, 0, 0.5f, 1));//таким образом делается раздельный экран для игры на двоих. нужно только создать ещё один объект View и привязывать к нему координаты игрока 2. } } |
Я тут такую штуку запилил, мб пригодится кому. (я стратегию делаю)
view.setSize(window.getSize().x, window.getSize().y);
Пихаем в главный цикл игры и получаем: если изменяются размеры окна, сама игра не растягивается.
Спасибо! Может пригодится. Но когда ты сделаешь игру до конца – скорее всего ты сделаешь окно на весь экран , как у полноценной игры, без возможности растягивать. Так же не получится с таким способом убрать края экрана (то , что за бордюром игры, тот самый черный фон). Может быть и получится, но не это надо постараться. Вобще если устраивает, то можно юзать:)
Результат 10 уроков
http://youtu.be/3mhfTul7MAc
Молодца, у меня была улыбка до ушей и прям душа поёт, приятно:)
У меня проблема возникла…
Теперь перед очисткой экрана (window.clear), пишем:
1
window.setView(view);//”оживляем” камеру в окне sfml
Запустите, должно работать
Дошел до этого Момента. При запуске у меня пропал страйт персонажа, а если нажимаю нажимать на стрелки, то пропадает карта.
Вложение:
Без карты.
Вложение:
Напишите на форум, пожалуйста.