Проблема производительности SFML в космическом захватчике, таком как игра - PullRequest
0 голосов
/ 10 апреля 2020

Я пытаюсь изучать C ++, и я делаю эту маленькую игру, похожую на космический захватчик, чтобы стать лучше. В настоящее время все работает просто отлично, но по какой-то причине производительность игры ужасна, учитывая, насколько проста графика (игра работает плавно некоторое время, а затем останавливается на полсекунды постоянно). Я почти уверен, что задержка связана с количеством пуль, которые я удаляю и создаю из вектора, который их содержит (я удаляю маркеры, которые go за пределами экрана, потому что нет смысла их обновлять или отображать) , Так как я мог это исправить? Я проверил игру l oop, и проблема не в этом.

Код ниже

player.h:

#pragma once
#include <vector>
#include <iostream>
#include "entity.h"
#include "SFML/Graphics.hpp"
#include "vector.h"

class Player : public Entity {
private:
    int f_lastshot = 0;
    const double shoot_delay = 0.5;
    const float bullet_speed = 1, speed = 0.75;
    std::vector<Entity> bullets;
    sf::Clock *c;
    sf::Sprite bullet_sprite;
public:
    Player(Vector &pos, int w, int h, sf::Sprite &spr);
    ~Player();
    void init();
    void update();
    void render(Window &win);
    void shoot();
};

player. cpp:

#include "player.h"

Player::Player(Vector &pos, int w, int h, sf::Sprite &spr) : Entity() {
    this->pos = pos;
    this->sprite = spr;
    this->width = w;
    this->height = h;
    float scale_x = (float)w / (float)spr.getTexture()->getSize().x;
    float scale_y = (float)h / (float)spr.getTexture()->getSize().y;
    sprite.setScale(scale_x, scale_y);
}

void Player::update() {
    if (sf::Mouse::isButtonPressed(sf::Mouse::Left) || sf::Keyboard::isKeyPressed(sf::Keyboard::S)) {
        if ((double)(f_lastshot / 60) > shoot_delay) // shoot only if enough time has passed
            shoot();
    }
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::A)) dir.setX(-speed); // move left
    else if (sf::Keyboard::isKeyPressed(sf::Keyboard::D)) dir.setX(speed); // move right
    else dir.setX(0); // stop if no button is being pressed
    f_lastshot++; 
    pos = pos + dir; // add the direction to the position

    for (Entity &e : bullets) {
        e.update(); // update each bullet
        if (e.getPos().getY() < 0) // if the bullet is outside the screen delete it
            bullets.erase(bullets.begin()); 
    }
}

void Player::render(Window &win) {
    sprite.setPosition(pos.getX(), pos.getY()); // update the position of the sprite
    win.render(sprite);
    for (Entity &bullet : bullets) 
        bullet.render(win);
}

void Player::shoot() {
    f_lastshot = 0;
    bullets.push_back(Entity::Entity(Vector::Vector(pos.getX() + width / 2 - 8, pos.getY()), 16, 32, bullet_sprite, Vector::Vector(0,-bullet_speed)));
}

void Player::init() {
    if (c == nullptr) c = new sf::Clock();
    else c->restart();
    dir.setX(0); dir.setY(0);

    sf::Texture *bullet_texture = new sf::Texture();
    if (!bullet_texture->loadFromFile("bullet.png"))
        std::cerr << "Could not load bullet image" << std::endl;

    bullet_sprite.setTexture(*bullet_texture);
}


Player::~Player() {
    delete bullet_sprite.getTexture();
    delete c;
}

entity.h:

#pragma once
#include <SFML/Graphics.hpp>
#include <iostream>
#include "vector.h"
#include "window.h"

class Entity {
protected:
    Vector pos;
    sf::Sprite sprite;
    int width, height;
    Vector dir; // the direction
public:
    Entity();
    Entity(Vector &pos, int w, int h, sf::Sprite &spr);
    Entity(Vector pos, int w, int h, sf::Sprite &spr);
    Entity(Vector pos, int w, int h, sf::Sprite &spr, Vector dir);

    void update();
    void render(Window &win);
    bool collides(Entity &other) const;
public:
    Vector getPos() const;
    Vector getDirection() const;
    sf::Sprite getSprite() const;
    int getWidth() const;
    int getHeight() const;
    void setPos(Vector &pos);
    void setWidth(int width);
    void setHeight(int height);
    void setSize(int width, int height);
    void setSprite(sf::Sprite &sprite);
    void setDirection(Vector dir);
};

