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

Author Topic: Vector of sprites  (Read 6331 times)

0 Members and 1 Guest are viewing this topic.

Dante12129

  • Newbie
  • *
  • Posts: 24
    • View Profile
Vector of sprites
« on: May 22, 2013, 07:28:47 am »
Hello, I'm making a spritesheet using a std::vector of sprites. It is declared as
std::vector<sf::Sprite> m_sprites
in my Spritesheet. My loading function adds sprites by adding a sprite to the array with the whole texture and a rectangle of that sprite's postion, like so:
for(int i = 0; i < m_sprites_per_column; i++)
            {
                for(int j = 0; j < m_sprites_per_row; j++)
                {
                    m_sprites.push_back(sf::Sprite(m_texture, sf::IntRect(j*sprite_dimensions.x, i*sprite_dimensions.y, sprite_dimensions.x, sprite_dimensions.y)));
                }
            }
I have checked and all the sprite and spritesheet dimensions are correct and that the texture loads correctly. I also make sure to reserve a vector with the sprites per row and column so that it doesn't reallocate. The problem is that whenever it draws the screen, it just draws white (which is what I cleared it with). Here's my drawing code:
window_ptr->clear(sf::Color::White);

            for(unsigned int i = 0; i < terrain.GetDimensions().x /terrain.GetSpritesheet().GetSpriteWidth(); i++)
            {
                for(unsigned int j = 0; j < terrain.GetDimensions().y / terrain.GetSpritesheet().GetSpriteHeight(); j++)
                {
                    sf::Sprite current_sprite = terrain.GetSpritesheet().GetSprite(i * (terrain.GetDimensions().x / terrain.GetSpritesheet().GetSpriteWidth()) + j);
                    current_sprite.setPosition(sf::Vector2f(i*32, j*32));
                    window_ptr->draw(current_sprite);
                }
            }

            window_ptr->display();
Terrain is an instance of my Map class that loads a spritesheet and map file so you can draw the map based on the spritesheet.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11032
    • View Profile
    • development blog
    • Email
AW: Vector of sprites
« Reply #1 on: May 22, 2013, 10:24:57 am »
You're copying a sprite and I think in that process the teference to the texture gets invalid.
Does it work if you set the texture again after copying?
Maybe you just want to us references to the sprites. ;)
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Vector of sprites
« Reply #2 on: May 22, 2013, 10:34:37 am »
Quote
You're copying a sprite and I think in that process the teference to the texture gets invalid.
The copy will point to the same texture, so as long as the texture itself doesn't move in memory, it's perfectly valid. Sprites can be copied as much as you want, it's textures that cannot be copied without updating the corresponding sprites. But since its texture is a class member, it seems to be ok.

How do you handle your Spritesheet instance? Do you copy it somewhere?
Laurent Gomila - SFML developer

Dante12129

  • Newbie
  • *
  • Posts: 24
    • View Profile
Re: Vector of sprites
« Reply #3 on: May 23, 2013, 03:58:15 am »
My map class copy-constructs its member spritesheet.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11032
    • View Profile
    • development blog
    • Email
AW: Vector of sprites
« Reply #4 on: May 23, 2013, 08:56:13 am »
So it indeed gets copied around, changing the textures memory address, thus invalidating the sprite's reference to the texture.
At best you don't copy around the container with the texture. If you want to do so regardless, you might want to use smart pointers, so the memory will stay the same, regardless of the container location.
And the uglier solution would be to reset the texture on every sprite. ;)
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Dante12129

  • Newbie
  • *
  • Posts: 24
    • View Profile
Re: Vector of sprites
« Reply #5 on: May 24, 2013, 02:12:45 am »
I created a std::shared_ptr for the texture in the spritesheet but it will still just show a white screen. Here's my map and spritesheet files:

Map.hpp:
#pragma once

#include <string>
#include <vector>
#include <SFML/System.hpp>
#include <SFML/Graphics.hpp>
#include "Spritesheet.hpp"

namespace dte
{
    class Map
    {
        public:
            //Constructors and destructor
            Map();
            Map(std::string spritesheet_filename, std::string map_filename, sf::Vector2u spritesheet_dims, sf::Vector2u sprite_dims, sf::Vector2u map_dimensions);
            ~Map();

            //Getters
            Spritesheet GetSpritesheet();
            sf::Vector2u GetDimensions();
            int GetTile(int position);

            //Setters
            void SetSpritesheet(Spritesheet& sheet);

        private:
            //The dimensions of the map
            sf::Vector2u m_dimensions;

