'Double clicking in SFML doesnt work as expected

I am working on a recreation of minesweeper using cpp and SFML. I have coded the board and the tiles switch to their revealed states when you left click on them, however, I am running into a strange issue with the right click.

Since I want to be able to flag and unflag tiles, I created a texture called tile_foreground that stores a texture the same size as the tiles but is completely transparent. The idea is that the first click will flag the tile and draw the flag sprite and a 2nd click would unflag the tile and change the sprite texture to the empty png so that only the base of the tile shows, with the cycle continuing on further clicks.

When you right click, the first click wont register and it will take at least a 2nd click to place down a flag on the tile. Sometimes it takes more than 2 clicks and clicking on a tile, moving your mouse to a different tile and then back to the same tile without clicking on a different tile will not flag the tile. It seems that the program doesnt register the first click as a click and waits for another one from the same tile to register it.

I suspect this comes from the way I handle the clicking events since before I added the statement below, holding down the right button would make the sprites alternate quickly and infinitely.

event.type == sf::Event::MouseButtonReleased

Any input would be appreciated since I dont really know how I would fix this.

Below is my main.cpp file

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



#include "Board.h"
#include "TextureManager.h"

void testFunc() {
    sf::RenderWindow window(sf::VideoMode(200, 200), "SFML works!");
    sf::CircleShape shape(100.f);         
    shape.setFillColor(sf::Color::Green);

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

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

void loadMSImages() {
    string files[21] = {"debug", "digits", "face_happy", "face_lose", "face_win", "flag", "mine", 
                        "number_1", "number_2", "number_3" , "number_4" , "number_5" , "number_6",
                        "number_7" , "number_8", "test_1", "test_2", "test_3", "tile_hidden", "tile_revealed", "tile_foreground"};
    for (int i = 0; i < 21; i += 1) {
        TextureManager::LoadTexture(files[i]);
    }
}

int main()
{
    loadMSImages();
    //testFunc(); // Test function to see if program is still working properly

    // Reads config file to determine window size

    int colNum;
    int rowNum;
    int mineNum;

    std::fstream reader("boards\\config.cfg");
    string readString;

    if (!reader.is_open()) 
    {
        std::cout << "File could not be opened" << std::endl;
        return 0;
    }

    // Reads col #, row #, and mine # in that order
    std::getline(reader, readString);
    colNum = stoi(readString);

    std::getline(reader, readString);
    rowNum = stoi(readString);

    std::getline(reader, readString);
    mineNum = stoi(readString);

    reader.close();

    // Provided test boards have a size of 25 x 16
    sf::RenderWindow window;
    window.create(sf::VideoMode((colNum * 32), (rowNum * 32) + 100), "Minesweeper"); // Width, Height
    window.setKeyRepeatEnabled(true);
    window.setVerticalSyncEnabled(true);

    Board testBoard(rowNum, colNum, mineNum);
    testBoard.printBoard();

    // Tile Testing Code
    
    // Creating sprites for buttons on bottom of game
    sf::Sprite testBoardOne;
    sf::Sprite testBoardTwo;
    sf::Sprite testBoardThree;
    sf::Sprite debugButton;

    sf::Sprite statusFace;

    sf::Sprite timerDigitOne;
    sf::Sprite timerDigitTwo;
    sf::Sprite timerDigitThree;

    // Setting the positions of these sprites
    testBoardOne.setPosition(sf::Vector2f(((32 * colNum) - 64), ((32 * rowNum))));
    testBoardTwo.setPosition(sf::Vector2f(((32 * colNum) -128), ((32 * rowNum))));
    testBoardThree.setPosition(sf::Vector2f(((32 * colNum) - 192), ((32 * rowNum))));
    debugButton.setPosition(sf::Vector2f(((32 * colNum) - 256), ((32 * rowNum))));

    statusFace.setPosition(sf::Vector2f((((32 * colNum)/2)-32), ((32 * rowNum))));

    // Loading the textures onto the sprites
    testBoardOne.setTexture(TextureManager::GetTexture("test_1"));
    testBoardTwo.setTexture(TextureManager::GetTexture("test_2"));
    testBoardThree.setTexture(TextureManager::GetTexture("test_3"));
    debugButton.setTexture(TextureManager::GetTexture("debug"));

    statusFace.setTexture(TextureManager::GetTexture("face_happy"));

    // 2D vector of tiles
    std::vector<std::vector<Tile*>> tiles;

    //Holding variables for creating Tile objects
    Tile* holdingTile;
    sf::Sprite* holdingSprite;

    for (int i = 0; i < 25; i += 1)
    {
        std::vector<Tile*> holdingVec;
        tiles.push_back(holdingVec);
        for (int j = 0; j < 16; j += 1)
        {
            holdingTile = new Tile(0, i, j);

            holdingSprite = new sf::Sprite();
            holdingSprite->setTexture(TextureManager::GetTexture("tile_hidden"));
            holdingSprite->setPosition(sf::Vector2f((32 * i), (32 * j)));

            holdingTile->setBackgroundSprite(holdingSprite);

            holdingSprite = new sf::Sprite();
            //holdingSprite->setTexture(TextureManager::GetTexture("tile_foreground"));
            holdingSprite->setPosition(sf::Vector2f((32 * i), (32 * j)));

            holdingTile->setForegroundSprite(holdingSprite);

            tiles.at(i).push_back(holdingTile);

        }
    }

    //============================ GAME LOOP ======================================

    // run the program as long as the window is open
    while (window.isOpen())
    {
        // check all the window's events that were triggered since the last iteration of the loop
        sf::Event event;
        while (window.pollEvent(event))
        {
            // "close requested" event: we close the window
            if (event.type == sf::Event::Closed)
                window.close();
        }
        
        // Clears previous frame, draws new frame, and displays it
        window.clear();

        // Draws tiles on board
        for (unsigned int i = 0; i < tiles.size(); i += 1)
        {
            for (unsigned int j = 0; j < tiles.at(i).size(); j += 1)
            {
                window.draw(*(tiles.at(i).at(j)->getBackgroundSprite()));
                window.draw(*(tiles.at(i).at(j)->getForegroundSprite()));
            } 
        }

        //Draws "Constant" Buttons, such as debug mode and boards
        window.draw(testBoardOne);
        window.draw(testBoardTwo);
        window.draw(testBoardThree);
        window.draw(debugButton);

        window.draw(statusFace);

        window.display();

        
        // Clicking handlers
        if (sf::Mouse::isButtonPressed(sf::Mouse::Left))
        {
            // transform the mouse position from window coordinates to world coordinates
            sf::Vector2f mouse = window.mapPixelToCoords(sf::Mouse::getPosition(window));

            // retrieve the bounding box of the sprite
            sf::FloatRect bounds;
            for (unsigned int i = 0; i < tiles.size(); i += 1)
            {
                for (unsigned int j = 0; j < tiles.at(i).size(); j += 1)
                {
                    bounds = tiles.at(i).at(j)->getBackgroundSprite()->getGlobalBounds();
                    if (bounds.contains(mouse))
                    {
                        //std::cout << "Sprite pressed" << std::endl;
                        tiles.at(i).at(j)->getBackgroundSprite()->setTexture(TextureManager::GetTexture("tile_revealed"));
                    }

                }

            }
            
        }

        //if (sf::Mouse::isButtonPressed(sf::Mouse::Right)) // WIP - Rapid oscillation of textures
        if (sf::Mouse::isButtonPressed(sf::Mouse::Right) && event.type == sf::Event::MouseButtonReleased)
        {
            // transform the mouse position from window coordinates to world coordinates
            sf::Vector2f mouse = window.mapPixelToCoords(sf::Mouse::getPosition(window));

            // retrieve the bounding box of the sprite
            sf::FloatRect bounds;
            for (unsigned int i = 0; i < tiles.size(); i += 1)
            {
                for (unsigned int j = 0; j < tiles.at(i).size(); j += 1)
                {
                    bounds = tiles.at(i).at(j)->getBackgroundSprite()->getGlobalBounds();
                    if (bounds.contains(mouse))
                    {
                        std::cout << "Tile right clicked" << std::endl;
                        
                        if (tiles.at(i).at(j)->isFlagged) 
                        {
                            tiles.at(i).at(j)->unflagTile();
                            tiles.at(i).at(j)->getForegroundSprite()->setTexture(TextureManager::GetTexture("tile_foreground"));
                        }
                        else
                        {
                            tiles.at(i).at(j)->flagTile();
                            tiles.at(i).at(j)->getForegroundSprite()->setTexture(TextureManager::GetTexture("flag"));
                        }
                               
                    }

                }

            }

        }

    }

    
    // Make sure to clear the textures at the end of the program to avoid errors
    TextureManager::Clear();

    return 0;
}


Solution 1:[1]

The problem is that you use the sf::Event event variable outside the while (window.pollEvent(event)) loop, so when you use the event, the last event type is assigned to it, which does not necessarily have to be MouseButtonReleased but can be one of these: https://www.sfml-dev.org/documentation/2.5.1/classsf_1_1Event.php#af41fa9ed45c02449030699f671331d4a


Solution:

Move this condition:

if (sf::Mouse::isButtonPressed(sf::Mouse::Right) && event.type == sf::Event::MouseButtonReleased)

to the while (window.pollEvent(event)) loop and change it to:

if (event.type == sf::Event::MouseButtonReleased && event.mouseButton.button == sf::Mouse::Right)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Selvan