Давайте научим персонажа немного взаимодействовать с картой. Карта – массив , а значит нам необходимо взаимодействовать с элементами этого массива. Если короче, то
Видеоверсия: http://www.youtube.com/watch?v=x6_E3RW0Mxo
нам надо проверять столкнулись ли мы с этим элементом и если да, то что делаем в этом случае.
Прежде чем начнем, сообщу, что я убрал последние две функции из файла камеры, убрал их вызов в main соответственно , а так же перенес слежение камеры за персонажем не в управление, а после, сократив код на 3 строки. Фишки с камерой были для демонстрации и если вам в вашей игре они нужны – оставляйте, конечно
Давайте реализуем столкновение с краями карты (символы ‘0’) и действие с камнем на карте. В классе игрока напишем функцию после функции update. Эту функцию мы потом передадим в update , чтобы оживить. Итак, пишем:
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 |
void interactionWithMap()//ф-ция взаимодействия с картой { for (int i = y / 32; i < (y + h) / 32; i++)//проходимся по тайликам, контактирующим с игроком, то есть по всем квадратикам размера 32*32, которые мы окрашивали в 9 уроке. про условия читайте ниже. for (int j = x / 32; j<(x + w) / 32; j++)//икс делим на 32, тем самым получаем левый квадратик, с которым персонаж соприкасается. (он ведь больше размера 32*32, поэтому может одновременно стоять на нескольких квадратах). А j<(x + w) / 32 - условие ограничения координат по иксу. то есть координата самого правого квадрата, который соприкасается с персонажем. таким образом идем в цикле слева направо по иксу, проходя по от левого квадрата (соприкасающегося с героем), до правого квадрата (соприкасающегося с героем) { if (TileMap[i][j] == '0')//если наш квадратик соответствует символу 0 (стена), то проверяем "направление скорости" персонажа: { if (dy>0)//если мы шли вниз, { y = i * 32 - h;//то стопорим координату игрек персонажа. сначала получаем координату нашего квадратика на карте(стены) и затем вычитаем из высоты спрайта персонажа. } if (dy<0) { y = i * 32 + 32;//аналогично с ходьбой вверх. dy<0, значит мы идем вверх (вспоминаем координаты паинта) } if (dx>0) { x = j * 32 - w;//если идем вправо, то координата Х равна стена (символ 0) минус ширина персонажа } if (dx < 0) { x = j * 32 + 32;//аналогично идем влево } } if (TileMap[i][j] == 's') { //если символ равен 's' (камень) x = 300; y = 300;//какое то действие... например телепортация героя TileMap[i][j] = ' ';//убираем камень, типа взяли бонус. можем и не убирать, кстати. } } } |
Персонаж может находиться сразу на нескольких квадратиках (которые 32*32, из них карта ж состоит). Так вот мы проходимся по всем квадратикам, с которыми он пересекается – по иксу слева направо, по игреку сверху вниз. Вверху алгоритм этого действия с комментариями.
А ниже небольшая схемка этого алгоритма:
Код будет работать некорректно (игрок будет не до конца подходить к объекту), поскольку я заметил, что допустил ошибку в предыдущих уроках. Короче – исправьте в методе update строчку последнюю на :
1 |
sprite.setPosition(x,y); |
А то немного не в ту позицию спрайт выводили. Извиняюсь, скопировал с класса пули своей старой игры и не заметил (там то пуля от центра танка летела, поэтому такой код был)
И чтобы это всё заработало нам необходимо вызвать эту функцию в методе update, чтобы она постоянно работала. Где нибудь в конце воткните её:
1 |
interactionWithMap();//вызываем функцию, отвечающую за взаимодействие с картой |
Всё, готово. Запустите и посмотрите на результат. В следующем уроке поработаем с текстом с примером всё того же уже знаменитого камня. Будем считать количество собранных камней
Листинг 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 |
#include <SFML/Graphics.hpp> #include <iostream> #include "map.h" #include "view.h" using namespace sf; ////////////////////////////////////////////////////КЛАСС ИГРОКА//////////////////////// class Player { private: float x, y ; public: float w, h, dx, dy, speed ; int dir; 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; dir =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();//вызываем функцию, отвечающую за взаимодействие с картой } void interactionWithMap()//ф-ция взаимодействия с картой { for (int i = y / 32; i < (y + h) / 32; i++)//проходимся по тайликам, контактирующим с игроком,, то есть по всем квадратикам размера 32*32, которые мы окрашивали в 9 уроке. про условия читайте ниже. for (int j = x / 32; j<(x + w) / 32; j++)//икс делим на 32, тем самым получаем левый квадратик, с которым персонаж соприкасается. (он ведь больше размера 32*32, поэтому может одновременно стоять на нескольких квадратах). А j<(x + w) / 32 - условие ограничения координат по иксу. то есть координата самого правого квадрата, который соприкасается с персонажем. таким образом идем в цикле слева направо по иксу, проходя по от левого квадрата (соприкасающегося с героем), до правого квадрата (соприкасающегося с героем) { if (TileMap[i][j] == '0')//если наш квадратик соответствует символу 0 (стена), то проверяем "направление скорости" персонажа: { if (dy>0)//если мы шли вниз, { y = i * 32 - h;//то стопорим координату игрек персонажа. сначала получаем координату нашего квадратика на карте(стены) и затем вычитаем из высоты спрайта персонажа. } if (dy<0) { y = i * 32 + 32;//аналогично с ходьбой вверх. dy<0, значит мы идем вверх (вспоминаем координаты паинта) } if (dx>0) { x = j * 32 - w;//если идем вправо, то координата Х равна стена (символ 0) минус ширина персонажа } if (dx < 0) { x = j * 32 + 32;//аналогично идем влево } } if (TileMap[i][j] == 's') { //если символ равен 's' (камень) x = 300; y = 300;//какое то действие... например телепортация героя TileMap[i][j] = ' ';//убираем камень, типа взяли бонус. можем и не убирать, кстати. } } } float getplayercoordinateX(){ return x; } float getplayercoordinateY(){ return y; } }; int main() { RenderWindow window(sf::VideoMode(640, 480), "Lesson 11. 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(); } float coordinatePlayerX, coordinatePlayerY = 0; coordinatePlayerX = p.getplayercoordinateX(); coordinatePlayerY = p.getplayercoordinateY(); ///////////////////////////////////////////Управление персонажем с анимацией//////////////////////////////////////////////////////////////////////// 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), 288, 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(coordinatePlayerX, coordinatePlayerY); 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); } window.draw(p.sprite); window.display(); } return 0; } |
Остальные файлы без изменений, за исключением еще view.h – в нем я оставил только метод getplayercoordinateXforview . Тут буква “Х” лишняя в названии метода, т.к мы считываем две координаты, а не только Х. Я у себя исправил и в прошлом уроке тоже исправил. Теперь метод называется “getplayercoordinateforview”
Спасибо автору за столь хорошие уроки.Но у меня возник вопрос как сделать перемещение героя в точку клика,он у меня сразу перемещается при клике а хотелось чтобы он постепенно двигался
Пожалуйста! Как время будет я подумаю и напишу об этом. Первое , что пришло в голову – забираешь координаты курсора мыши , а потом делаешь проверки в цикле типа “пока текущая координата игрока Х меньше координаты курсора, то идти вправо” и так далее с другими направлениями. Открылся форум, задай вопрос там, может sfml товарищи уже делали это Надо оживлять форум, тогда база знаний пополнится. А полезные ответы форума я потом буду закидывать в уроки.
Хорошо,спасибо)
Спасибо автор за твою серию уроков. Однако мне кое-что не понятно. В комментариях ты говоришь, что “мы проходимся по всей карте”, но как так, если в определении переменных цикла фигурируют координаты игрока?
И ещё, небольшая просьба: в последующих уроках постарайся более формально и точно излагать мысли. Ну например, вместо “мы проходимся по всей карте” стоило бы (лишь на мой взгляд) написать “мы проходимся по массиву символов…”. Просто иногда не очень понятно, что имеется в виду.
да, спасибо за замечание. стоит сказать, что мы проходимся не по всей карте в этом случае, а лишь по квадратам, с которыми игрок контактирует. поэтому там и фигурируют его координаты. это в комментарии к коду косяк такой, в уроке в целом я думал, что пояснил это, особенно на картинке.
Добрый день, хорошие уроки, вопрос следующий: при добавлении функции void interactionWithMap(),после компиляции появляется один белый экран, и windows аварийно завершает программу,в чем может быть проблема?
Привет, спасибо.
С вопросом таким нужно на форум написать
хорошо сейчас перепишу
Добрый день!
Вы не могли бы объяснить, почему расчеты именно такие: в одном случае y = i * 32 – h, а в другом y = i * 32 + 32? Почему не -h/+h или не -32/+32 в обоих случаях?
То есть, почему мы в одном случае отнимаем h, равную 96 пикс, а в другом прибавляем 32 пикселя? Почему не оперировать одной и той же величиной в обоих случаях?
Спасибо!