SFML-Bilder aus dem Speicher löschen



  • Hi Leute,
    eine Frage, wo ich bisher einfach keine Antwort darauf finden konnte:
    Es geht einfach darum z.B. Bild 1 "Map0.png" aus dem Speicher zu löschen. Habe - wie unten kommentiert - drei verschiedene Ansätze probiert, aber es hat nicht geklappt. Wie macht man das richtig? Am besten so, dass man das Bild bei Bedarf wieder laden kann, also dass nicht das Array oder so unwiderbringlich beschädigt wird 🙂

    #include <SFML/Graphics.hpp>
    #include <iostream>
    #include <array>
    
    int main()
    {
        sf::RenderWindow mMainWindow(sf::VideoMode(1000,500), "Map", sf::Style::Close);
    	sf::View view(sf::Vector2f(500, 300), sf::Vector2f(1000, 500));
    	mMainWindow.setFramerateLimit(60);
    	mMainWindow.setKeyRepeatEnabled(false);
    
    	sf::Image ImageName;
    	sf::Texture TextureName;
    	sf::Sprite SpriteName;
    
    	std::array<sf::Image, 4> ImageArray = { {ImageName, ImageName, ImageName, ImageName} };
    	std::array<std::string, 4> FileNameArray = { {"map0.png", "map1.png", "map2.png", "map3.png"} };
    	std::array<sf::Texture, 4> TextureArray = { {TextureName, TextureName, TextureName, TextureName} };
    	std::array<sf::Sprite, 4> SpriteArray = { {SpriteName, SpriteName, SpriteName, SpriteName} };
    	std::array<sf::Vector2i, 4> TexturePositionArray = { {sf::Vector2i(0,0), sf::Vector2i(500,0), sf::Vector2i(1000,0), sf::Vector2i(1500,0)} };
    
    	for(int i=0; i<4; i++)
    	{
    		(ImageArray[i]).loadFromFile((FileNameArray[i]));
    		(TextureArray[i]).loadFromImage((ImageArray[i]));
    		(SpriteArray[i]).setPosition(TexturePositionArray[i].x,TexturePositionArray[i].y);
    		(SpriteArray[i]).setTexture((TextureArray[i]));
    	}
    
        while (mMainWindow.isOpen())
        {
            sf::Event event;
    		sf::Vector2i mousePos;
    		bool leftclicked = false;
    
            while (mMainWindow.pollEvent(event))
            {
                switch (event.type)
                {
                case sf::Event::Closed:
                    mMainWindow.close();
                    break;
    			case sf::Event::MouseButtonPressed:
                    if (event.mouseButton.button == sf::Mouse::Left) 
    				{
    					leftclicked = true;
    					mousePos = sf::Vector2i(event.mouseButton.x, event.mouseButton.y);
    					break;
    				}
                }
    		}
    		if(leftclicked)
    		{
    			//1. Versuch:
    			//for (int i = 0; i < 4; i++)
    			//{
    			//	ImageArray[i].loadFromFile("tileseta.png") ;     //Durch weiße Textur ersetzen: Textur muss gleiche Größe haben, sonst Fehler bei Kollisionsabfrage usw. (hier nicht implementiert)
    			//	TextureArray[i].loadFromImage(ImageArray[i]);	 // Speicherbeanspruchung steigt sogar noch um ca. 50 %, statt zu sinken.
    			//	SpriteArray[i].setTexture((TextureArray[i]));
    			//}
    
    			//2. Versuch:
    			//ImageArray[0] = sf::Image();                         //Grafik immer noch sichtbar, Speicherbeanspruchung bleibt gleich......
    
    			//3. Versuch:
    			ImageArray[0].loadFromFile("");						   //Grafik immer noch sichtbar, Speicherbeanspruchung bleibt gleich......
    		}
            mMainWindow.clear();
    		mMainWindow.setView(view);
    		for(int i=0; i<4; i++)
    		{
    			mMainWindow.draw(SpriteArray[i]);
    		}
            mMainWindow.display();
        }
        return 0;
    }
    


  • Hallo,

    wozu brauchst du die Images?
    Egal, jedenfalls sind die Bilddaten von Images im Hauptspeicher und von Textures auf der Grafikkarte.

    Um ein Bild im Texture zu ändern müsstes du sowas machen:

    sf::Image img;
    img.loadFromFile("blank.png");
    texture.update(img.getPixelsPtr());
    

    Sinnvoll erscheint mir das aber nicht.



  • Der Code würde dann in dem Fall hier so aussehen?:

    if(leftclicked)
    		{
    
    			ImageArray[0].loadFromFile("white.png");
    			TextureArray[0].update(ImageArray[0].getPixelsPtr());
    
    //white.png is ein weißes Bild in der gleichen Größe wie die ursprüngliche //Bilddatei, weil es ansonsten irgendwie nicht funktioniert.
    		}
    

    Wenn ich das so mache, dann steigt der Speicherbedarf deutlich (bei Schleife durch alle 4 Elemente sogar um 50 %).

    Es geht um ein Projekt, wo ich viele Images für ne große Karte brauche, um die RGB-Pixel-Farbe festzustellen. Nur dafür brauche ich das. Anhand der Sprites lässt sich das ja nicht machen. Auch Kollisionsabfrage ist in SFML nur über grobe Quadrate möglich, brauche aber Genauigkeit bis zum einzelnen Pixel. Es geht um ein Provinzsystem wie in den Paradox-Spielen, also Victoria, Hearts of Iron und wie sie alle heißen.....Wie wird das dort mit den Provinzen gemacht? Die haben bestimmt auch ne Colormap, wo jede Provinz ihre eigene RGB-Kombination hat, um diese eindeutig zu identifizieren.



  • Für Pixel-genaue-Kollisionsabfragen brauchst aber nicht das komplette Bild, sondern nur Pixel an/aus.

    Siehe sf::Texture-Reference:

    Since they live in the graphics card memory, the pixels of a texture cannot be accessed without a slow copy first. And they cannot be accessed individually. Therefore, if you need to read the texture's pixels (like for pixel-perfect collisions), it is recommended to store the collision information separately, for example in an array of booleans.

    Wobei 'array of booleans' noch verkleinert werden kann.
    Das würde den Speicherverbrauch schonmal deutlich reduzieren.
    Keine Ahnung wie Paradox das macht, denke aber so ähnlich.



  • Die Abfrage mach ich beim Image (image.getPixel....), nicht bei der Texture. Ich glaub bei der Textur geht das auch gar nicht. Ich finde keinen solchen Code.

    Außerdem brauch ich mehrere Tausend Provinzen, von denen jede per Klick einzeln identifizierbar sein muss, also brauch ich mehrere Tausend unterscheidbare Farben. Kann man das tatsächlich mit einem bool Array realisieren? Kann mir das rein logisch überhaupt nicht vorstellen. Hab ein Array in der Art auch versuchsweise mal gemacht, aber mit short integers, also mit Maus auf Pixel klicken und er zeigt die Identifikationsnummer an der entsprechenden Pixelposition, die mit der Position im Array korrespondiert, wo eben die jeweiligen Id-Nummern gespeichert sind, die eben eine bestimmte Provinz oder so repräsentieren, jedoch müsste man da rund 2,5 Mrd. Nummern per Hand eintragen (das würde 100 Jahre dauern, weil da keine for-Schleife mehr hilft; ich müsste das sozusagen alles per Hand Pixel für Pixel eintragen), geschweige denn, dass das Array diese Dimensionen (40.000*20.000 Pixel mindestens) nicht packen würde.
    Oder verstehe ich da etwas falsch?
    Wie kann man trotzdem das Image aus dem Speicher laden?



  • Nachtrag:
    Mit "Image aus dem Speicher laden" meine ich natürlich "entladen", also "unload".



  • Ich verstehe dein vorgehen immer noch nicht.
    Warum der Speicherverbrauch bei einem neuen Image.loadFromFile ansteigt, weiss ich nicht. (Wobei ich auch weiterhin nicht weiss, warum du das überhaupt nochmal lädst. Lad doch die Texture fromFile und ins Image direkt das richtige File).

    Nochmal ne Erklärung zum Bool-Array:
    Du klickst auf die Karte an pos x/y;
    mit x/y kannst du schnell rausfinden auf welche Sprites geklickt wurde (Punkt in Sprite-Rect?);
    Mit den paar Sprites die in Frage kommen, machst du mit dem Bool-Array 'pixel-perfect collisions'.
    Hat nichts mit deinem Farbenansatz zu tun.



  • So?:

    ImageArray[0].loadFromFile("map0.png");
    			TextureArray[0].loadFromFile("white.png");
    

    Speicherbedarf steigt immer noch massiv an, anstatt zu sinken.

    Wegen Sprite-Kollisionsabfrage: Das Anklicken von Sprites fällt weg, weil die eben nur dieses grobe Kollisionsquadrat drumherum haben und sich das überhaupt nicht eignet für individuell geformte Sprites, die dicht beieinander liegen. Hab das mal probiert und war da deutlich in der falschen Provinz. Es hilft nichtmal bestimmte Bereiche unsichtbar zu machen, weil sie dann trotzdem als zum Kollisionsquadrat zugehörig zählen. Daher der Ansatz mit dem Image, weil das auch schnell in paint.net zu malen und einzufärben ist. Die Sprites verwende ich eben zum Einfärben mit "Besitzer"-Farbe.



  • Ok, dann kann ich dir leider nicht weiter helfen.
    Ich verstehe deinen Ansatz nicht (theoretisch schon, aber nicht von deinem Code her) und du offenbar meinen nicht.

    Mit den paar Sprites die in Frage kommen, machst du mit dem Bool-Array 'pixel-perfect collisions'.

    Wenn du 'pixel-perfect collisions' für nicht geeignet für individuell geformte Sprites hälst, dann verstehst du was falsch.



  • Also ich kann mit SFML die Indizes der angeklickten Sprites (SpriteRect)ausgeben, z.B. wenn 2 Sprites übereinander liegen, dann zeigt er mir die 2 richtigen Nummern der Sprites aus dem Vektor an.

    Ein 2d-Array manuell zu erstellen ist auch kein Problem, aber wie fülle ich es mit den richtigen Werten, ohne das per Hand Pixel für Pixel (bei den abertausenden Pixeln) aufbauen zu müssen. Was ich auch nicht verstehe, warum bool dafür ausreichen soll. Oder verstehe ich da was falsch? Bitte die Schritte erklären!



  • Der Code funktioniert wie folgt:

    - Anhand der Bounding-Boxen ermitteln welche Sprites getroffen worden sein könnten
    - Für jedes Sprite in dieser Liste:
      - Geklickten Punkt in das Koordinatensystem des Sprites transformieren
      - Nachgucken ob das Sprite an diesem Punkt klickbar ist oder nicht
    

    Und jetzt überleg dir warum ein bool Array für den letzten Schritt ausreicht.



  • Ok, bool klingt in dem Zusammenhang logisch 🙂
    Was ist aber gemeint mit "geklickten Punkt ind das Koordinatensystem des Sprites transformieren". Eine x,y Position der Maus im Fenster lässt sich ja leicht feststellen, aber was dann? Geht es da um eine spezielle Funktion von SFML?



  • Ne, einfach Position des Sprites abziehen 😉

    Wenn das Sprite an Position 100,100 angezeigt wird, und die Maus an Position 110, 110 ist, dann musst du in der Collision-Mask des Sprite natürlich an Position 10,10 nachsehen, nicht an Position 110,110.



  • Ok, soweit verstehe ich das, aber wie baut man sich diese "Collision-Mask"? Ein Array Pixel für Pixel per Hand befüllen kann ich mir schwer vorstellen. Gibt es da irgendwelche Funktionen dafür, die so ein sprite (.png) umwandeln können? SFML bietet sowas nicht.



  • Ich glaub ich weiß jetzt was ich brauch. Es geht darum aus einer png die transparenten und nicht transparenten Bereiche auszulesen, also transparent=0 und non-transparent=1 und daraus z.B. ein 2d Array zu erstellen.

    Bei den Sprites würden sich ohnehin nur die transparenten Bereiche überlappen. Wenn man die aber für die Kollision deaktiviert, also Array an den entsprechendne Stellen auf 0 setzt, dann würde es gehen. Gibt es da spezielle Bibliotheken für C++/Win 7, welche die Pixel einer png Bilddatei in Bezug auf ihrer Transparenz auslesen können und daraus ein sauberes 2d bool Array machen?


Anmelden zum Antworten