SFML community forums

Help => Graphics => Topic started by: Kaev on October 22, 2012, 01:11:52 pm

Title: Cant draw Tilemap with Class
Post by: Kaev on October 22, 2012, 01:11:52 pm
Hello!

First: Sry for my bad english  :P
I wrote a class to load a map from my map1.txt.
Every value in the maparray is correct.
But i cant see anything in my window except my playercharacter and a black screen behind him, not one single tile.

#include <iostream>
#include <fstream>
#include <vector>
#include <SFML/Graphics.hpp>

using namespace std;

class Map
{
        public:
                int map_x, map_y;
                static int tile;
                int map[100][100];
                sf::Image image;
                sf::Texture textur;  
                vector<sf::Sprite> tiles;
                sf::RenderTexture rMap;
                sf::Sprite sMap;

        public:

                Map()
                {
                        map_x = 0;
                        map_y = 0;  
                       
            image.loadFromFile("graphics/boden.png");
            if(!image.loadFromFile("graphics/boden.png"))
            {
                std::cout << "boden.png konnte nicht geladen werden!\n";
            }
            textur.loadFromImage(image);
                };

                ~Map(){};

                void loadMap(const char *filename)
                {
                        int loadCounterX = 0, loadCounterY = 0;
                        std::ifstream file(filename);
                        if(file.is_open())
                        {
                                file >> map_x >> map_y;
                                while(!file.eof())
                                {
                                        file >> map[loadCounterX][loadCounterY];
                                        loadCounterX++;
                                        if(loadCounterX >= map_x)
                                        {
                                                loadCounterX = 0;
                                                loadCounterY++;
                                        }
                                }
                        }
                       
                        for(int x = 0; x < map_x; x++)
                        {
                                for(int y = 0;y < map_y; y++)
                                {
                                        int tileId = map[x][y];
                                        sf::Sprite tile;
                                        tile.setTexture(textur);
                                        tile.setTextureRect(sf::IntRect(map[y][x]*32, 0, 32, 32));
                                        tile.setPosition(x*32, y*32);
                                        tiles.push_back(tile);
                                }
                        }

                        for(int x = 0; x < tiles.size(); x++)
                        {
                                rMap.draw(tiles.at(x));
                                cout << "x";

                        }
                }

                sf::Sprite drawMap()
                {
                        rMap.display();

                        sMap.setTexture(rMap.getTexture());
                        return sMap;

                }
};
 

first i called this out of the mainloop:
Map map1;
map1.loadMap("map1.txt");
 

And in my mainloop i call:
Spiel.draw(map1.drawMap());
 

Hope you can help me.

Greetings!
Title: Re: Cant draw Tilemap with Class
Post by: eXpl0it3r on October 22, 2012, 01:27:38 pm
I though I knew that problem from somewhere (spieleprogrammierer)... ;D

The problem I think lies in the call sMap.setTexture(rMap.getTexture());, because you set a new texture but you don't change the size of the texture rectangle, so you actually don't cut out anything from the render texture. (I think Schorsch already tried to point you in that direction).
Try changing it to: sMap.setTexture(rMap.getTexture(), true);
Also you shouldn't return a copy of the sprite when calling drawMap() but instead use a const reference. ;)
Title: Re: Cant draw Tilemap with Class
Post by: Kaev on October 22, 2012, 01:58:47 pm
I though I knew that problem from somewhere (spieleprogrammierer)... ;D

The problem I think lies in the call sMap.setTexture(rMap.getTexture());, because you set a new texture but you don't change the size of the texture rectangle, so you actually don't cut out anything from the render texture. (I think Schorsch already tried to point you in that direction).
Try changing it to: sMap.setTexture(rMap.getTexture(), true);
Also you shouldn't return a copy of the sprite when calling drawMap() but instead use a const reference. ;)

:P

sMap.setTexture(rMap.getTexture(), true);
Still everything black. :/

