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

Author Topic: ["SOLVED"] Too dumb for OOP  (Read 2579 times)

0 Members and 1 Guest are viewing this topic.

Cr0n

  • Newbie
  • *
  • Posts: 10
    • View Profile
["SOLVED"] Too dumb for OOP
« on: April 28, 2020, 09:37:08 pm »
Hello!
Soo, my skills are down to storing dynamic Font objects in a linked list and letting it shoot sprites. That is nice.
Now I wanted to store Textures in a std::map. I used a std::String as key and an unique pointer to a sf::Texture as the value.

std::map<std::string, std::unique_ptr<sf::Texture>> textures;
    textures["poop"] = loadTexture("res/kugel.png");

    sf::Sprite obj;
    std::map<std::string, std::unique_ptr<sf::Texture>>::iterator it = textures.find("poop");
    obj.setTexture(*(it->second));;

Results not in the Texture but in a big white plane.
Or, more specific, if I put view.zoom(1.01) in the update loop, you begin to see a white line.
Why a white line and not the texture???


Am I using the map wrong? Am i too dumb for object oriented programming?

Because:
I also tried (first, actually) to make my own player class which inherits from sf::Sprite.

class poop : public sf::Sprite {
    public:
        poop(std::map<std::string, std::unique_ptr<sf::Texture>> textures){ //
            score = 0;
            lives = 3;
            std::map<std::string, std::unique_ptr<sf::Texture>>::iterator it = textures.find("poop");
            setTexture(*(it->second));;
            std::cout << getTexture()->isSmooth();
        }

Funfact:
THIS DOES NOT WORK
I dont even see a white screen this time. I just see black nothingness.
Its almost as I didnt even set a texture. Still, I get '0' for the isSmooth() - that does mean I set the texture, or does it?

I pass the map via move as it is supposed to be since you cant copy UNIQUE pointers (learned it the hard way)

    poop mPlayer(std::move(textures));

I really have no clue why the first thing does not work and furthermore why the own-class-thing does not work I mean I give the adress of the beginning of the wanted value (sorted to key "poop" which is a texture) to the sprite object WHY does that not work?

And where is the difference if I am now doing it in my own class that inherits from sprite? It is indeed inherited as public so that shouldnt be the problem either?


Please I think all my question are quite dumb I have no idea what im doing so maybe if you show me how it is done right maybe I know how it is right too in the future maybe I could learn from it.
Im thanking you very much for reading this :)
~Cron


---

Edit1:

If I exclude the map and try to set the Texture via loadTexture directly, I still only get a white image, but this time it has the rectangular size of the original image and is centered nicely in the middle of the screen (where it should be)
sf::Sprite obj;
    obj.setTexture(*loadTexture("res/kugel.png"));;

std::unique_ptr<sf::Texture> loadTexture(std::string filename) {
    std::unique_ptr<sf::Texture> texture(new sf::Texture());
    if (!texture->loadFromFile(filename))
        throw std::runtime_error("ResourceHolder::load - Failed to load " + filename);
    return texture;
};

I am doing something terribly wrong aint I

Edit2

I modifed it to THIS:
    obj.setTexture(*loadTexture("res/kugel.png"));;
    sf::Image f = (obj.getTexture()->copyToImage()) ;
    std::cout << f.getPixel(1,2).toInteger();

it resulted in my PC to stop working for 2 Minutes after finally throwing a seg fault.
(i doubt you need this inform. but im runnin Ubuntu 18.04 and SFML Vers. is (idk downloaded it somwhere last month)
So Map is prob. fine, but why isnt loadTexture???

« Last Edit: May 02, 2020, 03:12:33 pm by Cr0n »

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Too dumb for OOP
« Reply #1 on: April 29, 2020, 07:59:28 am »
Solution 1 should work, but you definitely need to show us more code. A complete and minimal example that reproduces the problem would be perfect, we can't guess all the hidden details.

In solution 2, the map and its textures are destroyed when the poop constructor ends.

In edit 1 and 2, the texture is destroyed right after you assign it to the sprite, because its owner unique_ptr is not stored after being returned from loadTexture, and thus destroyed.

Note than reading something from an invalid pointer, and getting something printed to the console (isSmooth() == 0), doesn't mean that the object is valid. The pointer is a memory address, and there's always something stored at a given memory address, even if it's pure garbage.
« Last Edit: April 29, 2020, 08:01:10 am by Laurent »
Laurent Gomila - SFML developer

Cr0n

  • Newbie
  • *
  • Posts: 10
    • View Profile
Re: Too dumb for OOP
« Reply #2 on: April 29, 2020, 03:27:57 pm »
Hello!
Thank you, solution 1 indeed does work, as I found out on my way to the minimum example. I was still giving the Texture Map to the poop class, and that, as you said, destroyed it!

Of course, if you dont want to destroy the map, you have to pass its adress. So the poop Constructor just changed to
poop(std::map<std::string, std::unique_ptr<sf::Texture>>* textures){ }

And the call just changed to
    poop obj(&textures);


The loadTexture function does indeed work now, too?!

  std::map<std::string, std::unique_ptr<sf::Texture>> textures;
    textures["poop"] = loadTexture("res/kugel.png");


Sorry, maybe I forgot to explain something, but calling the function like this surely doesnt dereferences the pointer?!


(Yee that isSmooth() thing really was desperate :-[)

Thanks for helping :)

Best regards
Cron

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Too dumb for OOP
« Reply #3 on: April 29, 2020, 04:29:43 pm »
Yes, changing to a pointer (even better: a const reference) makes solution 2 work as well.

Now wrap your std::map into a lightweight class and you have a so-called "texture manager" -- much more convenient to use ;)
Laurent Gomila - SFML developer