            //The spritesheet for the map
            Spritesheet m_spritesheet;

            public:
            //The map data
            std::vector<int> map_array;
    };

}
 
Map.cpp:
#include <fstream>
#include <iostream>
#include <cstdlib>
#include "Map.hpp"

namespace dte
{
    //Constructors and destructor
    Map::Map() = default;
    Map::Map(std::string spritesheet_filename, std::string map_filename, sf::Vector2u spritesheet_dims, sf::Vector2u sprite_dims, sf::Vector2u map_dimensions)
    {
        m_dimensions = map_dimensions;
        m_spritesheet = dte::Spritesheet(spritesheet_filename, spritesheet_dims, sprite_dims);
        map_array.resize(m_dimensions.y * m_dimensions.x, 0);

        std::ifstream map_file(map_filename);
        if(map_file.is_open())
        {
            for(unsigned int i = 0; i < m_dimensions.x; i++)
            {
                for(unsigned int j = 0; j < m_dimensions.y; j++)
                {
                    int current_map_piece;
                    map_file >> current_map_piece;
                    map_array.at(j * m_dimensions.x + i) = current_map_piece;
                }
            }
        }
        else
        {
            std::cerr << "Could not load map: " << map_filename << std::endl;
            exit(EXIT_FAILURE);
        }
    }
    Map::~Map() = default;

    //Getters
    Spritesheet Map::GetSpritesheet() { return m_spritesheet; }
    sf::Vector2u Map::GetDimensions() { return m_dimensions; }
    int Map::GetTile(int position) { return map_array.at(position); }

    //Setters
    void Map::SetSpritesheet(Spritesheet& sheet) { m_spritesheet = sheet; }
}
 

Spritesheet.hpp:
#pragma once

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

 namespace dte
 {
     class Spritesheet
     {
         public:
            //Constructors and destructor
            Spritesheet();
            Spritesheet(const Spritesheet& source);
            Spritesheet(std::string filename, sf::Vector2u image_dims, sf::Vector2u sprite_dims);
            ~Spritesheet();

            //Getters
            unsigned int GetSpriteWidth();
            unsigned int GetSpriteHeight();
            sf::Sprite GetSprite(int sprite_number);

            //Overloaded operators
            Spritesheet& operator= (const Spritesheet& source);

         private:
            //Container of all the coordinates of the images
            std::vector<sf::Sprite> m_sprites;

            //Image dimensions
            sf::Vector2u m_texture_dimensions;
            //Sprite dimensions
            sf::Vector2u m_sprite_dimensions;

            //The image of the spritesheet
            std::shared_ptr<sf::Texture> m_texture;

            //Number of sprites in the image
            int m_sprite_count, m_sprites_per_row, m_sprites_per_column;

            //The name of the spritesheet for the file
            std::string m_filename;
     };
 }
 
Spritesheet.cpp:
#include <iostream>
#include <cstdlib>
#include "Spritesheet.hpp"

