2
votes

I have some SFML 2.0 code, where I draw a robot which moves in a grid. Grid is drawn using OpenGL, the robot image is loaded using sf::Texture. I have some code that makes walls on user left mouse click (no collision detection). I made a function which erases them on right click. Walls are stored in sf::Sprite, then put into std::list and drawn in a loop. When I call list.erase() program segfaults. Debugger shows some problem in sf::transformable = operator. How to fix that.

Here is the code:

#include <SFML/Graphics.hpp>
#include <SFML/OpenGL.hpp>
#include <iostream>
#include <list>
using namespace std;
static const size_t WIN_HEIGHT=800, WIN_WIDTH=800;
void drawGrid();
void fixCoords(int & x, int & y);
static list<sf::Sprite> walls;

int main()
{
    // Create the main window
    sf::RenderWindow window(sf::VideoMode(WIN_WIDTH, WIN_HEIGHT), "SFML window");

    /*** Robot code ***/
    sf::Image robotImg;
    robotImg.loadFromFile("robot.png");
    robotImg.createMaskFromColor(sf::Color(89, 167, 45));
    sf::Texture robotTexture;
    robotTexture.loadFromImage(robotImg);
    sf::Sprite robotSpr(robotTexture);
    sf::Sprite t;
    robotSpr.setPosition(sf::Vector2f(400, 405));

    /*****  Wall code  ****/
    int x = 0, y = 0;
    sf::Image wallimg;
    wallimg.loadFromFile("wall.png");
    wallimg.createMaskFromColor(sf::Color(255, 0, 255));
    sf::Texture walltex;
    walltex.loadFromImage(wallimg);
    sf::Sprite wall;
    wall.setTexture(walltex);

    int movex = 0, movey = 0;
    gluOrtho2D(0, WIN_WIDTH, 0, WIN_HEIGHT);
    while (window.isOpen())
    {
        // Process events
        sf::Event event;
        while (window.pollEvent(event))
        {
            // Close window : exit
            if (event.type == sf::Event::Closed)
            {
                window.close();
                return 0;
            }

            if (event.type == sf::Event::MouseButtonPressed )
            {
                if(event.mouseButton.button == sf::Mouse::Left)
                {
                    x = event.mouseButton.x;
                    y = event.mouseButton.y;
                    fixCoords(x, y);
                    wall.setPosition(sf::Vector2f(x, y));
                    walls.push_back(wall);
                }
                if(event.mouseButton.button == sf::Mouse::Right)
                {
                    x = event.mouseButton.x;
                    y = event.mouseButton.y;
                    fixCoords(x, y);
                    for(list<sf::Sprite>::iterator it = walls.begin(); it != walls.end(); it++)
                    {
                        if((it->getPosition().x == x) && (it->getPosition().y == y)) // This line
                            walls.erase(it);
                    }
                }

            }

            if(event.type == sf::Event::KeyPressed)
            {
                if((movex == 0) && (movey == 0))
                {
                    if(event.key.code == sf::Keyboard::Up)
                        movey -= 37;
                    if(event.key.code == sf::Keyboard::Down)
                        movey += 37;
                    if(event.key.code == sf::Keyboard::Left)
                        movex -= 40;
                    if(event.key.code == sf::Keyboard::Right)
                        movex += 40;
                }
            }
        }
        window.pushGLStates();
        window.clear(sf::Color(90, 167, 45));
        // Insert SFML Draws here
        if(movex > 0)
        {
            robotSpr.move(1, 0);
            movex--;
        }
        if(movex < 0)
        {
            robotSpr.move(-1, 0);
            movex++;
        }
        if(movey > 0)
        {
            robotSpr.move(0, 1);
            movey--;
        }
        if(movey < 0)
        {
            robotSpr.move(0, -1);
            movey++;
        }
        window.draw(robotSpr);
        if((x != 0) && (y != 0))
        {
            for(list<sf::Sprite>::iterator it = walls.begin(); it != walls.end(); it++)
            window.draw(*it);
        }

        window.popGLStates();
        // OpenGL Here
        drawGrid();

        // Update the window
        window.display();
    }
}

void drawGrid()
{
    glColor3ub(0, 0, 0);
    glBegin(GL_LINES); // Horizontal lines
    for(int i = 0; i < WIN_HEIGHT; i += WIN_HEIGHT / 20)
    {
        glVertex2i(0, i);
        glVertex2i(WIN_WIDTH, i);
    }
    glEnd();

    glColor3ub(0, 0, 0);
    glBegin(GL_LINES); // Vertical lines
    for(int i = 0; i < WIN_WIDTH; i += WIN_WIDTH / 20)
    {
        glVertex2i(i, 0);
        glVertex2i(i, WIN_HEIGHT);
    }
    glEnd();
}

void fixCoords(int &x, int &y)
{
    /**** Find the nearest x sqare ****/
    for(int i = 1; i < WIN_WIDTH - 1; i += 40)
    {
        if((x >= i) && x <= (i + 40))
        {
            x = i - 1;
            break;
        }
    }
    for(int i = WIN_HEIGHT; i > 0; i -= 40)
    {
        if((y >= i) && y <= (i + 40))
        {
            y = i;
            break;
        }
    }
}
1
maybe the copy constructor for sf::Sprite is bunk. Which version of SFML are you using exactly? - Michael Slade

1 Answers

3
votes

This is an annoyance of the way the list<T> container works.

A list<T> container is implemented as a doubly linked list. So an iterator needs to access its current element to get to the next element. If you have just erased its current element, everything explodes.

You can make it work like this:

list<sf::Sprite>::iterator it=walls.begin(),next;
while(it!=walls.end()) {
    next = it; next++;
    if((it->getPosition().x == x) && (it->getPosition().y == y))
        walls.erase(it);
    it = next;
}

you could also use remove_if with an appropriate predicate class, but that would just be uglier and more convoluted.