The problem was you were using a vector. In vectors if you remove any item except the last item, some of the items will get moved around in memory, which was causing the white box problem for you because you didn't overload the assignment operator.
To fix your issue there are 4 solutions:
1- Overload the assignment operator
2- Keep pointers to the platforms inside the vector instead, be careful about memory leaks
3- Use a list instead of a vector
4- Keep the texture in a static member so all of the platforms use the same texture
Also note that if you aren't planning to move the texture to a static member, then there is no need to keep it as a pointer, since white box problem happens if the texture has been deleted before the sprites that were using it, but if you aren't using a static member and you aren't keeping it as a pointer, it will never happen on accident in your case. Just be sure that in both the copy constructor and assignment operator you are copying the texture or reloading it properly.
In the code below I did a mix of the solutions 1 & 3 & 4. Hopefully this will help you with your problem.
#include <SFML/Graphics.hpp>
#include <SFML/System.hpp>
#include <SFML/Window.hpp>
#include <list>
class Platform
{
private:
// Since all of the platforms load the same file, I moved the texture to a static member so all of the platforms can use the same texture.
static sf::Texture* m_texture;
sf::Sprite m_sprite;
public:
Platform()
{
m_sprite.setTexture(*m_texture);
m_sprite.setPosition(100, 1080);
}
Platform(const Platform ©)
{
// Just to be sure the sprite is pointing at the currect texture.
m_sprite.setTexture(*m_texture);
m_sprite.setPosition(copy.m_sprite.getPosition());
}
sf::Sprite getSprite()
{
return m_sprite;
}
void moveUp()
{
m_sprite.move(0, -10.f);
}
// It's how you should overload the assignment operator
Platform& operator= (const Platform& copy)
{
// Just to be sure the sprite is pointing at the currect texture.
m_sprite.setTexture(*m_texture);
m_sprite.setPosition(copy.m_sprite.getPosition());
return *this;
}
// Since there is no need to reload the texture on every constructor, I moved the loading action to a static function.
// An alternative can be to have a static variable that counts how many platform objects currently exist, then load the texture if the value previously was 0 in the constructor, and unload the texture if the value was 1 in the deconstructor.
// But that approach might cause rapid loading and unloading which can bring your game's fps down.
static void loadTexture()
{
if (m_texture == nullptr) // prevents memory leak
{
m_texture = new sf::Texture;
if (!m_texture->loadFromFile("textures/platform.png"))
throw "image not found";
}
}
// I also moved the unloading to a static function.
static void unloadTexture()
{
if (m_texture != nullptr) // prevents memory leak
{
delete m_texture;
m_texture = nullptr;
}
}
};
// Nothing special here, it's the way static members work in c++.
// We need this line to make sure its initial value is `nullptr` and to prevent the "Undefined reference to..." error
sf::Texture* Platform::m_texture = nullptr;
int main()
{
sf::RenderWindow window(sf::VideoMode(1920, 1080), "SFML works!", sf::Style::Fullscreen);
window.setFramerateLimit(60);
Platform::loadTexture(); // loading the platform texture here, since we are going to use platforms now
// I changed the vector to list, since in vectors, if you delete something from anywhere other the end of the vector, it will move every object inside it in the memory, which was the reason you where experiencing the white box error.
// On the other hand, you can delete anything in a list without having to move other elements, but in return you can't access a random element of it outside of a loop.
// Check https://cplusplus.com/reference/list/list/ to understand how it works.
std::list<Platform> platforms;
int timer = 0;
while (window.isOpen())
{
sf::Event evnt;
while (window.pollEvent(evnt))
{
if (evnt.type == sf::Event::Closed)
{
window.close();
}
}
// Spawn platforms every second
timer++;
if (timer >= 60)
{
platforms.push_back(Platform());
timer = 0;
}
// Erase platforms that go off screen
// It's how you can iterate through members of a list
for (auto it = platforms.begin(); it != platforms.end()
{
it->moveUp();
if (it->getSprite().getPosition().y < 0)
it = platforms.erase(it);
else
it++;
}
// Draw
window.clear();
// It's how you can iterate through members of a list
for (auto it = platforms.begin(); it != platforms.end(); it++)
window.draw(it->getSprite());
window.display();
}
Platform::unloadTexture();
return 0;
}