namespace dte
{
    //Spritesheet
    //Constructors and destructor
    Spritesheet::Spritesheet()
    {
        //Initalize all sizes to 0
        m_texture_dimensions = sf::Vector2u(0, 0); m_sprite_dimensions = sf::Vector2u(0, 0);
        m_sprite_count = 0; m_sprites_per_row = 0; m_sprites_per_column = 0;
        //The texture and vector don't contain anything
        //Set the filename to nothing
        m_filename = "";
    }
    Spritesheet::Spritesheet(const Spritesheet& source)
    {
        m_texture = source.m_texture;
        m_sprites = source.m_sprites;
        m_texture_dimensions = source.m_texture_dimensions; m_sprite_dimensions = source.m_sprite_dimensions;
        m_sprites_per_row = source.m_sprites_per_row; m_sprites_per_column = source.m_sprites_per_column; m_sprite_count = source.m_sprite_count;
        m_filename = source.m_filename;
    }
    Spritesheet::Spritesheet(std::string filename, sf::Vector2u image_dims, sf::Vector2u sprite_dims)
    {
        //Initalize the sizes
        m_texture_dimensions = image_dims; m_sprite_dimensions = sprite_dims;
        m_sprites_per_row = m_texture_dimensions.y/m_sprite_dimensions.y; m_sprites_per_column = m_texture_dimensions.x/m_sprite_dimensions.y; m_sprite_count = m_sprites_per_row*m_sprites_per_column;
        //Set the filename
        m_filename = filename;
        //Initialize the texture
        sf::Texture shared_texture;
        if(!shared_texture.loadFromFile(m_filename, sf::IntRect(0, 0, m_texture_dimensions.y, m_texture_dimensions.x)))
        {
            std::cerr << "Could not load texture: " << m_filename << std::endl;
            exit(EXIT_FAILURE);
        }
        m_texture = std::make_shared<sf::Texture>(shared_texture);
        //Initialize the vector so that sprites can be accessed
        m_sprites.reserve(m_sprites_per_row*m_sprites_per_column);
        if(m_sprites_per_column == 1)
        {
            for(int i = 0; i < m_sprites_per_row; i++)
            {
                m_sprites.push_back(sf::Sprite(*m_texture, sf::IntRect(i*m_sprite_dimensions.x, 0, m_sprite_dimensions.x, m_sprite_dimensions.y)));
            }
        }
        else if(m_sprites_per_row == 1)
        {
            for(int i = 0; i < m_sprites_per_column; i++)
            {
                m_sprites.push_back(sf::Sprite(*m_texture, sf::IntRect(0, i*m_sprite_dimensions.y, m_sprite_dimensions.x, m_sprite_dimensions.y)));
            }
        }
        else
        {
            for(int i = 0; i < m_sprites_per_column; i++)
            {
                for(int j = 0; j < m_sprites_per_row; j++)
                {
                    m_sprites.push_back(sf::Sprite(*m_texture, sf::IntRect(j*m_sprite_dimensions.x, i*m_sprite_dimensions.y, m_sprite_dimensions.x, m_sprite_dimensions.y)));
                }
            }
        }
    }
    Spritesheet::~Spritesheet() = default;

    //Getters
    unsigned int Spritesheet::GetSpriteWidth() { return m_sprite_dimensions.x; }
    unsigned int Spritesheet::GetSpriteHeight() { return m_sprite_dimensions.y; }
    sf::Sprite Spritesheet::GetSprite(int sprite_number) { return m_sprites.at(sprite_number); }

    //Overloaded operators
    Spritesheet& Spritesheet::operator= (const Spritesheet& source)
    {
        m_texture = source.m_texture;
        m_sprites = source.m_sprites;
        m_texture_dimensions = source.m_texture_dimensions; m_sprite_dimensions = source.m_sprite_dimensions;
        m_sprites_per_row = source.m_sprites_per_row; m_sprites_per_column = source.m_sprites_per_column; m_sprite_count = source.m_sprite_count;
        m_filename = source.m_filename;

        return *this;
    }
}
 

And here's how I'm creating the instance of my map class:
terrain = dte::Map("resources/Images/Terrain.png", "resources/Maps/World1.map", sf::Vector2u(512, 512), sf::Vector2u(32, 32), sf::Vector2u(window_ptr->getSize().x/32, window->getSize().y/32));

Dante12129

  • Newbie
  • *
  • Posts: 24
    • View Profile
Re: Vector of sprites
« Reply #6 on: May 24, 2013, 04:34:03 am »
I changed Spritesheet.cpp to this:

#include <iostream>
#include <cstdlib>
#include "Spritesheet.hpp"

