SFML и C++ Уроки \ Разработка игр › Форумы › SFML Graphics › игра "Space Invaders". Нужна помощь.
В этой теме 6 ответов, 2 участника, последнее обновление Павел Букреев 7 года/лет, 7 мес. назад.
-
АвторСообщения
-
Не так давно начал прогать на C++/C#, не без помощи универа 😀
В общем требуется сделать игру наподобие Space Invaders.
Игра без карты, поэтому надобность в Tiled Map Editor отпадает, игрок просто не может зайти за координаты, а цели при достижении определённых координат просто опускаются вниз и идут в обратную сторону.
Всё делал по примеру из видеоуроков, но пули почему-то не получается сделать, они даже не появляются нигде :C
И ещё наверное было бы неплохо цели как-то вручную в список занести, тк для каждой обновление действительно не совсем удобно и громоздко писать. Сейчас я для теста просто 8 штук сделал, а так их более 20 планируется сделать.
На данный момент имеется прямоугольный черный экран с анимированным корабликом с одной степенью свободы (управляется стрелочками) и 8 целей, которые спускаются зигзагом вниз (пока что бесконечно).Фото того, что имеется
За код строго не судите, я только начал С:
Много закоменчено, много лишнего. Большинство скопировано с примеров из видеоуроков.
Прилагаю код из main.cpp:C++123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230#include <SFML/Graphics.hpp>#include <iostream>#include <sstream>#include <vector>#include <list>using namespace sf;//включаем пространство имен sf, чтобы постоянно не писать sf::using namespace std;class Entity {public:float dx, dy, x, y, sx, sy, speed, moveTimer;//добавили переменную таймер для будущих целейint w, h, health;bool life, isMove;Texture texture;Sprite sprite;String name;//враги могут быть разные, мы не будем делать другой класс для врага.всего лишь различим врагов по имени и дадим каждому свое действие в update в зависимости от имениEntity(Image &image, float X, float Y, int W, int H, String Name, float SX, float SY){x = X; y = Y; w = W; h = H; name = Name; moveTimer = 0;sx = SX; sy = SY;speed = 0; health = 100; dx = 0; dy = 0;life = true; isMove = false;texture.loadFromImage(image);sprite.setTexture(texture);sprite.setOrigin(w / 2, h / 2);}FloatRect getRect(){return FloatRect(sx, sy, w, h);}virtual void update(float time) = 0;//все потомки переопределяют эту ф-цию};class Player :public Entity {public:enum { left, right, up, down, stay } state;//добавляем тип перечисления - состояние объектаint playerScore;//эта переменная может быть только у игрокаbool isShoot;Player(Image &image, float X, float Y, int W, int H, String Name, float SX, float SY) :Entity(image, X, Y, W, H, Name, SX, SY){playerScore= isShoot = 0; state = stay;if (name == "Player1"){sprite.setTextureRect(IntRect(x, y, w, h));}}void checkCollision(float Dx)//ф ция проверки столкновений с картой{if ((dx < 0) & (sx <= 3)) { sx = 3; state = stay; }if ((dx > 0) & (sx >= 477 - 90)) { sx = 477 - 90; state = stay; }}void control(){if (Keyboard::isKeyPressed){//если нажата клавишаif (Keyboard::isKeyPressed(Keyboard::Left)) { state = left; speed = 0.5; sprite.setTextureRect(IntRect(0, 0, 90, 30)); }//первая координата Х отрицательна =>идём влевоif (Keyboard::isKeyPressed(Keyboard::Right)) { state = right; speed = 0.5; sprite.setTextureRect(IntRect(180, 0, 90, 30)); }if (Keyboard::isKeyPressed(Keyboard::Right) & Keyboard::isKeyPressed(Keyboard::Left)) { state=stay; sprite.setTextureRect(IntRect(90, 0, 90, 30)); } //первая координата Х положительна =>идём вправоif (Keyboard::isKeyPressed(Keyboard::Space)) {isShoot = true;}}else { state = stay;; }}void update(float time) //функция "оживления" объекта класса. update - обновление. принимает в себя время SFML , вследствие чего работает бесконечно, давая персонажу движение.{control();//функция управления персонажемswitch (state)//реализуем поведение в зависимости от направления. (каждая цифра соответствует направлению){case right:dx = speed; break;//состояние идти вправоcase left:dx = -speed; break;//состояние идти влево// case up: break;//будет состояние поднятия наверх (например по лестнице)// case down: dx = 0; break;//будет состояние во время спуска персонажа (например по лестнице)case stay: dx = 0; sprite.setTextureRect(IntRect(x, y, w, h)); break;//и здесь тоже}if (!isMove){ state = stay; }sx += dx*time;checkCollision(dx);//обрабатываем столкновение по Хsprite.setPosition(sx + (w / 2), sy+(h / 2) );if (health <= 0){ life = false; }}};class Enemy :public Entity{public:enum { left, right, up, down, stay } state;float way, xmax, xmin;Enemy(Image &image, float X, float Y, int W, int H, String Name, float SX, float SY) :Entity(image, X, Y, W, H, Name, SX, SY){state = right;way = 300;xmin = sx;xmax = sx + way;if (name == "Enemy"){sprite.setTextureRect(IntRect(x, y, w, h));speed = dx = 0.1;//даем скорость.этот объект всегда двигается}}void wayCheck()//ф ция проверки столкновений с картой{if ((dx < 0) & (sx <= xmin)) { state = right; sy +=10; }if ((dx > 0) & (sx >= xmax)) { state = left; sy +=10; }}void update(float time) //функция "оживления" объекта класса. update - обновление. принимает в себя время SFML , вследствие чего работает бесконечно, давая персонажу движение.{ wayCheck();switch (state)//реализуем поведение в зависимости от направления. (каждая цифра соответствует направлению){case right:dx = speed; break;//состояние идти вправоcase left:dx = -speed; break;//состояние идти влево}sx += dx*time;sprite.setPosition(sx + (w / 2), sy + (h / 2));if (health <= 0){ life = false; }}};class Bullet :public Entity{//класс пулиpublic:enum{ up, down } state;Bullet(Image &image, float X, float Y, int W, int H, String Name, float SX, float SY) :Entity(image, X, Y, W, H, Name, SX, SY){//всё так же, только взяли в конце состояние игрока (int dir)//obj = lvl.GetObjects("solid");//инициализируем .получаем нужные объекты для взаимодействия пули с картойx = X;y = Y;sx = SX, sy = SY;state = up;//direction = dir;speed = 0.8;w = W, h = H;;life = true;//выше инициализация в конструкторе}void update(float time){switch (state){case up: dy = -speed; break;//интовое значение state = upcase down: dy = speed ; break;//интовое значение state = down}//sx += dx*time;//само движение пули по хsy += dy*time;//по уif ((sy <= 0)|(sy>=640)) life = false;sprite.setPosition(sx + w / 2, sy + h / 2);//задается позицию пуле}};int main(){RenderWindow window(sf::VideoMode(480, 640), "GAME");float CurrentFrame = 0;//хранит текущий кадрClock clock;//Player s("ship.png", 90, 0, 90, 30, 240, 600);//создаем объект p класса player,задаем "hero.png" как имя файла+расширение, далее координата Х,У, ширина, высота.Image shipImage;shipImage.loadFromFile("textures/ship.png");Player s(shipImage, 90, 0, 90, 30, "Player1", 240, 600);//объект класса игрокаImage BulletImage;//изображение для пулиBulletImage.loadFromFile("textures/bullet_test.png");//загрузили картинку в объект изображенияImage enemyImage;enemyImage.loadFromFile("textures/enemy_test.png");Enemy e1(enemyImage, 0, 0, 30, 30, "Enemy", 15, 20);Enemy e2(enemyImage, 0, 0, 30, 30, "Enemy", 15+30+10, 20);Enemy e3(enemyImage, 0, 0, 30, 30, "Enemy", 15+60+20, 20);Enemy e4(enemyImage, 0, 0, 30, 30, "Enemy", 15+90+30, 20);Enemy e5(enemyImage, 0, 0, 30, 30, "Enemy", 15, 20 + 30 + 15);Enemy e6(enemyImage, 0, 0, 30, 30, "Enemy", 15 + 30 + 10, 20+30+15);Enemy e7(enemyImage, 0, 0, 30, 30, "Enemy", 15 + 60 + 20, 20 + 30 + 15);Enemy e8(enemyImage, 0, 0, 30, 30, "Enemy", 15 + 90 + 30, 20 + 30 + 15);list<Entity*> entities;list<Entity*>::iterator it;list<Entity*>::iterator it2;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 (s.isShoot == true) { s.isShoot = false; entities.push_back(new Bullet(BulletImage, 0, 0, 10, 10, "Bullet", s.sx, s.sy)); }//если выстрелили, то появляется пуля. enum передаем как intif (event.type == sf::Event::Closed)window.close();if (event.type == sf::Event::KeyPressed){if (event.key.code == sf::Keyboard::Space){entities.push_back(new Bullet(BulletImage, 0, 0, 10, 10, "Bullet", s.sx, s.sy));}}}// if (Keyboard::isKeyPressed(Keyboard::Left) || Keyboard::isKeyPressed(Keyboard::Right))// {// if (Keyboard::isKeyPressed(Keyboard::Left)) { s.dir = 3; s.speed = 1; s.sprite.setTextureRect(IntRect(0, 0, 90, 30)); }//первая координата Х отрицательна =>идём влево// if (Keyboard::isKeyPressed(Keyboard::Right)) { s.dir = 1; s.speed = 1; s.sprite.setTextureRect(IntRect(180, 0, 90, 30)); }// if (Keyboard::isKeyPressed(Keyboard::Right) & Keyboard::isKeyPressed(Keyboard::Left)) { s.speed = 0; s.sprite.setTextureRect(IntRect(90, 0, 90, 30)); } //первая координата Х положительна =>идём вправо// }// else s.sprite.setTextureRect(IntRect(90, 0, 90, 30));for (it = entities.begin(); it != entities.end();)//говорим что проходимся от начала до конца{Entity *b = *it;//для удобства, чтобы не писать (*it)->b->update(time);//вызываем ф-цию update для всех объектов (по сути для тех, кто жив)if (b->life == false) { it = entities.erase(it); delete b; }// если этот объект мертв, то удаляем егоelse it++;//и идем курсором (итератором) к след объекту. так делаем со всеми объектами списка}e1.update(time); e2.update(time); e3.update(time); e4.update(time); e5.update(time); e6.update(time); e7.update(time); e8.update(time);s.update(time);//оживляем объект p класса Player с помощью времени sfml, передавая время в качестве параметра функции update. благодаря этому персонаж может двигатьсяwindow.clear();window.draw(s.sprite);//рисуем спрайт объекта p класса playerwindow.draw(e1.sprite); window.draw(e2.sprite); window.draw(e3.sprite); window.draw(e4.sprite);window.draw(e5.sprite); window.draw(e6.sprite); window.draw(e7.sprite); window.draw(e8.sprite);window.display();}return 0;}я не вижу window.draw для пуль и для entities в целом.
О да, точно. Потерял 😀
Спасибо.
Хм. Если клавишу огня зажать, то будет стрелять бесконечной очередью. Если зажать в движении, то будет стрелять когда меняется направление. Как то не солидно. Надо наверно через определённое время опрашивать нажатость кнопки (чтобы стреляло допустим не чаще раза в секунду).переноси событие в event, либо через время стреляй.
можно поподробнее про это?
В общем классы вынес в отдельный файл, чтоб не мешали.
Никак не могу придумать нормальные алгоритм для задержки выстрелов игрока и алгоритм для огня целей по игроку :/Код main.cpp и Entities.h
C++123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596#include <SFML/Graphics.hpp>#include <iostream>#include <sstream>#include <vector>#include <list>#include "Entities.h"int main(){RenderWindow window(sf::VideoMode(480, 640), "GAME");float CurrentFrame = 0;//хранит текущий кадрlist<Entity*> entities;list<Entity*>::iterator it; //итератор для "пробегания по списку"list<Entity*>::iterator it2; //второй итератор, для реализации модели столкновений элементов списка//Player s("ship.png", 90, 0, 90, 30, 240, 600);//создаем объект p класса player,задаем "hero.png" как имя файла+расширение, далее координата Х,У, ширина, высота.Image shipImage;shipImage.loadFromFile("textures/ship.png");Player s(shipImage, 90, 0, 90, 30, "Player", 240, 600);//объект класса игрокаImage BulletImage;//изображение для пулиBulletImage.loadFromFile("textures/bullet_test.png");//загрузили картинку в объект изображенияImage enemyImage;enemyImage.loadFromFile("textures/enemy_test.png");const int n = 4; const int m = 8;int i, j;for (i = 0; i < n; i++){for (j = 0; j < m; j++){entities.push_back(new Enemy(enemyImage, 0, 0, 30, 30, "Enemy", 15 + j * 30 + j * 15, 20 + i * 30 + i * 15));}}float bullettimer = 0;Clock clock;while (window.isOpen()){float time = clock.getElapsedTime().asMicroseconds();clock.restart();time = time / 800;//bullettimer += time;sf::Event event;while (window.pollEvent(event)){if (event.type == Event::Closed)window.close();// if (s.isShoot == true) { s.isShoot = false; entities.push_back(new Bullet(BulletImage, 0, 0, 10, 10, "Bullet", s.sx+s.w/2-5, s.sy, 0)); }//если выстрелили, то появляется пуля. enum передаем как intif (event.type == Event::KeyPressed){if (event.key.code == Keyboard::Space){entities.push_back(new Bullet(BulletImage, 0, 0, 10, 10, "Bullet", s.sx + s.w / 2 - 5, s.sy, 0));}}}s.update(time);for (it = entities.begin(); it != entities.end();)//говорим что проходимся от начала до конца{Entity *b = *it;//для удобства, чтобы не писать (*it)->b->update(time);//вызываем ф-цию update для всех объектов (по сути для тех, кто жив)if (b->life == false) { it = entities.erase(it); delete b; }// если этот объект мертв, то удаляем егоelse it++;//и идем курсором (итератором) к след объекту. так делаем со всеми объектами списка}for (it = entities.begin(); it != entities.end(); it++)//проходимся по эл-там списка{for (it2 = entities.begin(); it2 != entities.end(); it2++){if ((*it)->getRect() != (*it2)->getRect())//при этом это должны быть разные прямоугольникиif (((*it)->getRect().intersects((*it2)->getRect())) && ((*it)->name == "Enemy") && ((*it2)->name == "Bullet"))//если столкнулись два объекта и они враги{(*it)->health -= 25;//меняем направление движения врага//(*it)->sprite.scale(-1, 1);//отражаем спрайт по горизонтали(*it2)->life = false;}}}for (it = entities.begin(); it != entities.end(); it++){if (((*it)->name == "Enemy") && ((*it)->life == true)) { int a = rand() % 1000; if (a > 99 && a < 101) entities.push_back(new Bullet(BulletImage, 0, 0, 10, 10, "EnemyBullet", (*it)->sx + s.w / 2 - 5, (*it)->sy, 1)); }}window.clear();for (it = entities.begin(); it != entities.end(); it++){window.draw((*it)->sprite);}window.draw(s.sprite);//рисуем спрайт объекта p класса playerwindow.display();}return 0;}C++123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152#define ENTITIES_H#include <SFML/Graphics.hpp>#include <iostream>#include <sstream>using namespace sf;//включаем пространство имен sf, чтобы постоянно не писать sf::using namespace std;class Entity {public:float dx, dy, x, y, sx, sy, speed, moveTimer;//добавили переменную таймер для будущих целейint w, h, health;bool life, isMove;Texture texture;Sprite sprite;String name;//враги могут быть разные, мы не будем делать другой класс для врага.всего лишь различим врагов по имени и дадим каждому свое действие в update в зависимости от имениEntity(Image &image, float X, float Y, int W, int H, String Name, float SX, float SY){x = X; y = Y; w = W; h = H; name = Name; moveTimer = 0;sx = SX; sy = SY;speed = 0; health = 100; dx = 0; dy = 0;life = true; isMove = false;texture.loadFromImage(image);sprite.setTexture(texture);sprite.setOrigin(w / 2, h / 2);}FloatRect getRect(){return FloatRect(sx, sy, w, h);}virtual void update(float time) = 0;//все потомки переопределяют эту ф-цию};class Player :public Entity {public:enum { left, right, stay } state;//добавляем тип перечисления - состояние объектаint playerScore;//эта переменная может быть только у игрокаbool isShoot;Player(Image &image, float X, float Y, int W, int H, String Name, float SX, float SY) :Entity(image, X, Y, W, H, Name, SX, SY){playerScore = isShoot = 0; state = stay;if (name == "Player"){sprite.setTextureRect(IntRect(x, y, w, h));}}void checkCollision(float Dx)//ф ция проверки столкновений с картой{if ((dx < 0) & (sx <= 3)) { sx = 3; state = stay; }if ((dx > 0) & (sx >= 477 - 90)) { sx = 477 - 90; state = stay; }}void control(){if (Keyboard::isKeyPressed){//если нажата клавишаif (Keyboard::isKeyPressed(Keyboard::Left)) { state = left; speed = 0.5; sprite.setTextureRect(IntRect(0, 0, 90, 30)); }//первая координата Х отрицательна =>идём влевоif (Keyboard::isKeyPressed(Keyboard::Right)) { state = right; speed = 0.5; sprite.setTextureRect(IntRect(180, 0, 90, 30)); }if (Keyboard::isKeyPressed(Keyboard::Right) & Keyboard::isKeyPressed(Keyboard::Left)) { state = stay; sprite.setTextureRect(IntRect(90, 0, 90, 30)); } //первая координата Х положительна =>идём вправоif (Keyboard::isKeyPressed(Keyboard::Space)) {isShoot = true;}}else { state = stay;; }}void update(float time) //функция "оживления" объекта класса. update - обновление. принимает в себя время SFML , вследствие чего работает бесконечно, давая персонажу движение.{control();//функция управления персонажемswitch (state)//реализуем поведение в зависимости от направления. (каждая цифра соответствует направлению){case right:dx = speed; break;//состояние идти вправоcase left:dx = -speed; break;//состояние идти влево// case up: break;//будет состояние поднятия наверх (например по лестнице)// case down: dx = 0; break;//будет состояние во время спуска персонажа (например по лестнице)case stay: dx = 0; sprite.setTextureRect(IntRect(x, y, w, h)); break;//и здесь тоже}if (!isMove){ state = stay; }sx += dx*time;checkCollision(dx);//обрабатываем столкновение по Хsprite.setPosition(sx + (w / 2), sy + (h / 2));if (health <= 0){ life = false; }}};class Enemy :public Entity{public:enum { left, right, up, down, stay } state;float way, xmax, xmin;Enemy(Image &image, float X, float Y, int W, int H, String Name, float SX, float SY) :Entity(image, X, Y, W, H, Name, SX, SY){state = right;way = 140;xmin = sx;xmax = sx + way;if (name == "Enemy"){sprite.setTextureRect(IntRect(x, y, w, h));speed = dx = 0.1;//даем скорость.этот объект всегда двигается}}void wayCheck()//ф ция проверки столкновений с картой{if ((dx < 0) & (sx <= xmin)) { state = right; sy += 10; }if ((dx > 0) & (sx >= xmax)) { state = left; sy += 10; }}void update(float time) //функция "оживления" объекта класса. update - обновление. принимает в себя время SFML , вследствие чего работает бесконечно, давая персонажу движение.{wayCheck();switch (state)//реализуем поведение в зависимости от направления. (каждая цифра соответствует направлению){case right:dx = speed; break;//состояние идти вправоcase left:dx = -speed; break;//состояние идти влево}sx += dx*time;sprite.setPosition(sx + (w / 2), sy + (h / 2));if (health <= 0){ life = false; }}};class Bullet :public Entity{//класс пулиpublic://enum { up, down } state;int direction;Bullet(Image &image, float X, float Y, int W, int H, String Name, float SX, float SY, int dir) :Entity(image, X, Y, W, H, Name, SX, SY){//всё так же, только взяли в конце состояние игрока (int dir)//obj = lvl.GetObjects("solid");//инициализируем .получаем нужные объекты для взаимодействия пули с картоx = X; y = Y;sx = SX, sy = SY;direction = dir;//state = up;speed = 1;w = W, h = H;;life = true;//выше инициализация в конструкторе}void update(float time){switch (direction){case 0: dy = -speed; break;//интовое значение state = upcase 1: dy = speed; break;//интовое значение state = down}//state = up;//sx += dx*time;//само движение пули по хsy += dy*time;//по уif ((sy <= 0) | (sy >= 640)) life = false;sprite.setPosition(sx + w / 2, sy + h / 2);//задается позицию пуле}};Вложения:
You must be logged in to view attached files.покопайся на форуме в поиске. был такой вопрос уже про задержку выстрела
-
АвторСообщения
Для ответа в этой теме необходимо авторизоваться.