Cr0n

  • Newbie
  • *
  • Posts: 10
    • View Profile
Re: Too dumb for OOP
« Reply #4 on: May 01, 2020, 12:45:17 am »
Hello again   ::)

as I dont want to spam the Forum and the title is universal applicable:

Exactly when does .getGlobalBounds() get updated?
I have a lot of Sprites, and they are all underneath each other. The player is scrolling over them.
I want to check if the player is in a certain colored area of the Backgroundsprite.
All those BG-Sprites are stored in a linked list, "PipeRenderTargets".

(heres the code:)


    for (auto const &n : pipeRenderTargets) {
                nC++;
                if(n.getGlobalBounds().intersects(mPlayer.getGlobalBounds())) {

                    sf::Vector2<int> nTop(n.getGlobalBounds().left, n.getGlobalBounds().top);
                    if((int)(mPlayer.getPosition().x + mPlayer.getGlobalBounds().width/2 - nTop.x)>0 && (int) (mPlayer.getPosition().y + mPlayer.getGlobalBounds().height/2 - nTop.y)>0) {
                        std::cout << (int) mPlayer.getPosition().x + mPlayer.getGlobalBounds().width/2 - nTop.x << "   " << (int)mPlayer.getPosition().y + mPlayer.getGlobalBounds().height/2 - nTop.y << "\n";
                        sf::Color result = peip.getPixel((int) mPlayer.getPosition().x + mPlayer.getGlobalBounds().width/2 - nTop.x,
                                                         (int) mPlayer.getPosition().y + mPlayer.getGlobalBounds().height/2 - nTop.y);
                        c++;
                        if(result.r == 237 && result.g == 239 && result.b == 235) {
                            std::cout << "Player track";
                            std::cout << -nTop.y;
                            delayer++;
                            if (delayer == 10) {
                                delayer = 0;
                                mPlayer.setPoopColor(*new sf::Color(255, 255, 255, 255));
                            }
                        }
                        else {
                            std::cout << "Player non track " << nC;
                            std::cout << "\n" << result.r << "  " << result.g << "  " << result.b << "\n";
                            std::cout << "  NOT " << (int)mPlayer.getPosition().y << "  " << mPlayer.getGlobalBounds().height/2 << "  " << - nTop.y << "\n";

                            c++;
                            mPlayer.setPoopColor(sf::Color::Red);
                        }
                    }
                }
            }


First, I iterate through the list and check if the bounds of the Backgroundtile and the Player intersect:
If they do, I store the origin of the tile. Then, I do a sanity check if the position of the player - the tile origin position is positive. If it is, I can now check on an Image object of that tile, "peip", if the pixel at the player's position is equal to a certain color and execute code if thats the case.

Now where is my problem?
Well, when the hitbox of the player is laid over 2 Backgroundtiles at once, as it naturally happens, the wanted coordinate is larger (in y position) than the image is in height thus returning false for the color check (as some random junk from another memory adress comes out). After a lotta time debugging (yes, with cout) I came to the conclusion that nTop is not being updated before the player is for a hundred percent on the next tile.

May I show you the output of the "debugger"?

Spieler track-380
256   472
Spieler track-380
256   474
Spieler track-380
256   475
Spieler track-380
256   477
Spieler track-380
256   478
Spieler track-380
256   480

Till here it all works fine. The 2 Numbers are the calculated pixel position (x,y - the image is 480px in height).
The 380 is the position of the upper background tile.

Spieler non track 2
non track means the player isnt in the wanted pixel-color area (2 is the sprite number)
       
  NOT 840  20  -380

this is the following seperated 3 Values: playerPosition.y + half its texture length (for centering, I know there are better collision detection solutions its just a prototype) MINUS the background track position. As you can see, here lies the problem: Normally, It _should_ be updated to the new sprite position, which is -860. And sometimes, it gets updated??!?



256   481
Spieler non track 2
       
  NOT 841  20  -380
256   1
Spieler track-860
^^ like here?? ^^
256   483
Spieler non track 2
       
  NOT 843  20  -380


And sometimes not. Its really buggy. Of course I could just write "modulo 480" and the bug would go away, but I really wanna understand why it updated nTop as it pleases. One could say it bugs me personally in a way.

Have a nice day
Cron

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Too dumb for OOP
« Reply #5 on: May 01, 2020, 08:45:02 am »
Quote
as I dont want to spam the Forum
You don't. It's much worse to mix multiple unrelated issues in a single post. And you should not be proud of the "universality" of your title ;) a detailed title that describes the problem is more efficient; imagine everybody using such titles, and a forum full of "I need help" subjects.

Quote
Exactly when does .getGlobalBounds() get updated?
It's always up-to-date.

Sorry I gave up reading everything ;D
Laurent Gomila - SFML developer