namespace dte
{
    //Spritesheet
    //Constructors and destructor
    Spritesheet::Spritesheet()
    {
        //Initalize all sizes to 0
        m_texture_dimensions = sf::Vector2u(0, 0); m_sprite_dimensions = sf::Vector2u(0, 0);
        m_sprite_count = 0; m_sprites_per_row = 0; m_sprites_per_column = 0;
        //The texture and vector don't contain anything
        //Set the filename to nothing
        m_filename = "";
    }
    Spritesheet::Spritesheet(const Spritesheet& source)
    {
        m_texture = source.m_texture;
        m_sprites = source.m_sprites;
        m_texture_dimensions = source.m_texture_dimensions; m_sprite_dimensions = source.m_sprite_dimensions;
        m_sprites_per_row = source.m_sprites_per_row; m_sprites_per_column = source.m_sprites_per_column; m_sprite_count = source.m_sprite_count;
        m_filename = source.m_filename;
    }
    Spritesheet::Spritesheet(std::string filename, sf::Vector2u image_dims, sf::Vector2u sprite_dims)
    {
        //Initalize the sizes
        m_texture_dimensions = image_dims; m_sprite_dimensions = sprite_dims;
        m_sprites_per_row = m_texture_dimensions.y/m_sprite_dimensions.y; m_sprites_per_column = m_texture_dimensions.x/m_sprite_dimensions.y; m_sprite_count = m_sprites_per_row*m_sprites_per_column;
        //Set the filename
        m_filename = filename;
        //Initialize the texture
        m_texture = std::shared_ptr<sf::Texture>(new sf::Texture);
        if(!m_texture->loadFromFile(m_filename, sf::IntRect(0, 0, m_texture_dimensions.y, m_texture_dimensions.x)))
        {
            std::cerr << "Could not load texture: " << m_filename << std::endl;
            exit(EXIT_FAILURE);
        }
        //Initialize the vector so that sprites can be accessed
        m_sprites.reserve(m_sprites_per_row*m_sprites_per_column);
        if(m_sprites_per_column == 1)
        {
            for(int i = 0; i < m_sprites_per_row; i++)
            {
                m_sprites.push_back(sf::Sprite(*m_texture, sf::IntRect(i*m_sprite_dimensions.x, 0, m_sprite_dimensions.x, m_sprite_dimensions.y)));
            }
        }
        else if(m_sprites_per_row == 1)
        {
            for(int i = 0; i < m_sprites_per_column; i++)
            {
                m_sprites.push_back(sf::Sprite(*m_texture, sf::IntRect(0, i*m_sprite_dimensions.y, m_sprite_dimensions.x, m_sprite_dimensions.y)));
            }
        }
        else
        {
            for(int i = 0; i < m_sprites_per_column; i++)
            {
                for(int j = 0; j < m_sprites_per_row; j++)
                {
                    m_sprites.push_back(sf::Sprite(*m_texture, sf::IntRect(j*m_sprite_dimensions.x, i*m_sprite_dimensions.y, m_sprite_dimensions.x, m_sprite_dimensions.y)));
                }
            }
        }
    }
    Spritesheet::~Spritesheet() = default;

    //Getters
    unsigned int Spritesheet::GetSpriteWidth() { return m_sprite_dimensions.x; }
    unsigned int Spritesheet::GetSpriteHeight() { return m_sprite_dimensions.y; }
    sf::Sprite Spritesheet::GetSprite(int sprite_number) { return m_sprites.at(sprite_number); }

    //Overloaded operators
    Spritesheet& Spritesheet::operator= (const Spritesheet& source)
    {
        m_texture = source.m_texture;
        m_sprites = source.m_sprites;
        m_texture_dimensions = source.m_texture_dimensions; m_sprite_dimensions = source.m_sprite_dimensions;
        m_sprites_per_row = source.m_sprites_per_row; m_sprites_per_column = source.m_sprites_per_column; m_sprite_count = source.m_sprite_count;
        m_filename = source.m_filename;

        return *this;
    }
}
 

but it still won't display correctly.

zsbzsb

  • Hero Member
  • *****
  • Posts: 1409
  • Active Maintainer of CSFML/SFML.NET
    • View Profile
    • My little corner...
    • Email
Re: Vector of sprites
« Reply #7 on: May 24, 2013, 05:50:58 am »
Stop making your life more complicated by trying to copy everything everywhere  ;)

For example, change

Map.hpp
Spritesheet GetSpritesheet();

to

Spritesheet& GetSpritesheet();

Another thing is that your really should be using initialization lists to initialize your variables.

Also you do not need this because you should just use references.

//Overloaded operators
    Spritesheet& Spritesheet::operator= (const Spritesheet& source)
    {
        m_texture = source.m_texture;
        m_sprites = source.m_sprites;
        m_texture_dimensions = source.m_texture_dimensions; m_sprite_dimensions = source.m_sprite_dimensions;
        m_sprites_per_row = source.m_sprites_per_row; m_sprites_per_column = source.m_sprites_per_column; m_sprite_count = source.m_sprite_count;
        m_filename = source.m_filename;

        return *this;
    }

One last thing  ;) try to write a simplified piece of code that causes the same effect that includes only the parts that cause the problem. You will see very fast where the problem comes from.
« Last Edit: May 24, 2013, 05:53:46 am by zsbzsb »
Motion / MotionNET - Complete video / audio playback for SFML / SFML.NET

NetEXT - An SFML.NET Extension Library based on Thor

Dante12129

  • Newbie
  • *
  • Posts: 24
    • View Profile
[Solved] Re: Vector of sprites
« Reply #8 on: May 25, 2013, 04:15:02 am »
I got it working by realizing that the Map class didn't even use the spritesheet and so now I just get a tile type (which is an int) from the map and then use that to select the correct sprite from the spritesheet. Thanks for all the help!
« Last Edit: May 26, 2013, 01:19:39 am by Dante12129 »