Gaurav Jangir
Gaurav Jangir

Reputation: 1

Rendering more than one sprites gives an unexpected error

The code files are written below. When i try to compile and run the project with 'Enemies.h' I got error on Enemy.cpp -> Enemy::Draw -> window.draw(healthText); If i comment out this line the program runs but the sprite is displayed as red box (sprite.setColor(sf::Color::Red);) if i remove this line nothing is seen. The texture enemy.png is not loading. But i try to render one enemy only (Including Enemy.h only and not including Enemies.h) then everything works perfectly i.e. the texture and text are displayed on the screen and everything run smoothly. I am trying to run it in microsoft visual studio 2022 community. I am not able to find the actual problem. It didn't give and error while loading picture or text but anywhere else in this code. Can you please help ?

main.cpp

#include <SFML/Graphics.hpp>
#include "Enemies.h"

int main()
{
    sf::RenderWindow window(sf::VideoMode(1920, 1080), "SFML works!");
    window.setVerticalSyncEnabled(true);

    Enemies enemies;
    sf::Clock clock;

    sf::Int64 enemySpawnMeasure = 0;
    while (window.isOpen())
    {
        sf::Time elapsed = clock.restart();
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
                window.close();
        }
        enemies.update(elapsed,enemySpawnMeasure);

        window.clear();
        enemies.draw(window);
        window.display();
    }

    return 0;
}

Enemies.h

#pragma once
#include "Enemy.h"
class Enemies
{
    std::vector<Enemy> enemies;
    int spawnRate;
public:
    void draw(sf::RenderWindow& window);
    void update(sf::Time& clockTime,sf::Int64& spawnMeasure);
};

Enemies.cpp

#include "Enemies.h"

void Enemies::draw(sf::RenderWindow& window)
{
    for (Enemy& enemy : enemies)
    {
        enemy.draw(window);
    }
}

void Enemies::update(sf::Time& clockTime,sf::Int64& measure)
{
    if(measure <= 1000000)
        measure += clockTime.asMicroseconds();
    else {
        Enemy enemy(sf::Vector2f(rand() % 1920, rand() % 1080));
        enemies.push_back(enemy);
        measure = 0;
    }
    for (Enemy& enemy : enemies)
    {
        enemy.update(clockTime);
    }
}

Enemy.h

#pragma once
#include<SFML/Graphics.hpp>
class Enemy
{
    sf::Vector2f position;
    sf::Texture texture;
    sf::Sprite sprite;
    sf::Vector2f velocity;
    int health;
    sf::Text healthText;
    sf::Font font;
public:
    Enemy(sf::Vector2f position=sf::Vector2f(0.f,0.f), sf::Vector2f velocity = sf::Vector2f(1.f,1.f), int health = 100, int damage = 10, int score = 10);
    void draw(sf::RenderWindow& window);
    void update(sf::Time& clockTime);
};

Enemy.cpp

#include "Enemy.h"

Enemy::Enemy(sf::Vector2f position, sf::Vector2f velocity, int health, int damage, int score)
{
    this->position = position;
    this->velocity = velocity;
    this->health = health;
    if (!texture.loadFromFile("./Assets/Images/enemy.png")) throw std::runtime_error("Enemy image not found.");
    sprite.setColor(sf::Color::Red);
    sprite.setTexture(texture);
    sprite.setPosition(position);
    if (!font.loadFromFile("./Assets/Fonts/RobotoMono-VariableFont_wght.ttf")) throw std::runtime_error("Font not found.");
    healthText.setFont(font);
    healthText.setString(std::to_string(health));
    healthText.setPosition(position.x,position.y-10);
}

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

void Enemy::update(sf::Time& clockTime)
{
    sprite.move(velocity * float(clockTime.asMicroseconds()/10000));
    healthText.setPosition(sprite.getPosition().x, sprite.getPosition().y - 10);
}

i need all the enemies to rendered at one time on the screen.

Upvotes: 0

Views: 33

Answers (1)

Alexander
Alexander

Reputation: 83

Seems like you are losing resources in each

enemies.push_back(enemy);

Pushing back makes copy of local enemy object. Therefore original texture and font are destroyed. Instead of having sf::Texture and sf::Font in every Enemy instance, try managing this assets in Enemies class loading them only one time. Then just provide a reference to this resources for Enemy construction.

In general, for each enemy (each sf::Sprite more precisely) you only need one shared copy of the enemy texture. The same applied to multiple sf::Text objects and one shared sf::Font.

Some notes about texture destroying can be found here SFML Tutorials, The white square problem. For the font and text see SFML, Details of sf::Text class reference.

Upvotes: 0

Related Questions