SFML и C++ Уроки \ Разработка игр › Форумы › Логика игр › Стрельба по клику мыши
В этой теме 7 ответов, 3 участника, последнее обновление KindRedSand 6 года/лет, 10 мес. назад.
-
АвторСообщения
-
Добрый день. Только учусь писать на С++.
Задумал небольшой проект стрелялки с видом сверху. Игрок ходит во все стороны и по диагонали. Сделано вращение вокруг самого себя. Реализовал с помощью уроков стрельбу по направлению движения игрока. Но никак не получается сделать что бы он стрелял в сторону клика. В уроке было написано совмещаем 18 урок(движение по клику мыши), и стрельба из 28 урока.
Принцип понятен. Если нажата кнопка мыши левая -> создается объект пули
C++1234567if (event.type == Event::MouseButtonPressed){if (event.key.code == Mouse::Left){entities.push_back(new Bullet(BulletImage, "Bullet", lvl, p.x, p.y, 16, 16, p.state));}}p.state это реализация движения
C++12345678case 0: dx = -speed; dy = 0; break;//интовое значение state = leftcase 1: dx = speed; dy = 0; break;//интовое значение state = rightcase 2: dx = 0; dy = -speed; break;//интовое значение state = upcase 3: dx = 0; dy = speed; break;//интовое значение = downcase 6: dx = speed; dy = -speed; break;//интовое значение = up_rightcase 7: dx = -speed; dy = -speed; break;//интовое значение = up_leftcase 8: dx = speed; dy = speed; break;//интовое значение = down_rightcase 9: dx = -speed; dy = speed; break;//интовое значение = down_leftНо нам нужно по клику мыши. Попробовал сделать так
C++1234567if (event.type == Event::MouseButtonPressed)//если нажата клавиша мышиif (event.key.code == Mouse::Left){//а именно леваяentities.push_back(new Bullet(BulletImage, "Bullet", lvl, p.x, p.y, 16, 16, p.state));p.isMove = true;//то начинаем движениеtempX = pos.x;//забираем координату нажатия курсора ХtempY = pos.y;//и Y}Но я не понимаю как заменить p.state(движение по кнопкам) на нужное мне движение
C++123456distance = 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; std::cout << "priehali\n"; }Один раз получилось сделать ну мне так кажется, т.к. кликал мышкой на экране. возле персонажа появлялась пуля и зависала. Через какое то время в консоли появлялось сообщение PRIEHALI. Получается движение заканчивалось. Но пули не видно
Знаю, тема давняя, но все же, может быть кто поможет, та же проблема. Сижу уже несколько дней, не могу понять.
Для начала разберитесь как вы вобще заставили всё это работать через select case. По сути вы можете вовсе без него прожить передавая ускорение напрямую с каждой кнопки
C++123456789if (Keyboard::isKeyPressed(Keyboard::Right))dx += -0.1; //Когда зажата стрелка вправо мы придаём импульс движения вправоpublic void onUpdate(Time delta){//...sprite.move(dx * delta.asSeconds(), dy * delta.asSeconds());//Применяем смещение//...}Ну а направить пулю проще простого:
C++12345678910111213141516171819202122232425Vector2f vectorNormalize(Vector2f vector, Vector2f vector2){float velocity = 1 / std::sqrt((abs((vector - vector2).x * 5)*abs((vector - vector2).x) * 5) + (abs((vector - vector2).y * 5)*abs((vector - vector2).y * 5)));return Vector2f(vector.x * velocity, vector.y * velocity);}//...if (event.type == Event::MouseButtonPressed){if (event.key.code == Mouse::Left){entities.push_back(new Bullet(BulletImage, "Bullet", lvl, p.x, p.y, 16, 16, p.state));Vector2f direction = Vector2f(Mouse::getPosition(window).x - p.sprite.getPosition().x, Mouse::getPosition(window).y - p.sprite.getPosition().y);direction = vectorNormilize(direction, Vector(0, 0));entities.back().velocity = Vector2f(direction.x * speed, direction.y * speed);}}//...//...public void onUpdate(Time delta){sprite.move(velocity.x * delta.asSeconds(), velocity.y * delta.asSeconds());}//...//deleted
Все равно не могу разобраться
Как сюда добавить выстрел? Пробовал сделать как вы написали, ничего не получилось (не достаточно опыта и знаний, только начал). Так же пока не делал общий класс Entities.
Суть понятна, но как реализовать нет.
Вот мой код:
C++123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250#include <SFML/Graphics.hpp>#include <iostream>#include <sstream>using namespace sf;using namespace std;const int level_max = 20; //Максимальный уровень персонажаclass projectiles {public:float speed_projectiles = 3;float distance;Texture texture_projectiles;Sprite sprite_projectiles;Vector2i size_projectiles; //Размер спрайтаVector2f temp;Vector2f player;projectiles(Image &image, int size_projectiles_x, int size_projectiles_y, float tempX, float tempY, float player_x, float player_y){texture_projectiles.loadFromImage(image);sprite_projectiles.setTexture(texture_projectiles);player.x = player_x; player.y = player_y;temp.x = tempX; temp.y = tempY;}};class player{public:float x, y; //Координаты персонажаfloat speed; //Скорость персонажаfloat CurrentFrame = 0; //Счетчик анимации персонажаint walk; //Направление персонажаint origin_player_y_for_wakl = 0; //Для правильной остановки персонажа (спрайта)int health_max = 100; //Максимальное здоровьеfloat health = health_max; //Здоровьеint level; //Уровеньconst int level_exp[level_max - 2] = {5, 10, 20, 40, 80, 160, 320, 640, 1280, 2560, 5120, 10240, 20480, 40960, 81920, 163840, 327680, 655360}; //Массив опытаint level_number = 0; //Счетчик массива опытаint level_experience; //Опыт игрокаint level_check = level_exp[0]; //Для проверки опытаint free_point = 10; //Очки характеристикbool life; //Состояние (жив/мертв)Texture texture;Sprite sprite;Vector2i size_player; //Размер спрайтаVector2i origin_player; //Начало отрисовки спрайтаstring name_player; //Ник персонажаplayer(Image &image, String name, int size_player_x, int size_player_y, int origin_player_x, int origin_player_y, float position_x, float position_y){texture.loadFromImage(image);sprite.setTexture(texture);name_player = name;x = position_x; y = position_y;size_player.x = size_player_x; size_player.y = size_player_y;origin_player.x = origin_player_x; origin_player.y = origin_player_y;sprite.setTextureRect(IntRect(origin_player.x, origin_player.y, size_player.x, size_player.y));sprite.setPosition(x, y); //Координаты появленияlife = true; //Состояние: живspeed = 0.1; //Начальная скорость: 0.1level_experience = 0; //Начальное кол-во опыта: 0level = 1; //Начальный уровень: 1}void control(float time) {if (!walk) //Для правильной остановки персонажа (спрайта){if (origin_player_y_for_wakl == 0) sprite.setTextureRect(IntRect(origin_player.x, 0, size_player.x, size_player.y));if (origin_player_y_for_wakl == size_player.y) sprite.setTextureRect(IntRect(origin_player.x, size_player.y * 1, size_player.x, size_player.y));if (origin_player_y_for_wakl == size_player.y * 2) sprite.setTextureRect(IntRect(origin_player.x, size_player.y * 2, size_player.x, size_player.y));if (origin_player_y_for_wakl == size_player.y * 3) sprite.setTextureRect(IntRect(origin_player.x, size_player.y * 3, size_player.x, size_player.y));}if (Keyboard::isKeyPressed(Keyboard::A)) {walk = 1;origin_player_y_for_wakl = origin_player.y + size_player.y;sprite.setTextureRect(IntRect(origin_player.x + size_player.x * (int)CurrentFrame, origin_player_y_for_wakl, size_player.x, size_player.y));}if (Keyboard::isKeyPressed(Keyboard::D)) {walk = 2;origin_player_y_for_wakl = origin_player.y + size_player.y * 2;sprite.setTextureRect(IntRect(origin_player.x + size_player.x * (int)CurrentFrame, origin_player_y_for_wakl, size_player.x, size_player.y));}if (Keyboard::isKeyPressed(Keyboard::W)) {walk = 3;origin_player_y_for_wakl = origin_player.y + size_player.y * 3;sprite.setTextureRect(IntRect(origin_player.x + size_player.x * (int)CurrentFrame, origin_player_y_for_wakl, size_player.x, size_player.y));}if (Keyboard::isKeyPressed(Keyboard::S)) {walk = 4;origin_player_y_for_wakl = origin_player.y;sprite.setTextureRect(IntRect(origin_player.x + size_player.x * (int)CurrentFrame, origin_player_y_for_wakl, size_player.x, size_player.y));}if (Keyboard::isKeyPressed(Keyboard::A) &Keyboard::isKeyPressed(Keyboard::W)) {walk = 5;origin_player_y_for_wakl = origin_player.y + size_player.y * 3;sprite.setTextureRect(IntRect(origin_player.x + size_player.x * (int)CurrentFrame, origin_player_y_for_wakl, size_player.x, size_player.y));}if (Keyboard::isKeyPressed(Keyboard::A) &Keyboard::isKeyPressed(Keyboard::S)) {walk = 6;origin_player_y_for_wakl = origin_player.y;sprite.setTextureRect(IntRect(origin_player.x + size_player.x * (int)CurrentFrame, origin_player_y_for_wakl, size_player.x, size_player.y));}if (Keyboard::isKeyPressed(Keyboard::D) &Keyboard::isKeyPressed(Keyboard::S)) {walk = 7;origin_player_y_for_wakl = origin_player.y;sprite.setTextureRect(IntRect(origin_player.x + size_player.x * (int)CurrentFrame, origin_player_y_for_wakl, size_player.x, size_player.y));}if (Keyboard::isKeyPressed(Keyboard::D) &Keyboard::isKeyPressed(Keyboard::W)) {walk = 8;origin_player_y_for_wakl = origin_player.y + size_player.y * 3;sprite.setTextureRect(IntRect(origin_player.x + size_player.x * (int)CurrentFrame, origin_player_y_for_wakl, size_player.x, size_player.y));}if (Keyboard::isKeyPressed(Keyboard::Up)) health -= 1; //Уменьшаем ХП стрелкой вверхif (Keyboard::isKeyPressed(Keyboard::Right)) level_experience += 1000; //Добавляет опыт стрелкой вправо(+1000)if (Keyboard::isKeyPressed(Keyboard::Left)) level_experience += 10000; //Добавляет опыт стрелкой влево(+10000)if (walk) //Анимация движения{CurrentFrame += 0.005*time;if (CurrentFrame > 3) CurrentFrame -= 3;}switch (walk) {case 1: x -= speed*time; break; //Левоcase 2: x += speed*time; break; //Правоcase 3: y -= speed*time; break; //Верхcase 4: y += speed*time; break; //Низcase 5: x -= speed*time, y -= speed*time; break; //Лево-Верхcase 6: x -= speed*time, y += speed*time; break; //Лево-Низcase 7: x += speed*time, y += speed*time; break; //Право-Низcase 8: x += speed*time, y -= speed*time; break; //Право-Верх}walk = 0; //Останавливаем персонажаsprite.setPosition(x, y); //Меняем координаты персонажа}void level_player() {if (level_experience >= level_check) //Проверка опыта / уровня{level_check += level_exp[level_number];level_number++; //Переход на след. эл. массива опытаlevel++; //Повышаем уровеньfree_point += 2; //Добавляем 2 очка характеристикhealth_max += 20;health = health_max; //Восстанавливаем ХП}if (level == level_max) level_experience = 0; //Не даем прокачивать больше макс. уровня}void update(float time) {if (health < 1) life = false; //Здоровье ноль или меньше - мертвif (health > health_max) health = health_max; //Если ХП больше чем нужноif (life) control(time); //Подключаем управлениеif (life & health < health_max) health += 0.0004 * time; //Востановление ХП с течением времениlevel_player(); //Подключаем опыт и уровень}};int main(){RenderWindow window;Vector2i position_window = window.getPosition();window.create(VideoMode(800, 600), "Game");window.setFramerateLimit(60); //FPSRectangleShape rectangle_tab(sf::Vector2f(230, 580)); //Окно информации на "Tab"rectangle_tab.setFillColor(sf::Color(0, 0, 0, 128)); //Черный цвет с прозрачностьюrectangle_tab.setPosition(position_window.y + 10, position_window.x + 10);Clock clock; //ВремяImage image_player; //Игрокimage_player.loadFromFile("images/sprite1.png");image_player.createMaskFromColor(Color::White); // Цвет прозрачностиImage image_projectiles; //Выстрелimage_projectiles.loadFromFile("images/shoot.png");image_projectiles.createMaskFromColor(Color::White); // Цвет прозрачностиFont font; //ТекстText text_stats("", font, 14);font.loadFromFile("fonts/font_SEVENET_7_CYR.ttf");text_stats.setColor(Color::White);// Объект класса playerplayer p(image_player, "Player", 48, 48, 0, 0, 300, 400);/*1. Image - Изображение2. Name - Имя игрока3. size_player_x - Размер по Х4. size_player_y - Размер по Y5. origin_player_x - Начало первого спрайта по Х6. origin_player_y - Начало первого спрайта по Y7. position_x - Начальная позиция спрайта по Y8. position_y - Начальная позиция спрайта по Y*/while (window.isOpen()){float time = clock.getElapsedTime().asMicroseconds();clock.restart();time = time / 800;Event event;while (window.pollEvent(event)){if (event.type == Event::Closed)window.close();}p.update(time); //Обновляем класс player с течением времениif (event.type == Event::MouseButtonPressed) //Нажата клавиша мышиif (event.key.code == Mouse::Left) //Левая{}window.clear(Color(128, 128, 128)); //Серый фон игрыwindow.draw(p.sprite); //Рисуем персонажаif (Keyboard::isKeyPressed(Keyboard::Tab)) //Окно информации на "Tab"{ostringstream name_player; name_player << p.name_player; //Имя персонажаostringstream health_player; health_player << (int)p.health; //Здоровье персонажаostringstream level_player; level_player << p.level; //Уровень персонажаostringstream exp_player; exp_player << p.level_experience; //Опыт персонажаostringstream position_player_x; position_player_x << (int)p.x; //Координата Xostringstream position_player_y; position_player_y << (int)p.y; //Координата Yostringstream free_point_player; free_point_player << p.free_point; //Доступные очки характеристикtext_stats.setString("Имя: " + name_player.str() + "\n\nЗдоровье: " + health_player.str() + "\n\nУровень: " + level_player.str() + "\nОпыт: " + exp_player.str() + "\n\nКоординаты:\nX: " + position_player_x.str() + "\nY: " + position_player_y.str() + "\n\nОчки характеристик: " + free_point_player.str());text_stats.setPosition(position_window.y + 20, position_window.x + 20);window.draw(rectangle_tab); //Выводим окно при нажатии "Tab"window.draw(text_stats); //Выводим текст при нажатии "Tab"}window.display();}return 0;}C++123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169Vector2f vectorNormalize(Vector2f vector, Vector2f vector2){float velocity = 1 / std::sqrt((abs((vector - vector2).x * 5)*abs((vector - vector2).x) * 5) + (abs((vector - vector2).y * 5)*abs((vector - vector2).y * 5)));return Vector2f(vector.x * velocity, vector.y * velocity);}class projectiles {public:float speed_projectiles = 3;float distance;Texture texture_projectiles;Sprite sprite_projectiles;Vector2i size_projectiles; //Размер спрайтаVector2f temp;//Vector2f player;Vector2f velocity;projectiles(Image &image, int size_projectiles_x, int size_projectiles_y, /*float tempX, float tempY, float player_x, float player_y*/ Vector2f startPosition, Vector2f velocity, float duration = 5000){texture_projectiles.loadFromImage(image);sprite_projectiles.setTexture(texture_projectiles);//player.x = player_x; player.y = player_y;//temp.x = tempX; temp.y = tempY;sprite.setPosition(startPosition);this.velocity = velocity;}void update(float delta){duration -= delta;sprite.move(velocity.x * (delta/1000), velocity.y * (delta/1000);}};int main(){RenderWindow window;Vector2i position_window = window.getPosition();window.create(VideoMode(800, 600), "Game");window.setFramerateLimit(60); //FPSRectangleShape rectangle_tab(sf::Vector2f(230, 580)); //Окно информации на "Tab"rectangle_tab.setFillColor(sf::Color(0, 0, 0, 128)); //Черный цвет с прозрачностьюrectangle_tab.setPosition(position_window.y + 10, position_window.x + 10);Clock clock; //ВремяImage image_player; //Игрокimage_player.loadFromFile("images/sprite1.png");image_player.createMaskFromColor(Color::White); // Цвет прозрачностиImage image_projectiles; //Выстрелimage_projectiles.loadFromFile("images/shoot.png");image_projectiles.createMaskFromColor(Color::White); // Цвет прозрачностиFont font; //ТекстText text_stats("", font, 14);font.loadFromFile("fonts/font_SEVENET_7_CYR.ttf");text_stats.setColor(Color::White);///Вектор проджектайловstd::vector<*projectiles> projectiles;// Объект класса playerplayer p(image_player, "Player", 48, 48, 0, 0, 300, 400);/*1. Image - Изображение2. Name - Имя игрока3. size_player_x - Размер по Х4. size_player_y - Размер по Y5. origin_player_x - Начало первого спрайта по Х6. origin_player_y - Начало первого спрайта по Y7. position_x - Начальная позиция спрайта по Y8. position_y - Начальная позиция спрайта по Y*/while (window.isOpen()){//float time = clock.getElapsedTime().asMicroseconds(); //Лучше использовать милисикунды. Шаг в микросекунду ты никогда не поймаешь.float time = clock.getElapsedTime().asMiliseconds();clock.restart();time = time / 800;p.update(time); //Обновляем класс player с течением времениEvent event;while (window.pollEvent(event)){if (event.type == Event::Closed)window.close();if (event.type == Event::MouseButtonPressed) //Нажата клавиша мыши //Если ты смотришь в сторону ивента то ты должен хватать его в пуле, а не в остатке;if (event.key.code == Mouse::Left) //Левая{float speed = 1;Vector2f velocity = vectorNormalize(Vector2f(Mouse::getPosition(window).x, Mouse::getPosition(window).y), p.sprite.getPosition());velocity.x *= speed;velocity.y *= speed;projectiles.push_back(new projectiles(image_projectiles, 32, 32, p.sprite.getPosition(), velocity));}}for (auto it : projectiles){if (it != nullptr)//Если ссылка валидна{it->update(time);if (it->duration <= 0)//Если время жизни подошло к концу{delete it;//Удаляем обьектprojectiles.erase(it);//Удаляем ссылку на него}}else projectiles.erase(it);//Удаляем ссылку на него}window.clear(Color(128, 128, 128)); //Серый фон игрыfor (auto it : projectiles){if (it != nullptr)//Если ссылка валидна{window.draw(it->sprite_projectiles);//Рисуем спрайт}else projectiles.erase(it);//Удаляем ссылку на него}window.draw(p.sprite); //Рисуем персонажаif (Keyboard::isKeyPressed(Keyboard::Tab)) //Окно информации на "Tab"{ostringstream name_player; name_player << p.name_player; //Имя персонажаostringstream health_player; health_player << (int)p.health; //Здоровье персонажаostringstream level_player; level_player << p.level; //Уровень персонажаostringstream exp_player; exp_player << p.level_experience; //Опыт персонажаostringstream position_player_x; position_player_x << (int)p.x; //Координата Xostringstream position_player_y; position_player_y << (int)p.y; //Координата Yostringstream free_point_player; free_point_player << p.free_point; //Доступные очки характеристикtext_stats.setString("Имя: " + name_player.str() + "\n\nЗдоровье: " + health_player.str() + "\n\nУровень: " + level_player.str() + "\nОпыт: " + exp_player.str() + "\n\nКоординаты:\nX: " + position_player_x.str() + "\nY: " + position_player_y.str() + "\n\nОчки характеристик: " + free_point_player.str());text_stats.setPosition(position_window.y + 20, position_window.x + 20);window.draw(rectangle_tab); //Выводим окно при нажатии "Tab"window.draw(text_stats); //Выводим текст при нажатии "Tab"}window.display();}return 0;}Спасибо, сделал правда немного по другому
Появилось время посидеть, посмотреть, подумать. Возникла проблема, сижу уже второй день – скорость снаряда. Если нажимать прямо в спрайте персонажа скорость огромная, чем дальше, тем меньше. У краев окна снаряд движется как черепаха, у персонажа – мгновенно. Я так понимаю, что проблема в расчете вектора. Как это можно исправить?Возможно беда с нормализацией вектора. Когда мышь находится по отношению к спрайту на расстоянии меньше одного юнита может произойти подобное из за того что мы находим скаляр для приведения длинны вектора к 1 юниту.
Попробуй заменить мой метод нормализации на этотC++1234567891011121314151617181920212223float vectorMagnitude(const sf::Vector2f& vector){return std::sqrt(vector.x * vector.x + vector.y * vector.y);}sf::Vector2f getVectorMovedOrigin(const sf::Vector2f& targetVector, const sf::Vector2f& originVector){return sf::Vector2f(targetVector - originVector);}sf::Vector2f vectorNormalize(const sf::Vector2f& vector, const sf::Vector2f origin)//1 аргумент - позицию курсора, 2 аргумент - центр спрайта{return vectorNormalize(getVectorMovedOrigin(vector, origin));}sf::Vector2f vectorNormalize(const sf::Vector2f& vector)//Если вектор уже имеет начало кординат в нужном месте{float magnitude = vectorMagnitude(vector);if (magnitude == 0.0f)return sf::Vector2f(1.0f, 0.0f);float distInv = 1.0f / magnitude;return sf::Vector2f(vector.x * distInv, vector.y * distInv);} -
АвторСообщения
Для ответа в этой теме необходимо авторизоваться.