Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: How to fix white square problem when used vectors?  (Read 7464 times)

0 Members and 1 Guest are viewing this topic.

Ivan

  • Newbie
  • *
  • Posts: 32
  • IT geek
    • View Profile
How to fix white square problem when used vectors?
« on: August 09, 2013, 04:54:25 pm »
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.



I wrote a minimal example. This is the code, can download here.
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;
}
 


binary1248

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1405
  • I am awesome.
    • View Profile
    • The server that really shouldn't be running
Re: How to fix white square problem when used vectors?
« Reply #1 on: August 09, 2013, 05:34:14 pm »
Either do as you said and reset the texture at every draw (which isn't so expensive because this doesn't involve additional OpenGL calls), or... the much simpler method is to just use an STL container that doesn't move data around when it is modified, such as std::list instead of std::vector.
SFGUI # SFNUL # GLS # Wyrm <- Why do I waste my time on such a useless project? Because I am awesome (first meaning).

Ivan

  • Newbie
  • *
  • Posts: 32
  • IT geek
    • View Profile
Re: How to fix white square problem when used vectors?
« Reply #2 on: August 09, 2013, 05:58:21 pm »
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);
}
 

The Hatchet

  • Full Member
  • ***
  • Posts: 135
    • View Profile
    • Email
Re: How to fix white square problem when used vectors?
« Reply #3 on: August 09, 2013, 08:51:34 pm »
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.

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.
« Last Edit: August 09, 2013, 09:00:53 pm by The Hatchet »

binary1248

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1405
  • I am awesome.
    • View Profile
    • The server that really shouldn't be running
Re: How to fix white square problem when used vectors?
« Reply #4 on: August 09, 2013, 09:50:36 pm »
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.
SFGUI # SFNUL # GLS # Wyrm <- Why do I waste my time on such a useless project? Because I am awesome (first meaning).

Ivan

  • Newbie
  • *
  • Posts: 32
  • IT geek
    • View Profile
Re: How to fix white square problem when used vectors?
« Reply #5 on: August 10, 2013, 10:03:22 pm »
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.

Ixrec

  • Hero Member
  • *****
  • Posts: 1241
    • View Profile
    • Email
Re: How to fix white square problem when used vectors?
« Reply #6 on: August 10, 2013, 10:11:40 pm »
Another option might be having the vector hold (smart) pointers to players instead of the player objects themselves.

Regarding the asset manager, I *think* (correct me if I'm wrong) that would be something like a std::map from some kind of key to a smart pointer for a texture.  So each Player would simply hang on to a key, and when they needed the texture they'd put that key into the map.
« Last Edit: August 10, 2013, 10:15:16 pm by Ixrec »

binary1248

  • SFML Team
  • Hero Member
  • *****
  • Posts: 1405
  • I am awesome.
    • View Profile
    • The server that really shouldn't be running
Re: How to fix white square problem when used vectors?
« Reply #7 on: August 10, 2013, 10:30:17 pm »
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??
Since your member variables are public, you can just do rivals.back().setTexture( rivals.back().texturePlayer );. You should make them private though and provide accessor methods.
SFGUI # SFNUL # GLS # Wyrm <- Why do I waste my time on such a useless project? Because I am awesome (first meaning).

Ivan

  • Newbie
  • *
  • Posts: 32
  • IT geek
    • View Profile
Re: How to fix white square problem when used vectors?
« Reply #8 on: August 10, 2013, 11:12:23 pm »
Yes, it should be private, I made public only in this test for simplify work.

With rivals.back().sprPlayer.setTexture(rivals.back().texturePlayer); it Works. But still don't like having to setTexture() two times. I'll keep thinking about how to solve. Thank you binary1248.

Lee R

  • Jr. Member
  • **
  • Posts: 86
    • View Profile
Re: How to fix white square problem when used vectors?
« Reply #9 on: August 11, 2013, 12:30:55 am »
You need to define both a copy constructor and copy assignment operator for the 'Player' class which sets the sprite's texture to that instance's internal copy (e.g. 'sprPlayer.setTexture(texturePlayer);').