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

Author Topic: Issue with sprites  (Read 1551 times)

0 Members and 1 Guest are viewing this topic.

tometherton

  • Newbie
  • *
  • Posts: 3
    • View Profile
Issue with sprites
« on: October 08, 2023, 08:43:00 am »
Hello all,

I've recently started picking up C++ programming again to make a very simple game for my daughter. I know that my code is not elegant and I am probably missing out on some of the best practices but I'm hoping someone might be able to help me with an issue I've been having. I've been trying to set up loading in of sprites so that it can be a bit more flexible than how I had it (with the filenames hardcoded into the program, now accessing the filenames from a resources text file).

However, since I've swapped to that it will only render one Sprite and no more. I've included what I think are the relevant bits of code below but I am aware I've probably missed some!

I'm using Thor library to load in and store the textures so they only get loaded in once.

Cutscene.cpp (this is the section for loading in the sprites - m_sprites is a linked list of type sf::Sprite)
//Sets up a file reader and variables needed to read in from it
MyFileReader* sreader = new MyFileReader;

char* buffer = new char;
unsigned int size = 0;

//Reads all the information into a buffer to scan from later
const char* fileName = "Assets/cutscenes/Cutscene1Sprites.txt";
if (sreader->Exists(fileName))
{
    sreader->OpenFile(fileName, "r");
    sreader->ReadBuffer(buffer, size);
    sreader->CloseFile();
}
else
{
    MessageBox(nullptr, TEXT("There was an error with the file, please check the game assets"), TEXT("File Error"), MB_OK);
    delete sreader;
}

unsigned int counter = 0;

int t = 0;

while (counter < size)
{
    sf::Sprite* sprite = new sf::Sprite;
    sf::Texture* tex = new sf::Texture;

    //Reads the type of sprite (background, character or object)
    const char* type = sreader->Scanfile(buffer, counter, size);

    //Reads the name of the sprite
    const char* name = sreader->Scanfile(buffer, counter, size);

    //Reads in the path for this sprtie
    const char* spath = sreader->Scanfile(buffer, counter, size);

    //Reads in the position (x then y) for this sprite
    float posX = atof(sreader->Scanfile(buffer, counter, size));
    float posY = atof(sreader->Scanfile(buffer, counter, size));

    //Reads in the scale (x then y) for this sprite
    float scaleX = atof(sreader->Scanfile(buffer, counter, size));
    float scaleY = atof(sreader->Scanfile(buffer, counter, size));

    //Loads in the texture and sets it to the sprite
    try
    {
        tex = &m_holder.acquire(name, thor::Resources::fromFile<sf::Texture>(spath));
    }
    catch (thor::ResourceAccessException)
    {
        tex = &m_holder[name];
    }
    sprite->setTexture(*tex);
    sprite->setPosition(sf::Vector2f(posX, posY));
    sprite->setScale(sf::Vector2f(scaleX, scaleY));

    m_sprites.Insert(sprite);
}
 

Cutscene.cpp (this is when it enters this GameState
//Adds the sprites for this GameState
LinkedListNode<sf::Sprite*>* spritenode = m_sprites.GetFirst();
while (spritenode)
{
    SpriteManager::GetInstance()->AddSprite(spritenode->GetData());
    spritenode = spritenode->GetNext();
}

SpriteManager.cpp (this is where it adds the sprites - m_currentSprites is a Linked List which is cleared every time a GameState swaps)
void SpriteManager::AddSprite(sf::Sprite* sprite)
{
        m_currentSprites.Insert(sprite);
}

WindowManager.cpp (this is the main draw function that is called each update loop - m_window is a sf::RenderWindow, GetSprites() returns the first in the Linked List of m_currentSprites referred to earlier)
m_window.clear(sf::Color::Green);

LinkedListNode<sf::Sprite*>* node;
node = SpriteManager::GetInstance()->GetSprites();

sf::RenderTexture renderTex, blurTex;
renderTex.create(m_window.getSize().x, m_window.getSize().y);
renderTex.clear(sf::Color::Green);

while (node)
{
    renderTex.draw(*node->GetData());
    node = node->GetNext();
}

renderTex.display();

//Draws the render texture to the window
const sf::Texture& texture = renderTex.getTexture();
sf::Sprite windowS(texture);

//Draw the render texture to the window
m_window.draw(windowS);

m_window.display();

 

I'm aware that I might not have included some code which might be helpful but this is my first time posting and I didn't want to upload half a Visual Studio Solution and make it too long!

Thanks for your time,
Tom

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11045
    • View Profile
    • development blog
    • Email
Re: Issue with sprites
« Reply #1 on: October 10, 2023, 08:00:35 am »
With the code/the possibility to debug this, it's rather tricky to understand the data flow.

sf::Sprite hold a "reference" to the texture they use. Through all the copying and reassigning of dynamically allocated objects, make sure that those references stay alive.

However, since I've swapped to that it will only render one Sprite and no more.
I think you may need to expand on this a little. Does it mean, that you get just a single sprite draw ever, or that every sprite uses the same texture, or that most sprites render as white squares?

If it's really just drawing one sprite, it would seem either your linked list implementation is flawed, or you're not storing the sprites correctly.

As some general advice, you should define a clear ownership model, who is the owner of the sprites, etc.
I also recommend to reduce the usage of raw pointers to an absolute minimum. Use stack object and references where possible.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

tometherton

  • Newbie
  • *
  • Posts: 3
    • View Profile
Re: Issue with sprites
« Reply #2 on: October 10, 2023, 11:48:05 am »
Thank you so much for your response, much appreciate the help and advice.

sf::Sprite hold a "reference" to the texture they use. Through all the copying and reassigning of dynamically allocated objects, make sure that those references stay alive.

I've now added in some member variables to the class Cutscene to hold the Sprite and the Texture (I'm sure this isn't the most memory or speed efficient way of doing it but I'd like to get it working first!)

