'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 |