const sf::Sprite sMap
Error at
sMap.setTexture(rMap.getTexture(), true);
Error: The object has type qualifiers that are not compatible with the member function
(German error: Error: Das Objekt weist Typqualifizierer auf, die nicht mit der Memberfunktion kompatibel sind)

Title: Re: Cant draw Tilemap with Class
Post by: Laurent on October 22, 2012, 02:03:43 pm
He means that:
const sf::Sprite& drawMap()
(you probably won't notice the difference on the performances though...)

Displaying and assigning the render-texture should only be done once, after drawing. This way, your getter can be const. And it should really have a different name. Calling "draw" something that only returns an object is misleading.
void loadMap(const char *filename)
{

    ...

    rMap.display();
    sMap.setTexture(rMap.getTexture());
}

const sf::Sprite& getSprite() const
{
    return sMap;
}
 

Additionnaly, an even better design would be to let the map draw itself, this way it can hide completely all the drawing details and provide a cleaner API to the caller:
void draw(sf::RenderTarget& target) const
{
    target.draw(sMap);
}

And... please fix your indentation, some parts of your code are hardly readable.
Title: Re: Cant draw Tilemap with Class
Post by: Kaev on October 22, 2012, 02:17:02 pm
#include <iostream>
#include <fstream>
#include <vector>
#include <SFML/Graphics.hpp>

using namespace std;

class Map
{
        public:
                int map_x, map_y;                       // width, height
                int map[100][100];                      // Maparray
                sf::Image image;                        // Image Tilemap
                sf::Texture textur;                     // Texture from Image Tilemap
                vector<sf::Sprite> tiles;               // Tilevector
                sf::RenderTexture rMap;         // Texture sMap
                sf::Sprite sMap;                        // Mapsprite

        public:

                Map()
                {
                        map_x = 0;
                        map_y = 0;  
                       
                        // Loading Texture
                        image.loadFromFile("graphics/boden.png");
                        if(!image.loadFromFile("graphics/boden.png"))
                       {
                              std::cout << "boden.png konnte nicht geladen werden!\n";
                       }
                       textur.loadFromImage(image);
                };

                ~Map(){};

                // Just tilesize
                static int getTile()
                {
                        return 32; // 1 Tile = 32 Pixel
                }

                void loadMap(const char *filename)
                {
                        // Load map from filename, first line width and height, other lines: id of tile
                        int loadCounterX = 0, loadCounterY = 0;
                        std::ifstream file(filename);
                        if(file.is_open())
                        {
                                file >> map_x >> map_y;
                                while(!file.eof())
                                {
                                        file >> map[loadCounterX][loadCounterY];
                                        loadCounterX++;
                                        if(loadCounterX >= map_x)
                                        {
                                                loadCounterX = 0;
                                                loadCounterY++;
                                        }
                                }
                        }
                       
                        // Create tiles
                        for(int x = 0; x < map_x; x++)
                        {
                                for(int y = 0;y < map_y; y++)
                                {
                                        int tileId = map[x][y];
                                        sf::Sprite tile;
                                        tile.setTexture(textur);
                                        tile.setTextureRect(sf::IntRect(map[y][x]*32, 0, 32, 32));
                                        tile.setPosition(x*32, y*32);
                                        tiles.push_back(tile);
                                }
                        }

                        // Draw tiles on rMap
                        for(int x = 0; x < tiles.size(); x++)
                        {
                                rMap.draw(tiles.at(x));

                        }

                        rMap.display();
                        sMap.setTexture(rMap.getTexture(), true);
                }

                // get Mapsprite
                const sf::Sprite& getSprite() const
                {
                        return sMap;

                }

                //draw Mapsprite on target
                void draw(sf::RenderTarget& target) const
                {
                        target.draw(sMap);
                }
};
 

Still everything black. :/
Indentation is changed a bit by the forum, sorry for this.
Title: Re: Cant draw Tilemap with Class
Post by: eXpl0it3r on October 22, 2012, 02:26:34 pm
You should provide a minimal and complete example and include a map file, so we could test everything... ;)
Title: Re: Cant draw Tilemap with Class
Post by: Kaev on October 22, 2012, 02:35:48 pm
map1.txt is like:
3 3
0 2 1
2 0 1
 