entity. cpp:

#include "entity.h"

Entity::Entity() {}

Entity::Entity(Vector &pos, int w, int h, sf::Sprite &spr)
    :  pos(pos), width(w), height(h) {
    this->sprite = spr;
    float scale_x = (float)w/(float)spr.getTexture()->getSize().x;
    float scale_y = (float)h/(float)spr.getTexture()->getSize().y;
    sprite.setPosition(pos.getX(), pos.getY());
    sprite.setScale(scale_x, scale_y);
}

Entity::Entity(Vector pos, int w, int h, sf::Sprite &spr)
    : pos(pos), width(w), height(h) {
    this->sprite = spr;
    float scale_x = (float)w / (float)spr.getTexture()->getSize().x;
    float scale_y = (float)h / (float)spr.getTexture()->getSize().y;
    sprite.setPosition(pos.getX(), pos.getY());
}

Entity::Entity(Vector pos, int w, int h, sf::Sprite &spr, Vector dir)
    : pos(pos), width(w), height(h) {
    this->sprite = spr;
    this->dir = dir;
    float scale_x = (float)w / (float)spr.getTexture()->getSize().x;
    float scale_y = (float)h / (float)spr.getTexture()->getSize().y;
    sprite.setPosition(pos.getX(), pos.getY());
}

bool Entity::collides(Entity &other) const {
    bool x_coll = (pos.getX() + width> other.getPos().getX() && pos.getX()< other.getPos().getX())||(pos.getX() < other.getPos().getX() + other.getWidth() && pos.getX() > other.getPos().getX());
    bool y_coll = (pos.getY() + height > other.getPos().getY() && pos.getY() < other.getPos().getY()||(pos.getY() < other.getPos().getY() + other.getHeight() && pos.getY() > other.getPos().getY()));
    return (x_coll && y_coll);
}

void Entity::render(Window &win) {
    sprite.setPosition(pos.getX(), pos.getY());
    win.render(sprite);
}

void Entity::update() {
    pos = pos + dir;
}

Vector Entity::getPos() const { return pos; }
int Entity::getWidth() const { return width; }
int Entity::getHeight() const { return height; }
sf::Sprite Entity::getSprite() const { return sprite; }
Vector Entity::getDirection() const { return dir; }
void Entity::setPos(Vector &pos) { this->pos = pos; }
void Entity::setWidth(int width) { this->width = width; }
void Entity::setHeight(int height) { this->height = height; }
void Entity::setSprite(sf::Sprite &sprite) { this->sprite = sprite; }
void Entity::setDirection(Vector dir) { this->dir = dir; }
void Entity::setSize(int width, int height) {
    this->width = width;
    this->height = height;
}

1 Ответ

0 голосов
/ 10 апреля 2020

Для выявления узких мест и других проблем с производительностью вам следует использовать профилировщик. Только просматривая код, трудно, если не невозможно, найти правильные места, которые нуждаются в оптимизации. Но одна проблема действительно заключается в том, как вы удаляете маркеры:

for (Entity &e : bullets) {
    e.update(); // update each bullet
    if (e.getPos().getY() < 0) // if the bullet is outside the screen delete it
        bullets.erase(bullets.begin()); 
}
  1. Ошибка logi c. Вы проверяете, находится ли текущий маркер e за пределами экрана, но затем все равно удаляете первый. Это не может быть правдой.

  2. Крайне неэффективно удалить первый элемент std::vector, потому что все последующие элементы должны быть перемещены на одну позицию влево. Это происходит для каждой удаляемой пули!

  3. Это может вызвать неопределенное поведение. ссылка говорит, что erase

    Делает недействительными итераторы и ссылки в или после точки стирания, включая итератор end ().

    Изменение размера вектора во время итерации обычно является плохой идеей.

Чтобы преодолеть эти проблемы, вы можете использовать Erase-Remove-Idiom , как это :

// Update as usual
for (Entity &e : bullets) {
    e.update();
}

// Erase-Remove bullets that are out of screen
bullets.erase(std::remove_if(bullets.begin(), bullets.end(),
    [](const Entity &e){ return e.getY() < 0;}), bullets.end()); 
...