[太空入侵者(如游戏)中的SFML性能问题

问题描述 投票:-1回答:1

我正在尝试学习C ++,并且我正在制作这个类似太空入侵者的小游戏来使其变得更好。目前,一切正常,但由于某种原因,考虑到图形的简单程度,游戏的性能还是很糟糕的(游戏平稳运行了一段时间,然后持续停顿了半秒钟)。我敢肯定,滞后是由于要删除的子弹和从包含它们的向量中创建的子弹造成的(我要删除屏幕外的子弹,因为没有必要更新或渲染它们)。那么我该如何解决呢?我检查了游戏循环,但问题不存在。

下面的代码

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;
}
c++ performance c++11 vector sfml
1个回答
0
投票

最后,您只能确定使用探查器。但是,确实存在一个问题,即删除项目符号的方式:

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. 存在逻辑错误。您检查当前的项目符号e是否在屏幕之外,但无论如何都要删除第一个。这不对。

  2. 删除std::vector的第一个元素效率极低,因为随后的所有元素都必须向左移动一个位置。您删除的每个子弹都会发生这种情况!

  3. 这可能会导致不确定的行为。 reference表示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;})); 
© www.soinside.com 2019 - 2024. All rights reserved.