Спрайты врагов не отображаются, когда я заполняю вектор циклом for

#include <iostream>
#include <SFML/Graphics.hpp>
#include <vector>

#include "Player.hpp"
#include "Global.hpp"
#include "Enemy.hpp"


float randint(float min, float max) {
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(min, max);
    return dis(gen);
}

int main() {
    Player player;

    player.setPos({ gbl::winWidth / 2.f, gbl::winHeight / 2.f });

    std::vector<Enemy> enemies;

    sf::RenderWindow window(sf::VideoMode(gbl::winWidth, gbl::winHeight), gbl::title);

    for (int i = 0; i < 5; i++) {
        Enemy newEnemy;
        newEnemy.setPos({ randint(10 + newEnemy.getSize().x, gbl::winWidth - newEnemy.getSize().x), 100 });
        enemies.push_back(newEnemy);
        
    }

    sf::Event ev;
    while (window.isOpen()) {
        while (window.pollEvent(ev)) {
            switch (ev.type) {
            case sf::Event::KeyPressed:
                if (ev.key.code == sf::Keyboard::Escape) window.close();
                break;
            case sf::Event::Closed:
                window.close();
                break;
            case sf::Event::KeyReleased:
                if (ev.key.code == sf::Keyboard::R) {
                    player.reset();
                    for (auto& enemy : enemies) {
                        enemy.setPos({ randint(0, gbl::winWidth), 100 });
                    }
                }
            }
        }


        window.clear(gbl::bgColor);

        player.draw(window);
        for (Enemy& enemy : enemies) {
            enemy.update();
            if (enemy.getPos().y + enemy.getSize().y > gbl::winHeight + enemy.getSize().y) {
                enemy.setOffscreen();
            }
            enemy.draw(window);
        }

        player.move();

        window.display();
    }

    return 0;
}

Я попробовал вручную создать объект типа Enemy и вставить его в вектор. Когда я запускаю игру, все работает так, как задумано. Но когда я использую цикл for для заполнения вектора и запускаю игру, отображается только спрайт игрока. Я попробовал поместить цикл for внутри и снаружи основного цикла, но это не сработало. Что я должен делать?

Примечание. Случайный файл заголовка находится в одном из файлов заголовков.

#include "Enemy.hpp"
#include <random>

static inline float randint(float min, float max) {
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(min, max);
    return dis(gen);
}

Enemy::Enemy() {
    texture.loadFromFile("assets/images/Enemy.png");
    sprite.setTexture(texture);
    speed = 0.1f;
    offscreen = false;
}


sf::Sprite Enemy::getSprite() const {
    return sprite;
}

sf::Vector2f Enemy::getPos() const {
    return sprite.getPosition();
}

sf::Texture Enemy::getTexture() const {
    return texture;
}

float Enemy::getSpeed() const {
    return speed;
}

bool Enemy::getOffscreen() const {
    return offscreen;
}

sf::Vector2u Enemy::getSize() const {
    return texture.getSize();
}


void Enemy::setPos(sf::Vector2f e_pos) {
    sprite.setPosition(e_pos);
}


void Enemy::setOffscreen() {
    offscreen = true;
}



void Enemy::draw(sf::RenderWindow& d_window){
    
    d_window.draw(sprite);
}


void Enemy::update() {
    if (offscreen == false)
        sprite.move({ 0, speed });
    else {
        setPos({ randint(getSize().x, 800.f - getSize().x), 0 });
        offscreen = false;
    }
    
}

This is the code for the Enemy class

Рассматривали ли вы возможность добавить точку останова в цикл for_each и посмотреть, что произойдет и что будет вызвано? Кроме того, вам, вероятно, следует добавить к вопросу тег SFML.

Max Play 30.07.2024 23:07

Совершенно не связано: randint полностью перестраивает генератор случайных чисел каждый раз, когда он генерирует число. Это очень дорого. Вы хотите создать gen один раз и продолжать использовать его повторно. Установите его в main и раздавайте или сделайте static, float randint(float min, float max) { static std::mt19937 gen(std::random_device{}()); std::uniform_int_distribution<> dis(min, max); return dis(gen); }, чтобы он создавался только один раз.

user4581301 30.07.2024 23:07

не могли бы вы хотя бы показать нам содержимое класса Enemy... ошибка, скорее всего, там...

Ahmed AEK 30.07.2024 23:08

Несвязано: std::for_each(enemies.begin(), enemies.end(), [&window](Enemy& enemy) можно упростить до for(Enemy& enemy : enemies)

Ted Lyngmo 30.07.2024 23:11
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
69
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

в вашем конструкторе Enemy вы привязываете спрайт к текстуре

Enemy::Enemy() {
    texture.loadFromFile("assets/images/Enemy.png");
    sprite.setTexture(texture);
    speed = 0.1f;
    offscreen = false;
}

но когда вы вставляете его в вектор, копия объекта вставляется в вектор, а исходная текстура уничтожается, поэтому спрайт теперь не имеет текстуры. (и вам не следует/не следует реализовывать конструктор копирования/перемещения)

вам нужен sf::Texture, чтобы не менять его местоположение в памяти, поэтому вместо этого вам нужно хранить указатель на него внутри Enemy объекта, лучший вариант - использовать std::shared_ptr<sf::Texture>ссылкуshared_ptr вместо хранения текстуры по значению, таким образом, даже если Enemy скопированная/перемещенная текстура останется в том же месте в памяти.

PS: не забудьте инициализироватьshared_ptr с помощью std::make_shared, также вы можете инициализировать только 1 текстуру противника за пределами Enemy и передать ее в конструктор Enemy в качестве аргумента для экономии памяти.

// in main
auto enemyTexture = std::make_shared<sf::Texture>();
enemyTexture->loadFromFile("assets/images/Enemy.png");
for (int i = 0; i < 5; i++) {
        Enemy newEnemy{enemyTexture};
        ...
    }

// in Enemy
Enemy::Enemy(std::shared_ptr<sf::Texture> texture) {
    texture_shared_ptr = std::move(texture);
    sprite.setTexture(*texture_shared_ptr);
    speed = 0.1f;
    offscreen = false;
}

Здесь тоже подойдет необработанный указатель, но shared_ptr избавит вас от необходимости думать о времени жизни объекта.


При выборе типа указателя для хранения в объекте.

  1. Необработанные указатели копируются дешево, но требуют внешнего управления временем существования объекта. (Обычно через менеджер ресурсов)
  2. unique_ptr отключает операции копирования и не позволяет вам делиться текстурами, а вы действительно хотите ими поделиться, поскольку каждая текстура может иметь размер в несколько КБ, но имеет ту же производительность, что и необработанный указатель.
  3. shared_ptr имеет небольшие накладные расходы на копирование и уничтожение (они происходят только один раз для каждого объекта), но вы никогда не столкнетесь с проблемами в течение всего срока службы, и это не мешает копированию вашего объекта.

На данный момент вы можете смело использовать shared_ptr, так как снижение производительности незначительно, но для большого проекта с тысячами объектов вы хотите использовать необработанные указатели и управлять временем жизни текстуры извне, поскольку эта небольшая экономия суммируется.

Спасибо большое, теперь все работает!

Xcc40 31.07.2024 09:29

Другие вопросы по теме