I think you may need to expand on this a little. Does it mean, that you get just a single sprite draw ever, or that every sprite uses the same texture, or that most sprites render as white squares?

If it's really just drawing one sprite, it would seem either your linked list implementation is flawed, or you're not storing the sprites correctly.

I'm loading in the background sprite which is the same size as the window but then the other sprite I load in (Jonathan) is not displaying at all. Even if I comment out the code to insert the background sprite into the Linked List it doesn't work - logically that means the Linked List only contains the Jonathan sprite but it still doesn't draw it...

As some general advice, you should define a clear ownership model, who is the owner of the sprites, etc.
I also recommend to reduce the usage of raw pointers to an absolute minimum. Use stack object and references where possible.

Thank you, that's really helpful and I am always grateful to receive advice on how to improve the code! I know it probably isn't particularly elegant. I've implemented the member variables I mention above (the Sprite and Texture ones) without using pointers and then just add a pointer to that Sprite to the Linked List.

Regarding ownership of Sprites do you mean which class they belong to? In a previous project (which was many years ago now and I've forgotten and learnt a lot in between!) I had a SpriteManager class which was responsible for everything including the Render. However, I decided to implement that slightly differently this time and hoping that I can keep the Sprites associated with each class so that they can be loaded in a bit more efficiently rather than loading them all at the start!

Thanks again and I am happy to put up some more bits of code if you think they would be helpful. In the meantime I'm going to have another little think about it.

tometherton

  • Newbie
  • *
  • Posts: 3
    • View Profile
Re: Issue with sprites
« Reply #3 on: October 10, 2023, 12:38:38 pm »
I have discovered what the problem was...  :-\

Loading the information in I had a file which has a ; between each value (so it has type of sprite, name of sprite, x-position, y-position, x-scale and y-scale).

It loaded the first sprite in fine but the second one wasn't loading the y-scale in correctly - it thought it had reached the end of the file.

By putting a line break after each ; it has fixed it. I am not sure why, if you have any insights as to why that might be I'd be interested but for now I am just going to put a line break in after each value! And thank you so much for your help, I still appreciate the guidance you gave me on my code and I've moved away from using the pointers as much now.

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11045
    • View Profile
    • development blog
    • Email
Re: Issue with sprites
« Reply #4 on: October 10, 2023, 01:54:23 pm »
Not sure how your file reader class works. Maybe it's implemented in a way that it expects a whitespace/newline somewhere.
Personally, I'd go with some more common format like toml, json, yaml, ini, etc.
There are many great libraries out there for those. I often used nlohmann json, but there are also simpler things out there.

By putting a line break after each ; it has fixed it. I am not sure why, if you have any insights as to why that might be I'd be interested but for now I am just going to put a line break in after each value! And thank you so much for your help, I still appreciate the guidance you gave me on my code and I've moved away from using the pointers as much now.
Glad to hear it helped! :)

Maybe another tip is to avoid const char* for strings. Yes, they will still be char arrays when compiling (or you could use string literals if you wanted), but from there on you really should just be using std::string instances when dealing with strings.
« Last Edit: October 10, 2023, 01:56:26 pm by eXpl0it3r »
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/