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

Author Topic: Issue with vector of sprites  (Read 9284 times)

0 Members and 1 Guest are viewing this topic.

legacyblade

  • Newbie
  • *
  • Posts: 25
    • View Profile
Issue with vector of sprites
« on: January 31, 2013, 02:01:30 am »
Hello. Thanks for clicking on this!

Anyways, I'm working on the framework for an in-game GUI, and I decided to start with tileable images that drained from the top down. I did this with rendertextures, but found that they were too slow to be updated several times a second.

So I made a class that will hold a dynamic amount of sprites that I can easily move, rotate, scale, etc. To do this, I have a class that stores textures and sprites in separate vectors. I'm going to make functions that can set the position/rect/rotation/scale of individual sprites, as well as transform all the sprites as a group. I don't get any errors in my code, but the sprites very infrequently draw to the screen. And if they do, only the most recent one is displayed (the earlier ones will either not display at all, or show up as a tan box).

My code (trimmed down so as to not bog you down with the stuff unrealated to this problem) is as follows:


Main.cpp
#include <iostream>
#include <utility>
#include <vector>

#include <SFML/Graphics.hpp>

#include "Sprite_Holder.h"

int main() {

        sf::RenderWindow window(sf::VideoMode(640,480),"UI Test");

        Sprite_Holder heartContainer;

        heartContainer.add_sprite("heart_back");
        heartContainer.add_sprite("heart");
        heartContainer.add_sprite("heart_shine");

        while (window.isOpen()) {

                sf::Event testEvent;

                while (window.pollEvent(testEvent)) {
                        if(testEvent.type == sf::Event::Closed) {
                                window.close();
                        }
                }



                window.clear(sf::Color(255,255,255));

                for(unsigned int i=0; i<heartContainer.sprites.size(); i++) {
                        window.draw(heartContainer.sprites[i]);
                }

                window.display();
        }

        return 1;
}
 

Sprite_Holder.h
#ifndef _SPRITE_HOLDER_LB_H
#define _SPRITE_HOLDER_LB_H


#include <SFML/Graphics.hpp>

#include <iostream>

#include <string>
#include <vector>

using namespace std;

class Sprite_Holder
{
public:
        int add_sprite(string filename, int index=-1, string extension="png", string directory="img");

        void move_sprite(int x, int y);
        void rotate_sprite(float angle);
        void scale_sprite(float x, float y);

        void set_sprite_rect(int spriteIndex, int left, int top, int width, int height);
        void set_sprite_pos(int spriteIndex, float x, float y);
        void set_sprite_rotate(int spriteIndex, float angle);
        void set_sprite_scale(int spriteIndex, float angle);

        vector<sf::Sprite> sprites;
        vector<sf::Texture> textures;

};

#endif
 

Sprite_Holder.cpp
#include "Sprite_Holder.h"

int Sprite_Holder::add_sprite(string filename, int index, string extension, string directory)
{
        sf::Sprite newSprite;
        sf::Texture newTexture;

        newTexture.loadFromFile(directory + "/" + filename + "." + extension);

        if(sprites.size() <= index || index == -1) {
                sprites.push_back(newSprite);
                textures.push_back(newTexture);

                sprites.back().setTexture(textures.back());

                return (sprites.size() - 1);
        } else {
                sprites.insert(sprites.begin()+index-1, newSprite);
                textures.insert(textures.begin()+index-1, newTexture);

                sprites[index].setTexture(textures[index]);

                return index;
        }

        return -1;
}
 

I'm using SFML 2.0 on visual c++ 2008. Thanks for reading this. Any help you could offer would be GREAT. I've been working on this for the past couple days (mostly just figuring out rendertexture and some algorythems for only drawing the proper percentage of the heart, but still) and I am completely stuck. If this just won't work, is there another solution for drawing things to the screen composed of a dynamic amount of images (I want to allow blue hearts to appear over the red ones if the player's max health goes past a certain point) that can be executed quickly?

eigenbom

  • Full Member
  • ***
  • Posts: 228
    • View Profile
Re: Issue with vector of sprites
« Reply #1 on: January 31, 2013, 02:22:29 am »
it could be that the textures are being moved around in memory by the std::vector, and so the sprites lose the references..
Quote
It is important to note that the sf::Sprite instance doesn't copy the texture that it uses, it only keeps a reference to it. Thus, a sf::Texture must not be destroyed while it is used by a sf::Sprite (i.e. never write a function that uses a local sf::Texture instance for creating a sprite).
from http://www.sfml-dev.org/documentation/2.0/classsf_1_1Sprite.php#details

legacyblade

  • Newbie
  • *
  • Posts: 25
    • View Profile
Re: Issue with vector of sprites
« Reply #2 on: January 31, 2013, 02:27:32 am »
It doesn't use a local texture. It stores the texture in a vector, then assigns it to the sprite (said sprite already being stored in a vector). So as far as I know, I'm keeping a copy of the texture in memory that I can refer to.

(that part is as follows. sprites and vectors are class variables declared in the header)

int Sprite_Holder::add_sprite(string filename, int index, string extension, string directory)
{
        sf::Sprite newSprite;
        sf::Texture newTexture;

        newTexture.loadFromFile(directory + "/" + filename + "." + extension);

        if(sprites.size() <= index || index == -1) {
                sprites.push_back(newSprite);
                textures.push_back(newTexture);

                sprites.back().setTexture(textures.back());

                return (sprites.size() - 1);
        } else {
                sprites.insert(sprites.begin()+index-1, newSprite);
                textures.insert(textures.begin()+index-1, newTexture);

                sprites[index].setTexture(textures[index]);

                return index;
        }

        return -1;
}

 

Am I doing it right? Or am I not properly keeping hold of the texture reference?