main.cpp (copied important lines from mine):
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include <SFML/System.hpp>
#include "map.cpp"

sf::RenderWindow window(sf::VideoMode(800, 608, 32), "Spiel", sf::Style::Close);
Spiel.setVerticalSyncEnabled(true);

Map map1;
map1.loadMap("map1.txt");

while (window.isOpen())
{
        sf::Event event;
        while (window.pollEvent(event))
        {
                        switch(event.type)
                        {
                        case sf::Event::Closed:
                                window.close();
                                break;
                        default:
                                break;
                        }
                }

          window.clear();
          map1.draw(window);
          window.display();
          }
}

return EXIT_SUCCESS;
}
 

map.cpp:
see above

EDIT:
You should fix the code-tag, everytime it ruins my indentation :P
Title: Re: Cant draw Tilemap with Class
Post by: Laurent on October 22, 2012, 02:45:29 pm
Quote
You should fix the code-tag, everytime it ruins my indentation
The code tag is not broken, it shows exactly what you type.
Title: Re: Cant draw Tilemap with Class
Post by: eXpl0it3r on October 22, 2012, 03:41:26 pm
The whole problem was that you never created the render texture, thus it had a size of (0,0) and obviously nothing gets displayed...

Here's a fully working code. I cleaned up your strange indentation (now uses tabs) and removed the useless vector thingy. I mean there's no sense in reading it into one vector if you're going to draw it once to a render texture and then use that texture. You don't need the sprites objects later. I also fixed the possibility that the program crashes if the map file doesn't exists and a few other minor things...
You should really stop just trying to 'solve' things by programming and rather sit down and think about what you're doing. ;)

Edit: Hmm the indentation doesn't look that nice now either...
@Laurent: Is it really necessary that one tab gets translated to 8 spaces? :O

#include <iostream>
#include <fstream>
#include <SFML/Graphics.hpp>

class Map
{
public:
        int map_x, map_y;       // width, height
        int map[100][100];      // Maparray
        sf::Texture textur;     // Texture from Image Tilemap
        sf::RenderTexture rMap; // Texture sMap
        sf::Sprite sMap;        // Mapsprite

public:

        Map() :
                map_x(0),
                map_y(0)
        {
                // Loading Texture
                if(!textur.loadFromFile("boden.png"))
                {
                        std::cout << "boden.png konnte nicht geladen werden!\n";
                }
        };

        // Just tilesize
        static int getTile()
        {
                return 32; // 1 Tile = 32 Pixel
        }

        void loadMap(const char *filename)
        {
                // Load map from filename, first line width and height, other lines: id of tile
                int loadCounterX = 0, loadCounterY = 0;
                std::ifstream file(filename);

                // Exit if file doesn't exist
                if(!file.is_open())
                {
                        std::cout << "Couldn't open '" << filename << "'. Aborting." << std::endl;
                        return;
                }

                file >> map_x >> map_y;
                while(!file.eof())
                {
                        file >> map[loadCounterX][loadCounterY];
                        loadCounterX++;
                        if(loadCounterX >= map_x)
                        {
                                loadCounterX = 0;
                                loadCounterY++;
                        }
                }

                // Create the off screen texture
                rMap.create(map_x*32, map_y*32);
                rMap.clear();

                // Create tiles
                for(unsigned int y = 0; y < map_y; ++y)
                        for(unsigned int x = 0; x < map_x; ++x)
                        {
                                sf::IntRect ir(map[y][x]*32, 0, 32, 32);
                                sf::Sprite sprite(textur, sf::IntRect(map[y][x]*32, 0, 32, 32));
                                sprite.setPosition(x*32, y*32);
                                rMap.draw(sprite);
                        }

                rMap.display();

                rMap.getTexture().copyToImage().saveToFile("test.png");
                sMap.setTexture(rMap.getTexture(), true);
        }

