SFML и C++ Уроки \ Разработка игр › Форумы › SFML Graphics › при перетаскивании окна пропадают монстры и игрок
В этой теме 4 ответа, 2 участника, последнее обновление Serob 6 года/лет, 10 мес. назад.
-
АвторСообщения
-
при перетаскивании окна (за полоску где крестик, свернуть развернуть окно) пропадают монстры и игрок. Пропадание рандомное иногда (чаще) только монстры, иногда пропадают все монстры и игрок. текстура на месте без изменений.
Что за глюк и как его победить?
ОС WIN 10, VC professional 2017
C++123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365#include <SFML/Graphics.hpp>#include <iostream>#include <sstream>#include <vector>#include <list>#include "level.h"#include "View.h"#include "tinyxml\tinyxml.h"using namespace :: std;using namespace :: sf;/////////////////////////////// общий класс игроков монстров ///////////////////////////class Entity{public:std :: vector <Object> obj;float dx, dy, x, y, speed, moveTimer;int w, h, health;bool life, isMove, onGround;Texture texture;Sprite sprite;String name;Entity(Image &image, String Name, float X, float Y, int W, int H, int N){x = X; y = Y; w = W; h = H; name = Name;dx = 0; dy = 0; life = true;speed = 0; moveTimer = 0;health = N; // переменная N разное колличество жизней у монстров и игроковisMove = false; onGround = false;texture.loadFromImage(image);sprite.setTexture(texture);sprite.setOrigin(w / 2, h / 2);}FloatRect getRect(){return FloatRect(x, y, w, h);}virtual void update(float time) = 0;};///////////////////////////////////////////////////////////////////////////////////////////КЛАСС ИГРОКА////////////////////////class Player :public Entity{public:enum { left, right, up, down, jump, stay } state;//добавляем тип перечисления - состояние объектаint playerScore;//эта переменная может быть только у игрокаPlayer(Image &image, String Name, Level &lev, float X, float Y, int W, int H, int N) :Entity(image, Name, X, Y, W, H, N){playerScore = 0; state = stay; obj = lev.GetAllObjects();if (name == "Player"){sprite.setTextureRect(IntRect(32, 0, w, h));}}void control() {if (life == true){if (Keyboard::isKeyPressed) {//если нажата клавишаif (Keyboard::isKeyPressed(Keyboard::Left)) //а именно левая{state = left; speed = 0.1;}if (Keyboard::isKeyPressed(Keyboard::Right)) {state = right; speed = 0.1;}if ((Keyboard::isKeyPressed(Keyboard::Up)) && (onGround)) {//если нажата клавиша вверх и мы на земле, то можем прыгатьstate = jump; dy = -0.6; onGround = false;//увеличил высоту прыжка}if (Keyboard::isKeyPressed(Keyboard::Down)) {state = down;}}}}void checkCollisionWithMap(float Dx, float Dy) //ф ция проверки столкновений с картой{for (int i = 0; i < obj.size(); i++)//проходимся по объектамif (getRect().intersects(obj[i].rect))//проверяем пересечение игрока с объектом{if (obj[i].name == "solid")//если встретили препятствие{if (Dy > 0) { y = obj[i].rect.top - h; dy = 0; onGround = true; }if (Dy < 0) { y = obj[i].rect.top + obj[i].rect.height; dy = 0; }if (Dx > 0) { x = obj[i].rect.left - w; }if (Dx < 0) { x = obj[i].rect.left + obj[i].rect.width; }}else { onGround = false; }}}void update(float time){control();//функция управления персонажемswitch (state)//тут делаются различные действия в зависимости от состояния{case right:dx = speed; break;//состояние идти вправоcase left:dx = -speed; break;//состояние идти влевоcase up: break;//будет состояние поднятия наверх (например по лестнице)case down: dx = 0; break;//будет состояние во время спуска персонажа (например по лестнице)case stay: break;//и здесь тоже}x += dx*time;checkCollisionWithMap(dx, 0);//обрабатываем столкновение по Хy += dy*time;checkCollisionWithMap(0, dy);//обрабатываем столкновение по Ysprite.setPosition(x + w / 2, y + h / 2); //задаем позицию спрайта в место его центраif (!isMove){speed = 0;}if (health <= 0) { life = false; }//этим методом будем забирать координаты для камерыif (life == true) { getPlayerCoordinateForView(x, y); }/////заставляем прыгатьdy = dy + 0.0015*time;}};//////////////////////////// монстр ///////////////////////////////////class Monstr :public Entity{public:Monstr(Image &image, String Name, Level &lvl, float X, float Y, int W, int H, int N) :Entity(image, Name, X, Y, W, H, N){obj = lvl.GetObjects("solid"); //инициализируем.получаем нужные объекты для взаимодействия врага с картойif (name == "Monstr"){sprite.setTextureRect(IntRect(360, 22, 20, 20)); // координаты в спрайте монстра и его размерdx = 0.05;//даем скорость.этот объект всегда двигается}}void checkCollisionWithMapMonstr(float Dx, float Dy)//ф ция проверки столкновений с картой{for (int i = 0; i < obj.size(); i++) //проходимся по объектамif (getRect().intersects(obj[i].rect)) //проверяем пересечение игрока с объектом{if (obj[i].name == "solid") //если встретили препятствие (объект с именем solid){if (Dy > 0) { y = obj[i].rect.top - h; dy = 0; onGround = true; }if (Dy < 0) { y = obj[i].rect.top + obj[i].rect.height; dy = 0; }if (Dx > 0) { x = obj[i].rect.left - w; dx = -0.1; sprite.scale(-1, 1); }if (Dx < 0) { x = obj[i].rect.left + obj[i].rect.width; dx = 0.1; sprite.scale(-1, 1); }}else { onGround = false; }}}void update(float time){if (name == "Monstr") {//для персонажа с таким именем логика будет такой//moveTimer += time; if (moveTimer>3000){ dx *= -1; moveTimer = 0; }//меняет направление примерно каждые 3 секx += dx*time;checkCollisionWithMapMonstr(dx, 0);//обрабатываем столкновение по Хy += dy*time;checkCollisionWithMapMonstr(0, dy);//обрабатываем столкновение по Yif (!isMove) speed = 0;sprite.setPosition(x + w / 2, y + h / 2); //задаем позицию спрайта в место его центраif (health <= 0) {life = false; }dy = dy + 0.0015*time;//делаем притяжение к земле}}};////////////////////////////////class hiMonstr :public Entity{public:hiMonstr(Image &image, String Name, Level &lvl, float X, float Y, int W, int H, int N) : Entity (image, Name, X, Y, W, H, N){obj = lvl.GetObjects("solid"); //инициализируем.получаем нужные объекты для взаимодействия врага с картойif (name == "hiMonstr"){sprite.setTextureRect(IntRect(77, 14, 59, 36)); // координаты в спрайте монстра и его размерdx = 0.01;//даем скорость.этот объект всегда двигается}}void checkCollisionWithMapMonstr(float Dx, float Dy)//ф ция проверки столкновений с картой{for (int i = 0; i < obj.size(); i++) //проходимся по объектамif (getRect().intersects(obj[i].rect)) //проверяем пересечение игрока с объектом{if (obj[i].name == "solid") //если встретили препятствие (объект с именем solid){if (Dy > 0) { y = obj[i].rect.top - h; dy = 0; onGround = true; }if (Dy < 0) { y = obj[i].rect.top + obj[i].rect.height; dy = 0; }if (Dx > 0) { x = obj[i].rect.left - w; dx = -0.1; sprite.scale(-1, 1); }if (Dx < 0) { x = obj[i].rect.left + obj[i].rect.width; dx = 0.1; sprite.scale(-1, 1); }}else { onGround = false; }}}void update(float time){if (name == "hiMonstr") {//для персонажа с таким именем логика будет такой//moveTimer += time; if (moveTimer>3000){ dx *= -1; moveTimer = 0; }//меняет направление примерно каждые 3 секx += dx*time;checkCollisionWithMapMonstr(dx, 0);//обрабатываем столкновение по Хy += dy*time;checkCollisionWithMapMonstr(0, dy);//обрабатываем столкновение по Y//if (!isMove) speed = 0;sprite.setPosition(x + w / 2, y + h / 2); //задаем позицию спрайта в место его центраif (health <= 0) { life = false; }dy = dy + 0.0015*time;//делаем притяжение к земле}}};int main(){RenderWindow window(VideoMode(800, 600), "HER BAM");view.reset(sf::FloatRect(0, 0, 800, 600)); //размер "вида" камеры при создании объекта вида камеры. (потом можем менять как хотим) Что то типа инициализации.Level lvl;lvl.LoadFromFile("images/map.tmx");std::list <Entity*> entilist;std::list <Entity*>::iterator it;std::list <Entity*>::iterator it2; //второй итератор.для взаимодействия между объектами спискаstd::vector <Object> c = lvl.GetObjects("Monstr");std::vector <Object> e = lvl.GetObjects("hiMonstr");//std::vector <Object> player = lvl.GetObjects("Player");Image heroImage;heroImage.loadFromFile("images/sonic2.png");Image MonstrImage;MonstrImage.loadFromFile("images/02810.png");Image hiMonstrImage;hiMonstrImage.loadFromFile("images/02810.png");//easyMonstrImage.createMaskFromColor(Color(255, 0, 0));//сделали маску по цвету.но лучше изначально иметь прозрачную картинкуfor (int t = 0; t < c.size(); t++)//проходимся по элементам этого вектора(а именно по врагам)entilist.push_back(new Monstr(MonstrImage, "Monstr", lvl, c[t].rect.left, c[t].rect.top, 20, 20, 1));//и закидываем в список всех наших врагов с картыfor (int i = 0; i < e.size(); i++)entilist.push_back(new hiMonstr(hiMonstrImage, "hiMonstr", lvl, e[i].rect.left, e[i].rect.top, 57, 36, 25));//и закидываем в список всех наших врагов с карты////объект игрока на нашей карте.задаем координаты игроку в начале при помощи него//передаем координаты прямоугольника player из карты в координаты нашего игрокаObject player = lvl.GetObject("Player");Player p(heroImage, "Player", lvl, player.rect.left, player.rect.top, 32, 32, 50);Clock clock; // функция времениwhile (window.isOpen()){float time = clock.getElapsedTime().asMicroseconds();clock.restart();time = time / 500;// обязательная фигня для создания окна часть 1Event event;while (window.pollEvent(event)){if (event.type == Event::Closed)window.close();}for (it = entilist.begin(); it != entilist.end();)//говорим что проходимся от начала до конца{Entity *b = *it;//для удобства, чтобы не писать (*it)->b->update(time);//вызываем ф-цию update для всех объектов (по сути для тех, кто жив)if (b->life == false) { it = entilist.erase(it); delete b; }// если этот объект мертв, то удаляем егоelse it++;//и идем курсором (итератором) к след объекту. так делаем со всеми объектами списка}for (it = entilist.begin(); it != entilist.end(); it++)//проходимся по эл-там списка{//если прямоугольник спрайта объекта пересекается с игроком//и при этом имя объекта EasyEnemy,то..if (((*it)->name == "Monstr") && (*it)->getRect().intersects(p.getRect())){std::cout << "intersection with Monstr, p.dy: " << p.dy <<", p.onGround: " << p.onGround << ", p.health: " << p.health << std::endl;if ((p.dy > 0) && (p.onGround == false)) { (*it)->dx = 0; p.dy = -0.2; (*it)->health = 0; }//если прыгнули на врага,то даем врагу скорость 0,отпрыгиваем от него чуть вверх,даем ему здоровье 0//иначе враг подошел к нам сбоку и нанес уронelse{p.health -= 10;}}// логика второго монстраif ((*it)->name == "hiMonstr" && (*it)->getRect().intersects(p.getRect())){std::cout << "intersection with hiMonstr, p.dy: " << p.dy <<", p.onGround: " << p.onGround << ", p.health: " << p.health << std::endl;if ((p.dy > 0) && (p.onGround == false)) { (*it)->dx = 0; p.dy = -0.3; (*it)->health = 0; } //если прыгнули на врага,то даем врагу скорость 0,отпрыгиваем от него чуть вверх,даем ему здоровье 0//// тут игрок должен получить урон но нифигаelse //иначе враг подошел к нам сбоку и нанес урон{p.health -= 20;}}}p.update(time);// обновляем камеруwindow.setView(view);// обязательная фигня для создания окна часть 2window.clear(Color(77, 83, 140));//рисуем новую картуlvl.Draw(window);for (it = entilist.begin(); it != entilist.end(); it++){window.draw((*it)->sprite); //рисуем entities объекты (сейчас это только враги)}// обязательная фигня для создания окна часть 3window.draw(p.sprite); // переменная обновления Playerwindow.display();}return 0;}дополнение к вышесказанному. пропадают “движущиеся” объекты которые “видимы” в окне. монстры за пределами “окна” не исчезают.
Для избегания таких вещей делают перемещение обьектов привязанно не ко времени, а к некому игровому тику который срабатывает каждый интервал времени.
Для привязки всего к тику придётся переписывать половину кода. Для себя я набросал два класса которые отвечают за подобную механику обновления обьектов. Можешь спокойно использовать и модифицировать их как хочешь. Учти что:
1. Твой код должен содержатся в классе – наследнике от State и в int main() должна быть обьявлена конструкция:
IApp app(window);
app.run(new ИмяНовогоКлассаState(&window))
2. onHandle(sf::Event event) будет вызыватся на каждый ивент содержащийся в пуле окна
3. onUpdate(sf::Time delta) и onDraw(sf::RenderWindow window) не всегда будут вызыватся одновременно за один цикл (если отрисовка займёт слишком много времени следующее обновление будет вызвано столько раз, сколько излишнего времени заняла отрисовка и наоборот, если отрисовка проходит быстрее чем временной шаг то будет снова вызыватся отрисовка пока не пройдт времени больше чем временной шаг).
Ну, думаю и ежу понятно что код инициализации должен быть в конструкторе класса, логика в onUpdate а отрисовка в onDraw. Если нужно перехватить вводимый игроком текст или ещё что-то связаное с sf::Event – ловим в onHandle.
Узлы, добавленые в addNode(Node * node), будут иметь приоритет заданый переменной Z и всегда приоритет ниже Parent(Т.е. класса в котором он был добавлен)
Ну и собственно классы(Разбиты на хидеры и .cpp, как и положено). Кстати, если сможешь понять как можно использовать класс-узел (Node) это сильно сможет облегчить тебе жизнь в дальнейшем програмировании игры (В частности при помощи этого класса можно спокойно отрисовывать спрайты слоями, т.е. вначале то что должно быть на заднем плане, дальше на главном а после то что должно быть поверх главного плана)C++12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849//IApp.h#ifndef IAPP_H#define IAPP_H#include <vector>#include <SFML/Graphics.hpp>class State;//Forward declaration of State так как присутствует рекурсивное объявление/// \brief This class manages the different states of the game.class IApp{public:IApp(sf::RenderWindow& window);~IApp();/// \brief Runs the director with the passed state as starting statevoid run(State* state);/// \brief Replaces the current statevoid replaceState(State* state);/// \brief Push a new state on top of the stackvoid pushState(State* state);/// \brief Pop the top statevoid popState();/// \brief The timestep for the update functionvoid setTimestep(sf::Time timestep);private:void draw(sf::RenderWindow* window);void handle(const sf::Event& event);void update();void doPop();bool running;bool shouldPop;std::vector<State*> states;sf::RenderWindow& window;sf::Time accumulator;sf::Time timestep;sf::Clock frameClock;};#endif // IAPP_HC++123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140//IApp.cpp#include "IApp.h"#include "State.h"#include <iostream>//#include <tmx/Log.h>//using namespace tmx;IApp::IApp(sf::RenderWindow& window) : window(window), running(false), shouldPop(false), timestep(sf::milliseconds(20)), accumulator(sf::Time::Zero){}IApp::~IApp(){//LOG("deleting app", Logger::Type::Info);for(auto itr = states.begin(); itr != states.end(); itr++)delete (*itr);states.clear();}void IApp::run(State* state){pushState(state);running = true;sf::Event event;frameClock.restart();while(running){if(!shouldPop){//handlewhile(window.pollEvent(event)){handle(event);}//updateaccumulator += frameClock.getElapsedTime();while(accumulator >= timestep){update();accumulator -= timestep;}frameClock.restart();//drawwindow.clear();draw(&window);window.display();}else{doPop();frameClock.restart();}}}void IApp::setTimestep(sf::Time timestep){this->timestep = timestep;}void IApp::replaceState(State* state){//LOG("replacing state", Logger::Type::Info);//LOG("states: " + states.size(),Logger::Type::Info);popState();// store and init the new statestate->setDirector(this);state->setWindow(&window);states.insert(states.end() - 1, state);LOG("states: " + states.size(), Logger::Type::Info);}void IApp::pushState(State* state){//LOG("Pushing state", Logger::Type::Info);//LOG("states: " + states.size(), Logger::Type::Info);state->setDirector(this);state->setWindow(&window);// pause current stateif ( !states.empty() )states.back()->pause();// store and init the new statestates.push_back(state);//LOG("states: " + states.size(), Logger::Type::Info);}void IApp::doPop(){//LOG("Popping state", Logger::Type::Info);//LOG("states: " + states.size(), Logger::Type::Info);shouldPop = false;// cleanup the current stateif ( !states.empty() ){//LOG("deleting state", Logger::Type::Info);delete states.back();//LOG("state deleted", Logger::Type::Info);states.pop_back();}// resume previous stateif ( !states.empty() )states.back()->resume();else{running = false;window.close();}//LOG("states: " + states.size(), Logger::Type::Info);}void IApp::popState(){shouldPop = true;}void IApp::handle(const sf::Event& event){if(!states.empty())states.back()->handle(event);}void IApp::update(){if(!states.empty())states.back()->update(timestep);}void IApp::draw(sf::RenderWindow* window){if(!states.empty())states.back()->draw(window);}C++1234567891011121314151617181920212223242526272829303132//State.h#ifndef STATE_H#define STATE_H#include "IApp.h"//#include "ResourceManager.h"#include "Node.h"/// \brief The base class for states that can be added to a directorclass State : public Node{friend class IApp;public:/// \brief ConstructorState();virtual ~State(){};/// \brief Called when another state is pushed on top of the stack.virtual void pause(){};/// \brief Called when the state becomes the top of the stack.virtual void resume(){};protected:IApp* app;void setApp(IApp* app);private:};#endif // STATE_HC++123456789101112//State.cpp#include "State.h"State::State() : Node("State"){setZ(-1000);}void State::setApp(IApp* app){this->app= app;}C++123456789101112131415161718192021222324252627282930313233343536373839404142434445464748//Node.h#ifndef NODE_H#define NODE_H#include <SFML/Graphics.hpp>class Node{public:Node(const std::string& name = "Node");virtual ~Node();std::string getName();void setName(const std::string& name);void draw(sf::RenderWindow* window);void update(sf::Time delta);void handle(const sf::Event& event);void addNode(Node* node);void removeNode(Node* node);void setWindow(sf::RenderWindow* window);int getZ() const;void setZ(int z);Node* getParent();void setParent(Node* parentnode);std::vector<Node*>& getChildren();protected:virtual void onDraw(sf::RenderWindow* window){}virtual void onUpdate(sf::Time delta){}virtual void onHandle(const sf::Event& event){}virtual void onWindowSet(){}Node* parent;sf::RenderWindow* window;private:std::vector<Node*> children;std::string name;int z;};#endif // NODE_HC++123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110//Node.cpp#include "Node.h"#include <algorithm>//#include <tmx/Log.h>//using namespace tmx;bool sortByZValue(const Node* a, const Node* b){return (a->getZ() < b->getZ());}Node::Node(const std::string& name) : name(name), parent(NULL), z(0), window(NULL){//ctor}Node::~Node(){for(Node* node : children){//LOG("deleting node: " + node->getName(),Logger::Type::Info);delete node;}}std::string Node::getName(){return name;}void Node::setName(const std::string& name){this->name = name;}void Node::setZ(int z){this->z = z;}int Node::getZ() const{return z;}void Node::setWindow(sf::RenderWindow* window){this->window = window;for(Node* node : children)node->setWindow(window);onWindowSet();}void Node::draw(sf::RenderWindow* window){onDraw(window);for(Node* node : children)node->draw(window);}void Node::update(sf::Time delta){onUpdate(delta);for(Node* node : children)node->update(delta);}void Node::handle(const sf::Event& event){onHandle(event);for(Node* node : children)node->handle(event);}void Node::addNode(Node* node){node->setParent(this);node->setWindow(window);children.push_back(node);std::sort(begin(children), end(children), sortByZValue);}void Node::removeNode(Node* node){for(auto itr = children.begin(); itr != children.end(); itr++){if(node == (*itr)){children.erase(itr);return;}}}Node* Node::getParent(){return parent;}void Node::setParent(Node* parentnode){parent = parentnode;}std::vector<Node*>& Node::getChildren(){return children;}А, и возможно я мог слишком неявно указать в чём проблема: Когда ты перетаскиваешь окно и игра доходит до секции отрисовки отрисовка приостанавливается до момента пока ты не отпустиш окно, и как только ты попадаешь в секцию где ты перемещаешь обьекты ты умножаешь перемещение на пройденое время ща цикл. Посколько цикл был приостановлен, но время продолжало идти перемещение обьектов оказывалось настолько большим что обьекты попросту повылетали за границы экрана.
спасибо за развернутый ответ.
решая одну проблемы мы создаём себе две и так далее…
-
АвторСообщения
Для ответа в этой теме необходимо авторизоваться.