Hi, first of all I know the "White square problem" described in the sprite tutorial. I understand it.
In my case, I create a Player class with a sprite and texture members. In main code, when a user press M key, create a new instance of Player and add it into a vector. I known that the elements of a vector move in memory when the vector change and then lost the reference of the sprite texture.
Now, my question is how I can fix it? I tried with add setTexture every time I draw, but I don't know if its a good solution.
I attach an screenshoot about I'm saying.
(http://en.sfml-dev.org/forums/index.php?action=dlattach;topic=12583.0;attach=841;image)
I wrote a minimal example. This is the code, can download here (http://sdrv.ms/18gIjix).
I use SFML-2.1 with Code:Blocks (Windows 8 Pro, 32bits)
Player.h
//Player class
#ifndef PLAYER_H
#define PLAYER_H
#include <SFML/Graphics.hpp>
#include <iostream>
class Player
{
public:
Player(sf::Vector2i position, sf::Color colorSpr, sf::String filenameSpr);
~Player();
void Draw(sf::RenderWindow &Window);
sf::Sprite sprPlayer;
sf::Texture texturePlayer;
int sprWidth, sprHeight;
protected:
private:
};
#endif
Player.cpp
#include "Player.h"
//constructor
Player::Player(sf::Vector2i position, sf::Color colorSpr, sf::String filenameSpr)
{
if(!texturePlayer.loadFromFile(filenameSpr, sf::IntRect(0, 0, 32, 32)))
std::cout << "Error, can't load SpriteSheet " << std::endl;
sprPlayer.setTexture(texturePlayer);
sprPlayer.setColor(colorSpr);
sprWidth = sprPlayer.getTextureRect().width;
sprHeight = sprPlayer.getTextureRect().height;
sprPlayer.setPosition(position.x * sprWidth, position.y * sprHeight);
}
//destructor
Player::~Player()
{
}
//draw player
void Player::Draw(sf::RenderWindow &Window)
{
//sprPlayer.setTexture(texturePlayer); //<- with this it works
Window.draw(sprPlayer);
}
Main.cpp
#include <SFML/Graphics.hpp>
#include <vector>
#include "Player.h"
const int maxPlayers = 10;
//colors for players
sf::Color pColors[maxPlayers] = {sf::Color(255, 0, 0, 255), sf::Color(0, 255, 0, 255),
sf::Color(100, 100, 0, 255), sf::Color(0, 0, 255, 255),
sf::Color(80, 200, 120, 255), sf::Color(100, 100, 155, 255),
sf::Color(123, 160, 90, 255), sf::Color(200, 0, 255, 255),
sf::Color(254, 195, 172, 255), sf::Color(255, 255, 255, 255)
};
sf::String spriteSheets[maxPlayers] = {"punky2.png", "punky.png", "punky.png", "punky.png", "punky.png",
"punky.png", "punky.png", "punky.png", "punky.png", "punky.png"
};
int main()
{
sf::Vector2i playerPosition(0, 0);
int numRivals = 0;
std::vector <Player> rivals;
rivals.reserve(maxPlayers);
sf::RenderWindow Window(sf::VideoMode(640, 480, 32), "Test");
Window.setKeyRepeatEnabled(false);
//create player
Player hero(playerPosition, pColors[0], spriteSheets[0]);
while (Window.isOpen())
{
sf::Event event;
while (Window.pollEvent(event))
{
switch (event.type)
{
case sf::Event::Closed:
{
Window.close();
break;
}
case sf::Event::KeyPressed:
if (event.key.code == sf::Keyboard::Escape)
{
Window.close();
}
else if (event.key.code == sf::Keyboard::Right)
{
hero.sprPlayer.move(3, 0);
}
else if (event.key.code == sf::Keyboard::Left)
{
hero.sprPlayer.move(-3, 0);
}
else if (event.key.code == sf::Keyboard::Down)
{
hero.sprPlayer.move(0, 3);
}
else if (event.key.code == sf::Keyboard::Up)
{
hero.sprPlayer.move(0, -3);
}
else if (event.key.code == sf::Keyboard::M)
{
if (numRivals < maxPlayers - 1)
{
numRivals++;
Player rival(sf::Vector2i(3 + numRivals, 0), pColors[numRivals], spriteSheets[numRivals]);
rivals.push_back(rival);
}
}
break;
}
}
//draw stuff
hero.Draw(Window);
if (numRivals > 0)
{
for (int k = 0; k < rivals.size(); k++)
{
rivals[k].Draw(Window);
}
}
Window.display();
Window.clear(sf::Color(255, 255, 0, 255));
}
return 0;
}
Thanks for reply binary1248. I tried with std::list but still doesn't work. Don't show the texture. I'm confused ???
std::list <Player> rivals;
....
Player rival(sf::Vector2i(3 + numRivals, 0), pColors[numRivals], spriteSheets[numRivals]);
rivals.push_back(rival);
....
std::list<Player>::iterator e;
for (e = rivals.begin(); e != rivals.end(); ++e)
{
(*e).Draw(Window);
}
Another way to get around this problem would be to have an asset manager hold all your textures(so they don't get moved around in memory) then when the player is created have them set their textures to the ones in the texture manager.
You'd probably want to do this anyways once your project starts getting bigger and bigger, that way you can load all your textures right at the start and don't have to worry when new entities get added or deleted.
Please can you show me and example of doing this, I don't know how make it. Thank you for your answers.
You could also try setting your vectors reserve size big enough to hold all your entities. I think vector elements get moved in memory when the vector has to resize itself, at which a new vector is created(new memory location), all the contents are copied over with the new element added and the old is destroyed. If you have a vector reserved big enough already I don't think it moves around in memory.
I'm doing this but doesn't work.
std::vector <Player> rivals;
rivals.reserve(maxPlayers);
You need to set the texture of the sprite after you insert into the std::list. If you set before and insert, the memory location of the data you will reference in the future will change since the std::list makes a copy of whatever you insert into it.
The problem is I load the texture in the constructor, I can't set after insert to vector. May be the solution is create an empty constructor and define a setPlayer() function??
Thanks for your answer.