В этом уроке немного поработаем с текстом sfml. Текст встречается во многих играх, поэтому к нему мы еще будем возвращаться и в следующих уроках
Видеоверсия: http://www.youtube.com/watch?v=56rmko7OMtI
рассмотрим простой пример:
мы уже научились взаимодействовать с картой и по прошлому уроку лев берет камень и телепортируется. давай сейчас он просто его возьмет без телепортации , камень при этом пропадет, а на экран выведется количество собранных камней.
Добавьте побольше камней на карте, заменив символы ‘ ‘, на ‘s’
Добавим новую переменную playerScore в классе игрока:
1 |
int dir,playerScore;//новая переменная, хранящая очки игрока |
Инициализируем её в конструкторе класса Игрока: ( туда же запихнул направление и скорость, поскольку был на форуме такой случай )
1 2 |
Player(String F, int X, int Y, float W, float H){ dir = 0; speed = 0; playerScore = 0; |
В функции взаимодействия с картой убираем телепортацию и пишем так:
1 2 3 4 |
if (TileMap[i][j] == 's') { playerScore++;//если взяли камень, переменная playerScore=playerScore+1; TileMap[i][j] = ' '; } |
Затем:
В папку с проектом там же где и файл main.cpp (можете создать рядом папку и кидать потом туда подобные файлы) закинем файл шрифта CyrilicOld.ttf
Этот шрифт похож на старые славянские письмена, я специально когда-то такой искал можете поискать и другие шрифты под свой вкус и учтите , что не все из них поддерживают русский язык.
Ссылка на мой шрифт: https://yadi.sk/d/_rD7mjInf4pSr
В ф-ции int main() мы пишем:
1 2 3 4 5 |
Font font;//шрифт font.loadFromFile("CyrilicOld.ttf");//передаем нашему шрифту файл шрифта Text text("", font, 20);//создаем объект текст. закидываем в объект текст строку, шрифт, размер шрифта(в пикселях);//сам объект текст (не строка) text.setColor(Color::Red);//покрасили текст в красный. если убрать эту строку, то по умолчанию он белый text.setStyle(sf::Text::Bold | sf::Text::Underlined);//жирный и подчеркнутый текст. по умолчанию он "худой":)) и не подчеркнутый |
Теперь нам нужно вывести текст в какую то позицию. Можно вывести его в позицию на карте, или же привязать к окну, чтобы текст бегал вместе с камерой и оставался на экране. Сделаем второй вариант:
Перед draw(p.sprite) напишем:
1 2 3 |
text.setString("Собрано камней:");//задает строку тексту text.setPosition(view.getCenter().x , view.getCenter().y );//задаем позицию текста, центр камеры window.draw(text);//рисую этот текст |
И теперь если запустим, увидим по центру надпись, а лев поверх неё. Поэтому – если текст будет по центру и вы хотите сделать его поверх льва – вы должны рисовать после draw(p.sprite).
Разница есть:
Это может показаться не важным, но если у вас сложное меню или интерфейс программы – вы сразу вспомните эту простую вещь.
Чтобы передвинуть текст, например, левее и выше, следует написать:
1 |
text.setPosition(view.getCenter().x - 165, view.getCenter().y - 200);//задаем позицию текста, отступая от центра камеры |
Теперь давайте выведем количество собранных камней. Если мы напишем по привычке:
1 |
text.setString("Собрано камней:"+какоетозначение/переменная);//задаем строку тексту |
То это не прокатит , поскольку это значение будет обозначать индекс символа, с которого надо забивать строку. Вместо “Собрано” будет написано “брано”, например. Поэтому нам необходимо немного извратиться:
подключаем в main.cpp:
1 |
#include <sstream> |
и тогда наша фиговина внизу примет такой вид:
1 2 3 4 5 |
std::ostringstream playerScoreString; // объявили переменную playerScoreString << p.playerScore; //занесли в нее число очков, то есть формируем строку text.setString("Собрано камней:" + playerScoreString.str());//задаем строку тексту и вызываем сформированную выше строку методом .str() text.setPosition(view.getCenter().x - 165, view.getCenter().y - 200);//задаем позицию текста, отступая от центра камеры window.draw(text);//рисую этот текст |
нам на помощь пришла вспомогательная библиотека c++, которая позволяет работать со строками в потоке. Эта штука дает возможность сделать преобразование типов, см строку:
1 |
playerScoreString << p.playerScore; //занесли в нее число очков, формируем строку |
и мы можем заносить число из этой строки в переменную числовую (integer , например). вот пример (взято отсюда):
1 2 3 4 |
std::stringstream ss; ss << "22"; int k = 0; ss >> k; |
как видим для обратной операции >> требуется stringstream. в нашем случае это не нужно и мы добавляем ‘o’ , что означает output . Видна похожесть с “cin и cout”. Конечно, ведь все они друзья iostream’а, потоковые братья ввода-вывода.
Теперь если мы запустим программу – камни можно будет собирать и это выводится на экран.
Этот метод вам нужен только если вы работаете с числовыми операциями (в нашем случае мы увеличивали переменную)
Когда проект ваш будет серьезен – для читаемости кода подобные вещи лучше выпиливать вне ф-ции main и в отдельные классы/файлы
В заключении хотелось бы сказать, что текст можно двигать, как и спрайт. у него так же есть метод ‘move’. помимо этого у него есть setPosition, вращение и многое другое. Этим инструментом можно пользоваться.
Как видим у нас есть один глюк, который при ходьбе, например вверх – съедает камень раньше, чем лев до него доходит. Почему? Потому что существует пустота на нашем тайле, которая воспринимается игрой как персонаж, поскольку входит в его ширину и высоту. Смотрите на картинке
, например последняя строка – видно, что вверху пустое пространство. (один тайл 96*96). Есть два выхода – грамотно и ровненько обрезать в редакторе эту пустую область и переделать немного код, обновив координаты вывода тайла. Или же в самом коде, в зависимости от направления движения персонажа, попробовать уменьшать w и h вывода тайла соответственно в зависимости от стороны движения.
На примере кнопки up покажу:
Смотрим координату одного пикселя до головы по игреку: 306 , соответственно нам надо написать:
1 2 3 4 5 6 |
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));// } |
Кто сделает остальные кнопки – скиньте код в комментарий, я поправлю у себя:) Этот глюк стал заметен только после взаимодействия с картой, хотя и изначально был очевиден. Мы видим, что до этого персонаж не до конца упирался головой в стенку при ходьбе вверх, или брал камень не до конца доходя до него. А еще хуже – если бы в нас стреляли враги – они могли бы нас убить рядом пролетевшей пулей И я нашел еще один глюк, он тоже легко исправим, тему создал на форуме . И вобще – пользуйтесь форумом:) Когда там будет много записей, человеку, имеющему такую же проблему, как у вас когда-то, – будет проще её решить, посмотрев вашу тему.
Следующий урок будет коротким – мы выведем время игры на экран. В очень многих играх это используется – игрок проиграл и игра вывела ->”рекорд времени”, “время игры” или что-то вроде этого.
Листинг 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 |
#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, dx, dy, speed ; int dir, playerScore;//новая переменная, хранящая очки игрока String File; Image image; Texture texture; Sprite sprite; Player(String F, float X, float Y, float W, float H){ dir = 0; playerScore = 0; 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); interactionWithMap(); } 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] = ' '; } } } }; int main() { RenderWindow window(VideoMode(640, 480), "Lesson 12. 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; 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)); } 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)); s_map.setPosition(j * 32, i * 32); window.draw(s_map); } std::ostringstream playerScoreString; // объявили переменную playerScoreString << p.playerScore; //занесли в нее число очков, то есть формируем строку text.setString("Собрано камней:" + playerScoreString.str());//задаем строку тексту и вызываем сформированную выше строку методом .str() text.setPosition(view.getCenter().x - 165, view.getCenter().y - 200);//задаем позицию текста, отступая от центра камеры window.draw(text);//рисую этот текст window.draw(p.sprite); window.display(); } return 0; } |
Сделал простой таймер. кому то пригодится.
<code class="
“
Вложение:
Akyl, когда ты используешь Sleep(900) , у тебя ведь экран замирает по сути, почти на секунду.
Sleep(); убрал)
вот блин. как правильно ставить код?
тут мой косяк) никак не доделаю для комментариев)я отредактирую за тебя. ‘< pr e>код< / pre>‘ только без пробелов, но лучше на форум выложи ещё, в раздел “готовые решения”. там не затеряется. спасибо!
там есть кнопка code
готово)
Код не правильно отабражается и не могу редактировать
Я приму к сведению эту проблему. Сайтом не занимаюсь, поскольку считаю, что лучше в новый sfml – урок вложить время.. На форуме можно редактировать свои посты.
Появилась кнопка “вставьте код”. Попробуй выложить на форум еще раз, используя её. Извиняюсь за неудобства.
Вложение:
Есть вопрос интересный. Вот представим что есть большой текст, его нужно вывести в ограниченом по .х блоке. Как автоматически сделать перенос на новую строку когда текст достигнет границы блока? или нужно в ручную редактировать текст и вставлять “\n” везде где нужен перенос?
может нужно снимать ширину этого блока и взять за ноль его левый край. просчитать при этом сколько приходится букв на ширину например 100, размера шрифта 10. и получится что первый символ занимает 10 пикселей(например), тогда 10 символов 100 пикселей. положим , что ширина блока 100. вот и получается, что можно после определенного номера символа (9ого) ставить перенос строки. или может покопать методы и там найдется пересечение. может можно узнать длину текста(не символов, а именно текста). посмотри методы text. что они дают.
сильно не искав нашел это на оф сайте:
sf::Vector2f Position = Text.GetCharacterPos(4);
позволяет получить позицию символа в строке. а позиция это и есть координата. а координату спрайта свитка (блока) ты тоже знаешь . координата правого края свитка это Х+его ширина. или Х+его ширина/2 (это если использовал setOrigin и смещал начало координат в центр спрайта). вобщем вот от сюда тоже можно плясать.
метод GetCharacterPos описан в Documentation of SFML 1.6 и в версии 2.2 не распознается. даже заголовочный файл не принимает #include .
Хочу сделать игру типа кто хочет стать миллионером и вся загвоздка в выводе текста строго в блоке. Ведь вариантов вопросов будет много и каждый вручную редактировать как-то не професионально
нашел аналог, называется findCharacterPos()
Координаты доделал, а так же заменил if на else if
Отлично, молодец ) может кому-то пригодится
Я не разобрался с координатами спрайтов. Поэтому взять координаты предыдущего комментатора: tkir
если персонаж идет вниз, то левая стенка “трется” по персонажу, а если идти наверх то от персонажа остается место.
Вложение:
внесу свои пять копеек. если ты вдруг под linux и у тебя выводятся злоедучии квадраты вместо кириллицы. то тебе может помочь следующий кусок кода
как-то так
Это справедливо и для Windows. У меня например именно так – если не поставить букву L перед литералом, то выводится абракадабра либо квадраты вместо кириллицы.
Вот код для преобразования любого типа в string
У кого проблемы при использовании sf::Text::getColor () решение:
Спасибо, помог твой комментарий
Я конечно ничего против потока не имею против, но в силу своего недостатоного понимания его(потока) просто немного изменил 1 строку и все норм)
Добрый день.
При запуске игры спрайт персонажа находиться в одном положении, но при нажатии клавиш движения спрайт сначала перемещается в другую позицию и уже оттуда начинает свое движение.
Скорее всего проблема в функции
void updateили в задании координатsetPosition(x, y)Подскажите пожалуйста как это можно исправить?