eigenbom

  • Full Member
  • ***
  • Posts: 228
    • View Profile
Re: Issue with vector of sprites
« Reply #3 on: January 31, 2013, 02:53:10 am »
A reference to an element in a std::vector may become invalid when adding things to it, because the vector may need to resize itself and move to a different location in memory.

In your Sprite_Holder constructor, try this:
Code: [Select]
textures.reserve(1024);
and make sure you don't add more than 1024 textures.
« Last Edit: January 31, 2013, 02:56:03 am by eigenbom »

xDarkShadowKnightx

  • Newbie
  • *
  • Posts: 4
    • View Profile
    • Email
Re: Issue with vector of sprites
« Reply #4 on: January 31, 2013, 02:54:22 am »
I see what the problem is. It's the two vectors your using to store the sprites and textures in your Sprite_Holder class. Your basically overwriting the last sprite/texture you made when calling Sprite_Holder::add_sprite. This is because your not storing them in vectors of pointers, therefore no new memory can be created! What you need to do is this

//make these vectors of pointers instead
vector<sf::Sprite*> sprites;
vector<sf::Texture*> textures;
 

Now when creating a new sprite or texture use the new operator. Like this

sprites.push_back(new sf::Sprite);
textures.push_back(new sf::Texture);
 

Then go ahead and set the last sprites texture to the last texture

sprites.back()->setTexture(*textures.back());
 

You should be able to easily implement this into your code and modify it to your needs. Btw make sure to delete that newly allocated memory when you don't need it anymore. Make sure when doing this to delete the sprite FIRST, THEN the texture or you'll have a null reference error when that sprite trys to access its texture. If you want to be safe and not have to worry about the mess of raw pointers, I would suggest using c++11's std::unique_ptr class to store those objects in. PM if you want help implementing this, I've done it several times and it was a pain at first!

eigenbom

  • Full Member
  • ***
  • Posts: 228
    • View Profile
Re: Issue with vector of sprites
« Reply #5 on: January 31, 2013, 03:02:03 am »
B'oh, i just wrote a reply, but it got lost to the ether. The gist of it: @darkshadowknight is wrong, the std::vector creates new memory if necessary. However his solution will also work and will solve the reference problem. :)

legacyblade

  • Newbie
  • *
  • Posts: 25
    • View Profile
Re: Issue with vector of sprites
« Reply #6 on: January 31, 2013, 03:29:14 am »
Wheew! Thanks for the replies. I really appreciate the fast help.

@Eigenbom's suggestion worked perfectly (thanks so much ^.^), but I'm going to try xDarkShadowKnightx's suggestion with the pointers, since I'd rather not have to allocate an arbitrary amount of memory whether I need that much or not.

@xDarkShadowKnightx, I've had bad luck trying to work with textures as pointers though (as sprites can't accept a pointer to a texture in their setTexture method usually), so I'm not sure if I'll be able to get that to work. But I hope it will. And I'll keep in mind to delete the sprite first. I'll just set up a destructor method for Sprite_Holder that loops through the vectors and deletes everything. If I can't get it to work, though, I'll definetly take you up on that PM offer :)

Thanks again guys. This really helped me out.



Ruckamongus

  • Jr. Member
  • **
  • Posts: 70
    • View Profile
Re: Issue with vector of sprites
« Reply #7 on: January 31, 2013, 03:42:28 am »
@xDarkShadowKnightx, I've had bad luck trying to work with textures as pointers though (as sprites can't accept a pointer to a texture in their setTexture method usually), so I'm not sure if I'll be able to get that to work.

Why not just dereference the pointer first? Like:
Sprite.setTexture(*texturePointer);

Also, if you use std::unique_ptr<sf::Texture> and std::unique_ptr<sf::Sprite> then you won't have to write the destructor and won't need to worry about memory leaks. :)

eigenbom

  • Full Member
  • ***
  • Posts: 228
    • View Profile
Re: Issue with vector of sprites
« Reply #8 on: January 31, 2013, 03:47:01 am »
No worries, glad it helped.  ;D

xDarkShadowKnightx

  • Newbie
  • *
  • Posts: 4
    • View Profile
    • Email
Re: Issue with vector of sprites
« Reply #9 on: January 31, 2013, 03:52:19 am »
eigenbom is right, I've always done that via the use of pointers though(must have read something about it a while ago..) either way should work though. And no problem! Good luck with your game!

legacyblade

  • Newbie
  • *
  • Posts: 25
    • View Profile
Re: Issue with vector of sprites
« Reply #10 on: January 31, 2013, 09:23:48 pm »
Just wanted to let ya know I got darkshadow's method working. :D Works great, and now I don't have to have my OCD nagging me that I'm using more memory than I need XD Dunno if it was something in my implantation, but it didn't work if my sprites were pointers. I'd get an error when calling it like this "window.draw(heartContainer.sprites);" (where heartContainer is an instance of Sprite_Holder and i is an existing index). But it works like a charm :D

@Ruckamongus, Oooo. I'm going to have to check out the unique_ptr later on. Sometimes it's a pain to implement the functionality to delete all the pointers, so that might save me a lot of time.

@xDarkShadowKnightX, thanks again for the help. And thanks for the wish of good luck. I really appreciate the help.

@eigenbom, even though I'm not using it here, I'm pretty sure the "reserve" function is going to come in handy somewhere down the line. I don't think I would have ever stumbled across that one, so thanks a ton for showing it to me!

eigenbom

  • Full Member
  • ***
  • Posts: 228
    • View Profile
Re: Issue with vector of sprites
« Reply #11 on: January 31, 2013, 09:32:44 pm »
Ha, but now your OCD can nag at you for having fragmented memory, instead of nice, cache-coherent blocks ;)