В продолжении темы текста и в закрепление темы времени, выведем игровое время. Игрок проиграл – выводится время игры или очки, которые от него зависят. Есть такие игры, завязанные по смыслу лишь на этом.
Видеоверсия: https://www.youtube.com/watch?v=aMbs3cCk2TU
у нашего персонажа еще нет жизни , врагов и прочих земных прелестей, поэтому выведем время в левом верхнем углу. Вы сможете его спрятать, а потом вывести типа так: if (player.getLife()) {вывод времени игры на экран;}
то есть программа спросит : “если жизнь игрока равна false, то {выводим игровое время;}”. Конечно, чтобы время не тикало дальше после смерти игрока вам следует в момент смерти запомнить его в переменную и уже потом её вывести.
Давайте добавим веселья и введем понятие жизнь и смерть игрока, а так же добавим условия , при которых здоровье может пополняться, либо убавляться.
В карту я накидал немного паинт-тайликов и вот он – великий и долгожданный апдейт карты:
Как видите это не работа со слоями – я не рисовал сердце на прозрачном фоне и не накидывал его на готовую плитку сверху. я просто взял плитку за основу (1ую), скопировал и нарисовал. Не лучший вариант, но для урока самый быстрый. Тайлмап редакторы и работу со слоями мы еще будем проходить.
В файле map.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 |
#include <SFML/Graphics.hpp> const int HEIGHT_MAP = 25; const int WIDTH_MAP = 40; sf::String TileMap[HEIGHT_MAP] = { "0000000000000000000000000000000000000000", "0 s 0", "0 s 0", "0 s s 0", "0 s 0", "0 0", "0 s s 0", "0 h 0", "0 0", "0 0", "0 h 0", "0 0", "0 0", "0 0", "0 0", "0 f 0", "0 f 0", "0 0", "0 0", "0 f 0", "0 0", "0 0", "0 f 0", "0 0", "0000000000000000000000000000000000000000", }; |
f – flower, h-heart.
Теперь при прорисовке карты в конце две строки:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
/////////////////////////////Рисуем карту///////////////////// 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)); if ((TileMap[i][j] == 'f')) s_map.setTextureRect(IntRect(96, 0, 32, 32));//добавили цветок if ((TileMap[i][j] == 'h')) s_map.setTextureRect(IntRect(128, 0, 32, 32));//и сердечко s_map.setPosition(j * 32, i * 32); window.draw(s_map); } |
Запустим и увидим совершенно новую обновленную крутую мега карту полную опасностей и приключений шок смотреть всем 2015
Теперь добавим переменную отвечающую за жизни игрока.
1 2 3 4 5 6 7 8 9 10 |
float w, h, dx, dy, speed; int dir, playerScore, health;//новая переменная, хранящая жизни игрока bool life;//переменная жизнь, логическая String File; Image image; Texture texture; Sprite sprite; Player(String F, float X, float Y, float W, float H){ dir = 0; speed = 0; playerScore = 0; health = 100; // инициализировали переменную жизни в конструкторе life = true;//инициализировали логическую переменную жизни |
В функции взаимодействия с картой “interactionWithMap” добавим:
1 2 3 4 5 6 7 8 9 |
if (TileMap[i][j] == 'f') { health-=40;//если взяли ядовитейший в мире цветок,то переменная health=health-40; TileMap[i][j] = ' ';//убрали цветок } if (TileMap[i][j] == 'h') { health += 20;//если взяли сердечко,то переменная health=health+20; TileMap[i][j] = ' ';//убрали сердечко } |
А в конце update ф-ции добавим:
1 |
if (health <= 0){ life = false; speed = 0; }//если жизней меньше либо равно 0, то умираем и исключаем движение игрока после смерти |
Если здоровье<=0 то игрок мертв. сюда же в действие можно добавить показ анимации взрыва, кровищи и чего угодно.
Итак – если сейчас запустим, наедимся волчьих ягод в размере трех штук (100хп-40дамага*3<=0), то ничего не произойдёт. Переменная здоровья действительно будет меньше нуля, а логическая переменная скажет “смерть”. Но игре все равно, мы этого не почувствуем. Почему? У нас в ф-ции main при управлении персонажем переприсваивается скорость. Отсюда такой результат. Выходов несколько : либо переносим управление в ф-цию update игрока, либо перед всем управлением делаем один большой if , который спрашивает жив ли игрок и если да, то он может ходить. Первый способ красивее, поскольку уберет нагромождение из main ф-ции и к тому же более структурирует программу. Но способ требует больше заморочек по времени на реализацию и для демонстрации сути я применю второй способ:
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 |
///////////////////////////////////////////Управление персонажем с анимацией//////////////////////////////////////////////////////////////////////// if (p.life) { 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)); } 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)); } 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), 307, 96, 96)); } 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)); } } |
Всё – персонаж отравился
Давайте выведем переменную здоровья и посмотрим как она себя ведет. Вместо собранных камней (я их коллекционирование у себя убираю, не нужно мне это больше. и вобще что это за лев такой , который камни собирает ?:-))
В конце main() вместо собирания камней меняю на здоровье:
1 2 3 4 5 |
std::ostringstream playerHealthString; // объявили переменную playerHealthString << p.health; //занесли в нее число здоровья, то есть формируем строку text.setString("Здоровье:" + playerHealthString.str());//задаем строку тексту и вызываем сформированную выше строку методом .str() text.setPosition(view.getCenter().x - 165, view.getCenter().y - 200);//задаем позицию текста, отступая от центра камеры window.draw(text);//рисую этот текст |
Запускаю , смотрю как работает.
Теперь сделаю вывод игрового времени, которое дальше не тикает после смерти игрока. Код с комментами и новыми строчками . тут объявляем время:
1 2 3 4 5 6 7 8 9 10 11 |
Clock clock; Clock gameTimeClock;//переменная игрового времени, будем здесь хранить время игры int gameTime = 0;//объявили игровое время, инициализировали. while (window.isOpen()) { float time = clock.getElapsedTime().asMicroseconds(); if (p.life) gameTime=gameTimeClock.getElapsedTime().asSeconds();//игровое время в секундах идёт вперед, пока жив игрок, перезагружать как time его не надо. оно не обновляет логику игры clock.restart(); time = time / 800; |
И новая строка в конце примет вид:
1 2 3 4 5 6 |
std::ostringstream playerHealthString,gameTimeString; // объявили переменную здоровья и времени playerHealthString << p.health; gameTimeString << gameTime; //формируем строку text.setString("Здоровье: " + playerHealthString.str()+"\nВремя игры: "+gameTimeString.str());//задаем строку тексту и вызываем сформированную выше строку методом .str() text.setPosition(view.getCenter().x - 165, view.getCenter().y - 200);//задаем позицию текста, отступая от центра камеры window.draw(text);//рисую этот текст |
Всё. Работает
Если хотите чтобы игрока вырвало когда он проиграет, пишем так:
1 |
if (p.life) gameTime = gameTimeClock.getElapsedTime().asSeconds(); else { view.rotate(0.01); }//игровое время в секундах идёт вперед, пока жив игрок, перезагружать как time его не надо. оно не обновляет логику игры . если игрок умер - вращаем камеру |
Кстати, вот мой рекорд:
Можно по всякому закончить игру. Можно вывести время игры только после смерти,а не всегда в процессе игры его выводить. Или же если у вас платформер типа Марио, то вы можете показать игроку карту, чтобы он знал, сколько же он не прошел до конца. Для этого после смерти можно использовать движение камеры в нужном направлении. Обычно это вправо, например так:
1 |
if (p.life) gameTime = gameTimeClock.getElapsedTime().asSeconds(); else {view.move(0.1, 0); }//игровое время в секундах идёт вперед, пока жив игрок, перезагружать как time его не надо. оно не обновляет логику игры |
Не сработает, пока не перенесем функцию
getplayercoordinateforview(p.getplayercoordinateX(), p.getplayercoordinateY());
в условие пока игрок жив. (Потому что будет перебивать вид, каждый раз следя за игроком.) Поэтому засунули и получилось вот так:
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 |
if (p.life) { 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)); } 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)); } 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), 307, 96, 96)); } 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()); } |
Теперь при смерти камера идет вправо, показывая непройденную оставшуюся карту.
Еще одна идейка – вывести надпись игра окончена, ваше время и прочая статистика и при этом плавно увеличить камеру именно на этот текст. view.zoom здесь будет использоваться.
Вариантов много, сколько людей, столько и идей. Делитесь своими идеями и реализациями на форуме в соответствующем разделе.
На следующем уроке немного поработаем с текстом , клавишами и координатами.
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 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
#include <SFML/Graphics.hpp> #include "map.h" #include "view.h" #include <iostream> #include <sstream> using namespace sf; ////////////////////////////////////////////////////КЛАСС ИГРОКА//////////////////////// class Player { private: float x, y; public: float w, h, speed,dx,dy; int dir, playerScore, health;//новая переменная, хранящая жизни игрока bool life;//переменная жизнь, логическая String File; Image image; Texture texture; Sprite sprite; Player(String F, float X, float Y, float W, float H){ dx=0;dy=0;dir = 0; speed = 0; playerScore = 0; health = 100; // инициализировали переменную жизни в конструкторе life = true;//инициализировали логическую переменную жизни 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); interactionWithMap(); if (health <= 0){ life = false; }//если жизней меньше либо равно 0, то умираем } float getplayercoordinateX(){ return x; } float getplayercoordinateY(){ return y; } void interactionWithMap() { for (int i = y / 32; i < (y + h) / 32; i++) for (int j = x / 32; j<(x + w) / 32; j++) { if (TileMap[i][j] == '0') { if (dy>0) { y = i * 32 - h; } if (dy<0) { y = i * 32 + 32; } if (dx>0) { x = j * 32 - w; } if (dx < 0) { x = j * 32 + 32; } } if (TileMap[i][j] == 's') { playerScore++;//если взяли камень, переменная playerScore=playerScore+1; TileMap[i][j] = ' '; } if (TileMap[i][j] == 'f') { health -= 40;//если взяли ядовитейший в мире цветок,то переменная health=health-40; TileMap[i][j] = ' ';//убрали цветок } if (TileMap[i][j] == 'h') { health += 20;//если взяли сердечко,то переменная health=health+20; TileMap[i][j] = ' ';//убрали сердечко } } } }; int main() { RenderWindow window(VideoMode(640, 480), "Lesson 13. kychka-pc.ru"); view.reset(FloatRect(0, 0, 640, 480)); Font font; font.loadFromFile("CyrilicOld.ttf"); Text text("", font, 20); text.setColor(Color::Red); text.setStyle(Text::Bold); 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; Clock gameTimeClock;//переменная игрового времени, будем здесь хранить время игры int gameTime = 0;//объявили игровое время, инициализировали. while (window.isOpen()) { float time = clock.getElapsedTime().asMicroseconds(); if (p.life) gameTime = gameTimeClock.getElapsedTime().asSeconds(); else { view.move(0.8, 0); }//игровое время в секундах идёт вперед, пока жив игрок, перезагружать как time его не надо. оно не обновляет логику игры clock.restart(); time = time / 800; sf::Event event; while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) window.close(); } ///////////////////////////////////////////Управление персонажем с анимацией//////////////////////////////////////////////////////////////////////// if (p.life) { 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)); } 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)); } 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), 307, 96, 96)); } 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); window.setView(view); 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)); if ((TileMap[i][j] == 'f')) s_map.setTextureRect(IntRect(96, 0, 32, 32));//добавили цветок if ((TileMap[i][j] == 'h')) s_map.setTextureRect(IntRect(128, 0, 32, 32));//и сердечко s_map.setPosition(j * 32, i * 32); window.draw(s_map); } std::ostringstream playerHealthString, gameTimeString; // объявили переменную здоровья и времени playerHealthString << p.health; gameTimeString << gameTime; //формируем строку text.setString("Здоровье: " + playerHealthString.str() + "\nВремя игры: " + gameTimeString.str());//задаем строку тексту и вызываем сформированную выше строку методом .str() text.setPosition(view.getCenter().x - 165, view.getCenter().y - 200);//задаем позицию текста, отступая от центра камеры window.draw(text);//рисую этот текст window.draw(p.sprite); window.display(); } return 0; } |
А как размеры карты увеличить? я изменяю в файле Map ширину и высоту в итоге при компиляции получаю ошибку(
этот вопрос к 9 ому уроку, а лучше на форум. при изменении размера карты надо добавлять и элементы массива , их кол-во должно равняться ширине или высоте соответственно
Как персонажа переместить на следующую карту? Вот этот вопрос будет разбираться ?
будет рассматриваться. всё завязано на координатах игрока
спасибо )
Если у кого есть желание поиграть уже в эту версию игры, предлагаю генератор случайной карты. Правда придется внести некоторые изменения в код игры, но это не так уж и много.
Молодца Можно и на форум в раздел решений добавить Добавишь?
sfml решения
Я на твою запись тогда дам ссылку в 16-ом sfml уроке.
Не люблю я форумы Но ок, разместил.
Не посмотрел темы уроков, что дальше будет генератор