        // get Mapsprite
        const sf::Sprite& getSprite() const
        {
                return sMap;
        }

        //draw Mapsprite on target
        void draw(sf::RenderTarget& target) const
        {
                target.draw(sMap);
        }
};

int main()
{
        // Create window
        sf::RenderWindow window(sf::VideoMode(800, 600), "SFML");
        // Limit framerate
        window.setFramerateLimit(60);

        Map mm;
        mm.loadMap("map1.txt");

        while(window.isOpen())
        {
                // Event handling
                sf::Event event;
                while(window.pollEvent(event))
                {
                        if(event.type == sf::Event::Closed)
                                window.close();
                }

                // Render
                window.clear();
                mm.draw(window);
                window.display();
        }
}
 
Title: Re: Cant draw Tilemap with Class
Post by: Laurent on October 22, 2012, 03:59:50 pm
Quote
@Laurent: Is it really necessary that one tab gets translated to 8 spaces? :O
The mod doesn't allow to change the tab width through the admin panel. I have to add a line of code to the mod sources.

And if I change the mod settings so that tabs are not translated to spaces, I lose line breaks :(
Title: Re: Cant draw Tilemap with Class
Post by: eXpl0it3r on October 22, 2012, 04:05:51 pm
The mod doesn't allow to change the tab width through the admin panel. I have to add a line of code to the mod sources.
I guess/hope shouldn't be that hard...
I'd suggest a 1 tab = 4 spaces translation, which is what you mostly get as default setting in many editors.

And if I change the mod settings so that tabs are not translated to spaces, I lose line breaks :(
Bad mod! :P
Title: Re: Cant draw Tilemap with Class
Post by: Laurent on October 22, 2012, 04:23:50 pm
Quote
I guess/hope shouldn't be that hard...
Yep, I'll try that as soon as I can.
Title: Re: Cant draw Tilemap with Class
Post by: Kaev on October 22, 2012, 04:30:32 pm
Thanks eXpl0it3r, everything works fine now.

@Laurent:
My indentation in VC++ is made with tabs, not spaces. After copy it into the codefunction, everything was messy so i had to clean it with spaces. That's why it looks so bad here.

Problem is fixed and thread can be closed now.
Thank you!
Title: Re: Cant draw Tilemap with Class
Post by: Laurent on October 22, 2012, 04:33:54 pm
Quote
My indentation in VC++ is made with tabs, not spaces. After copy it into the codefunction, everything was messy so i had to clean it with spaces. That's why it looks so bad here.
I was not talking about the indentation length, but about things that were not properly aligned although they belong to the same level of indentation (the texture loading in your first post, the event loop in your last post, etc.).
Title: Re: Cant draw Tilemap with Class
Post by: Laurent on October 22, 2012, 08:51:37 pm
Quote
I'd suggest a 1 tab = 4 spaces translation
Done :)
Title: Re: Cant draw Tilemap with Class
Post by: eXpl0it3r on October 22, 2012, 08:57:41 pm
Quote
I'd suggest a 1 tab = 4 spaces translation
Done :)
You're awesome! :)

Btw would it be possible to install the tapatalk mod, so one could read the forum easily on the phone?
Title: AW: Re: Cant draw Tilemap with Class
Post by: eXpl0it3r on October 23, 2012, 01:58:56 am
Btw would it be possible to install the tapatalk mod, so one could read the forum easily on the phone?
Ha! This is working great, thanks a lot! :-)
Title: Re: Cant draw Tilemap with Class
Post by: Laurent on October 23, 2012, 06:43:04 am
You're faster than light ;D

I'll open a new topic so that we can stop polluting this one.