SFML и C++ Уроки \ Разработка игр › Форумы › Логика игр › Передвижение персонажа по клику мыши
В этой теме 3 ответа, 3 участника, последнее обновление Maksim 6 года/лет, 9 мес. назад.
-
АвторСообщения
-
Персонаж двигается только если двигается курсор мыши, иначе он стоит. Если слегка дернуть мышкой, он немного подвинется и ждет дальше движений
C++123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341#include <iostream>#include <SFML/Graphics.hpp>#include "map.h"#include "View.h"#include <sstream>#include "mission.h"#include <cmath>using namespace sf;class Player {private:public:float w, h, dx, dy,x,y, speed;int dir, playerScore, health;bool life, isMove, isSelect;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; playerScore = 0; health = 100; dx = 0; dy = 0;isMove = false, isSelect = false;life = true;File = F;w = W; h = H;image.loadFromFile("images/" + File);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);/*sprite.setOrigin(w / 2, h / 2);*/interactionWithMap();if (health <= 0) {life = false; speed = 0;}}void interactionWithMap()//ф-ция взаимодействия с картой{for (int i = y / 65; i < (y + h) / 65; i++)//проходимся по тайликам, контактирующим с игроком, то есть по всем квадратикам размера 32*32, которые мы окрашивали в 9 уроке. про условия читайте ниже.for (int j = x / 65; j<(x + w) / 65; j++)//икс делим на 32, тем самым получаем левый квадратик, с которым персонаж соприкасается. (он ведь больше размера 32*32, поэтому может одновременно стоять на нескольких квадратах). А j<(x + w) / 32 - условие ограничения координат по иксу. то есть координата самого правого квадрата, который соприкасается с персонажем. таким образом идем в цикле слева направо по иксу, проходя по от левого квадрата (соприкасающегося с героем), до правого квадрата (соприкасающегося с героем){if (TileMap[i][j] == '0')//если наш квадратик соответствует символу 0 (стена), то проверяем "направление скорости" персонажа:{if (dy>0)//если мы шли вниз,{y = i * 65 - h;//то стопорим координату игрек персонажа. сначала получаем координату нашего квадратика на карте(стены) и затем вычитаем из высоты спрайта персонажа.}if (dy<0){y = i * 65 + 65;//аналогично с ходьбой вверх. dy<0, значит мы идем вверх (вспоминаем координаты паинта)}if (dx>0){x = j * 65 - w;//если идем вправо, то координата Х равна стена (символ 0) минус ширина персонажа}if (dx < 0){x = j * 65 + 65;//аналогично идем влево}}if (TileMap[i][j] == 's') { //если символ равен 's' (камень)playerScore++;//какое то действие... например телепортация герояTileMap[i][j] = ' ';//убираем камень, типа взяли бонус. можем и не убирать, кстати.}if (TileMap[i][j] == 'f') {health -= 50;TileMap[i][j] = ' ';}if (TileMap[i][j] == 'h') {health += 10;TileMap[i][j] = ' ';}}}float getWidth() {//получить ширину объекаreturn w;}void setWidth(float width) {//установить ширину объектаw = width;}float getHeight() {//взять ширину объектаreturn h;}void setHeight(float height) {//задать ширину объектаh = height;}float getplayercoordinateX() {return x;}float getplayercoordinateY() {return y;}};int main(){RenderWindow window(VideoMode(640, 480), "Test" /*Style::Fullscreen*/);view.reset(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", 65.0, 65.0, 46.0, 45.0);Font font;font.loadFromFile("CyrilicOld.ttf");Text text("",font,20);Text text2("", font, 20);text.setFillColor(Color::Blue); /*text.setStyle(Text::Bold | Text::Underlined);*/text2.setFillColor(Color::Black);Image quest_image;quest_image.loadFromFile("images/missionbg.jpg");quest_image.createMaskFromColor(Color(0, 0, 0));Texture quest_texture;quest_texture.loadFromImage(quest_image);Sprite s_quest;s_quest.setTexture(quest_texture);s_quest.setTextureRect(IntRect(0, 0, 340, 510)); //приведение типов, размеры картинки исходныеs_quest.setScale(0.6f, 0.6f);//чуть уменьшили картинку, => размер стал меньшеfloat CurrentFrame = 0;Clock clock;Clock gameTimeClock;int gameTime = 0;bool showMissionText = true;//перетаскивание персонажа по мышкой//bool isMove = false;//float dX = 0;//корректировка нажатия по х//float dY = 0;//по у//createObjectForMapTimer += time;//наращиваем таймер //ТАЙМЕР ДЛЯ ОНЛАЙН ГЕНЕРАЦИИ//if (createObjectForMapTimer>3000) {// randomMapGenerate();//генерация случ камней// createObjectForMapTimer = 0;//обнуляем таймер//}randomMapGenerate();int tempX = 0;int tempY = 0;float distance = 0.0;while (window.isOpen()){float time = clock.getElapsedTime().asMicroseconds();clock.restart();time = time / 800;if (p.life) gameTime = gameTimeClock.getElapsedTime().asSeconds();else {view.rotate(0.1);view.zoom(1.0006f);}//else// view.move(0.1, 0); // движение камеры вправоVector2i pixelPos = Mouse::getPosition(window);//забираем коорд курсораVector2f pos = window.mapPixelToCoords(pixelPos);//переводим их в игровые (уходим от коорд окна)Event event;while (window.pollEvent(event)){if (event.type == Event::Closed)window.close();if (event.type == Event::MouseButtonPressed)//если нажата клавиша мышиif (event.key.code == Mouse::Left) {//а именно леваяif (p.sprite.getGlobalBounds().contains(pos.x, pos.y))//и при этом координата курсора попадает в спрайт{p.sprite.setColor(Color::Green);//красим спрайт в зеленый,тем самым говоря игроку,что он выбрал персонажа и может сделать ходp.isSelect = true;}}if (p.isSelect)//если выбрали объектif (event.type == Event::MouseButtonPressed)//если нажата клавиша мышиif (event.key.code == Mouse::Right) {//а именно праваяp.isMove = true;//то начинаем движениеp.isSelect = false;//объект уже не выбранp.sprite.setColor(Color::White);//возвращаем обычный цвет спрайтуtempX = pos.x;//забираем координату нажатия курсора ХtempY = pos.y;//и Y}if (p.isMove) {distance = sqrt((tempX - p.x)*(tempX - p.x) + (tempY - p.y)*(tempY - p.y));//считаем дистанцию (длину от точки А до точки Б). формула длины вектораif (distance > 2) {//этим условием убираем дергание во время конечной позиции спрайтаp.x += 0.1*time*(tempX - p.x) / distance;//идем по иксу с помощью вектора нормалиp.y += 0.1*time*(tempY - p.y) / distance;//идем по игреку так же}elsep.isMove = false;}if(event.type == Event::KeyPressed)if (event.key.code == Keyboard::Tab) {switch (showMissionText) {case true: {std::ostringstream playerHealthString;playerHealthString << p.health;std::ostringstream task;task << getTextMission(getCurrentMission(p.getplayercoordinateX()));text2.setString("Здоровье: " + playerHealthString.str() + "\n" + task.str() + "\n");showMissionText = false;break;}case false: {/*text2.setString(" ");*/showMissionText = true;break;}}}}if (p.life) {if (Keyboard::isKeyPressed(Keyboard::Left)) {p.dir = 1; p.speed = 0.1;CurrentFrame += 0.005 * time;if (CurrentFrame > 2) CurrentFrame -= 2;p.sprite.setTextureRect(IntRect(46 * int(CurrentFrame), 0, 46, 45));}if (Keyboard::isKeyPressed(Keyboard::Right)) {p.dir = 0; p.speed = 0.1;CurrentFrame += 0.005 * time;if (CurrentFrame > 2) CurrentFrame -= 2;p.sprite.setTextureRect(IntRect(46 * int(CurrentFrame) + 46, 0, -46, 45));}if (Keyboard::isKeyPressed(Keyboard::Up)) {p.dir = 3; p.speed = 0.1;CurrentFrame += 0.005 * time;if (CurrentFrame > 2) CurrentFrame -= 2;p.sprite.setTextureRect(IntRect(46 * int(CurrentFrame) + 46, 0, -46, 45));}if (Keyboard::isKeyPressed(Keyboard::Down)) {p.dir = 2; p.speed = 0.1;CurrentFrame += 0.005 * time;if (CurrentFrame > 2) CurrentFrame -= 2;p.sprite.setTextureRect(IntRect(46 * int(CurrentFrame) + 46, 0, -46, 45));}getplayercoordinateforview(p.getplayercoordinateX(), p.getplayercoordinateY());}// КАМЕРА ДЛЯ СТРАТЕГИИ//Vector2i localPosition = Mouse::getPosition(window);//if (localPosition.x < 3) { view.move(-0.2*time, 0); }//если пришли курсором в левый край экрана,то двигаем камеру влево//if (localPosition.x > window.getSize().x - 3) { view.move(0.2*time, 0); }//правый край-вправо//if (localPosition.y > window.getSize().y - 3) { view.move(0, 0.2*time); }//нижний край - вниз//if (localPosition.y < 3) { view.move(0, -0.2*time); }//верхний край - вверхp.update(time);window.setView(view);window.clear(Color(195,151,78));//--------------------------------------------------------------------------------------------------------for (int i = 0; i < HEIGHT_MAP; i++)for (int j = 0; j < WIDTH_MAP; j++){if (TileMap[i][j] == ' ') s_map.setTextureRect(IntRect(65, 0, 65, 65));if (TileMap[i][j] == 's') s_map.setTextureRect(IntRect(130, 0, 65, 65));if ((TileMap[i][j] == '0')) s_map.setTextureRect(IntRect(0, 0, 65, 65));if ((TileMap[i][j] == 'f')) s_map.setTextureRect(IntRect(195, 0, 65, 65));if ((TileMap[i][j] == 'h')) s_map.setTextureRect(IntRect(260, 0, 65, 65));s_map.setPosition(j * 65, i * 65);//по сути раскидывает квадратики, превращая в карту. то есть задает каждому из них позицию. если убрать, то вся карта нарисуется в одном квадрате 65*65 и мы увидим один квадратwindow.draw(s_map);}//-----------------------------------------------------------------------------------------std::ostringstream playerScoreString, playerHealthString, gameTimeString;playerScoreString << p.playerScore;playerHealthString << p.health;gameTimeString << gameTime;text.setString("Собрано червячков:" + playerScoreString.str() + "\nЗдоровье: " + playerHealthString.str() + "\nВремя в игре: " + gameTimeString.str());text.setPosition(view.getCenter().x - 370, view.getCenter().y - 200);if (!showMissionText) {text2.setPosition(view.getCenter().x + 125, view.getCenter().y - 130);s_quest.setPosition(view.getCenter().x + 115, view.getCenter().y - 130);window.draw(s_quest);window.draw(text2); //Рисуем текст 2 (миссия)}window.draw(text);window.draw(p.sprite);window.display();}return 0;}Как я понял, движение должно происходить так: сначала нужно кликнуть ЛКМ на персонажа, он должен выделиться зелёным. После этого нужно кликнуть ПКМ на точку карты, куда мы хотим пойти. Персонаж должен пойти в эту точку. То есть для движения достаточно двух нажатий кнопок мышки. Если после этих двух нажатий персонаж не двигается или двигается не так/не туда, надо подробно описать, что именно и при нажатии чего происходит.
Также можно попробовать закомментировать строчку 300, где написано
C++1p.update(time);и заменить её на
C++1p.sprite.setPosition(p.x, p.y);Так можно проверить, не прерывается ли движение в функции update().
У тебя обновление позиции спрайта находится в пуле ивентов. Попробуй начать вводить текст с клавиатуры и увидишь тот же эффект что и немного двигая мышь. Что бы пофиксить это логика перемещения спрайта должна быть вынесена в главный цикл, а не пул ивентов:
C++123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173while (window.isOpen()){float time = clock.getElapsedTime().asMicroseconds();clock.restart();time = time / 800;if (p.life) gameTime = gameTimeClock.getElapsedTime().asSeconds();else {view.rotate(0.1);view.zoom(1.0006f);}//else// view.move(0.1, 0); // движение камеры вправоVector2i pixelPos = Mouse::getPosition(window);//забираем коорд курсораVector2f pos = window.mapPixelToCoords(pixelPos);//переводим их в игровые (уходим от коорд окна)Event event;while (window.pollEvent(event)){if (event.type == Event::Closed)window.close();if (event.type == Event::MouseButtonPressed)//если нажата клавиша мышиif (event.key.code == Mouse::Left) {//а именно леваяif (p.sprite.getGlobalBounds().contains(pos.x, pos.y))//и при этом координата курсора попадает в спрайт{p.sprite.setColor(Color::Green);//красим спрайт в зеленый,тем самым говоря игроку,что он выбрал персонажа и может сделать ходp.isSelect = true;}}if (p.isSelect)//если выбрали объектif (event.type == Event::MouseButtonPressed)//если нажата клавиша мышиif (event.key.code == Mouse::Right) {//а именно праваяp.isMove = true;//то начинаем движениеp.isSelect = false;//объект уже не выбранp.sprite.setColor(Color::White);//возвращаем обычный цвет спрайтуtempX = pos.x;//забираем координату нажатия курсора ХtempY = pos.y;//и Y}////////////////////////////////////////////////if (p.isMove) {//distance = sqrt((tempX - p.x)*(tempX - p.x) + (tempY - p.y)*(tempY - p.y));//считаем дистанцию (длину от точки А до точки Б). формула длины вектора//if (distance > 2) {//этим условием убираем дергание во время конечной позиции спрайта// p.x += 0.1*time*(tempX - p.x) / distance;//идем по иксу с помощью вектора нормали// p.y += 0.1*time*(tempY - p.y) / distance;//идем по игреку так же//}//else// p.isMove = false;//}if(event.type == Event::KeyPressed)if (event.key.code == Keyboard::Tab) {switch (showMissionText) {case true: {std::ostringstream playerHealthString;playerHealthString << p.health;std::ostringstream task;task << getTextMission(getCurrentMission(p.getplayercoordinateX()));text2.setString("Здоровье: " + playerHealthString.str() + "\n" + task.str() + "\n");showMissionText = false;break;}case false: {/*text2.setString(" ");*/showMissionText = true;break;}}}}if (p.life) {if (Keyboard::isKeyPressed(Keyboard::Left)) {p.dir = 1; p.speed = 0.1;CurrentFrame += 0.005 * time;if (CurrentFrame > 2) CurrentFrame -= 2;p.sprite.setTextureRect(IntRect(46 * int(CurrentFrame), 0, 46, 45));}if (Keyboard::isKeyPressed(Keyboard::Right)) {p.dir = 0; p.speed = 0.1;CurrentFrame += 0.005 * time;if (CurrentFrame > 2) CurrentFrame -= 2;p.sprite.setTextureRect(IntRect(46 * int(CurrentFrame) + 46, 0, -46, 45));}if (Keyboard::isKeyPressed(Keyboard::Up)) {p.dir = 3; p.speed = 0.1;CurrentFrame += 0.005 * time;if (CurrentFrame > 2) CurrentFrame -= 2;p.sprite.setTextureRect(IntRect(46 * int(CurrentFrame) + 46, 0, -46, 45));}if (Keyboard::isKeyPressed(Keyboard::Down)) {p.dir = 2; p.speed = 0.1;CurrentFrame += 0.005 * time;if (CurrentFrame > 2) CurrentFrame -= 2;p.sprite.setTextureRect(IntRect(46 * int(CurrentFrame) + 46, 0, -46, 45));}getplayercoordinateforview(p.getplayercoordinateX(), p.getplayercoordinateY());}// КАМЕРА ДЛЯ СТРАТЕГИИ//Vector2i localPosition = Mouse::getPosition(window);//if (localPosition.x < 3) { view.move(-0.2*time, 0); }//если пришли курсором в левый край экрана,то двигаем камеру влево//if (localPosition.x > window.getSize().x - 3) { view.move(0.2*time, 0); }//правый край-вправо//if (localPosition.y > window.getSize().y - 3) { view.move(0, 0.2*time); }//нижний край - вниз//if (localPosition.y < 3) { view.move(0, -0.2*time); }//верхний край - вверхif (p.isMove) {//Перемещён сюдаdistance = sqrt((tempX - p.x)*(tempX - p.x) + (tempY - p.y)*(tempY - p.y));//считаем дистанцию (длину от точки А до точки Б). формула длины вектораif (distance > 2) {//этим условием убираем дергание во время конечной позиции спрайтаp.x += 0.1*time*(tempX - p.x) / distance;//идем по иксу с помощью вектора нормалиp.y += 0.1*time*(tempY - p.y) / distance;//идем по игреку так же}elsep.isMove = false;}p.update(time);window.setView(view);window.clear(Color(195,151,78));//--------------------------------------------------------------------------------------------------------for (int i = 0; i < HEIGHT_MAP; i++)for (int j = 0; j < WIDTH_MAP; j++){if (TileMap[i][j] == ' ') s_map.setTextureRect(IntRect(65, 0, 65, 65));if (TileMap[i][j] == 's') s_map.setTextureRect(IntRect(130, 0, 65, 65));if ((TileMap[i][j] == '0')) s_map.setTextureRect(IntRect(0, 0, 65, 65));if ((TileMap[i][j] == 'f')) s_map.setTextureRect(IntRect(195, 0, 65, 65));if ((TileMap[i][j] == 'h')) s_map.setTextureRect(IntRect(260, 0, 65, 65));s_map.setPosition(j * 65, i * 65);//по сути раскидывает квадратики, превращая в карту. то есть задает каждому из них позицию. если убрать, то вся карта нарисуется в одном квадрате 65*65 и мы увидим один квадратwindow.draw(s_map);}//-----------------------------------------------------------------------------------------std::ostringstream playerScoreString, playerHealthString, gameTimeString;playerScoreString << p.playerScore;playerHealthString << p.health;gameTimeString << gameTime;text.setString("Собрано червячков:" + playerScoreString.str() + "\nЗдоровье: " + playerHealthString.str() + "\nВремя в игре: " + gameTimeString.str());text.setPosition(view.getCenter().x - 370, view.getCenter().y - 200);if (!showMissionText) {text2.setPosition(view.getCenter().x + 125, view.getCenter().y - 130);s_quest.setPosition(view.getCenter().x + 115, view.getCenter().y - 130);window.draw(s_quest);window.draw(text2); //Рисуем текст 2 (миссия)}window.draw(text);window.draw(p.sprite);window.display();}понятно, чуть позже проверю с выносом позиций, спасибо
-
АвторСообщения
Для ответа в этой теме необходимо авторизоваться.