is there's a way to make SFML Event MouseButtonPressed work after one click? - sfml

so I am trying to make a small game using sfml but I am stuck at MouseButton event I try to choose from the menu I has to spam clicks for it to work I thought the problem was my mouse so I tried another one and it didn't solve the problem
void draw(sf::RenderWindow &window)
{
mousePos = window.mapPixelToCoords(sf::Mouse::getPosition(window));
window.draw(Background_rect);
for (int i = 0; i < 3; i++)
{
window.draw(rect[i]);
if (rect[i].getGlobalBounds().contains(mousePos))
{
while (window.pollEvent(event))
{
if (event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Left)
{
mainMenuPress = mainMenuSelected[i];
}
}
}
}
}
so i think the code is good but I don't understand why it doesn't work after 1 click

i couldn't solve it as event mouseButtonPressed i don't know what's the problem with event type but it didn't work for me i had to change the condition as mouse isButtonPressed and it worked
void draw(sf::RenderWindow &window)
{
mousePos = window.mapPixelToCoords(sf::Mouse::getPosition(window));
window.draw(Background_rect);
for (int i = 0; i < 3; i++)
{
if (rect[i].getGlobalBounds().contains(mousePos))
{
if (sf::Mouse::isButtonPressed(sf::Mouse::Left)) //Changed
{
mainMenuPress = mainMenuSelected[i];
}
}
window.draw(rect[i]);
}
}
thanks for your help

As you have shown more code, I thing this for loop might ba the problem -
you should move pollEvent to the place where you update any stuff, and then make any ifs and loops inside it
// in update
while (window.pollEvent(event))
{
for (int i = 0; i < 3; i++)
{
if (event.type == sf::Event::MouseButtonPressed && event.mouseButton.button == sf::Mouse::Left)
{
if (rect[i].getGlobalBounds().contains(mousePos))
{
mainMenuPress = mainMenuSelected[i];
}
}
}
}
// and then draw

Related

Dragging a sprite in sfml

I'm doing an assignment at the moment and for it, I need to drag chess pieces on a board, the problem is that I have no idea how to make this work for more than one piece. I can compute the mouse position and I've been able to move just one piece but the code only works for that single piece. My question is how do I click on a piece, have it become the active piece until I release the mouse button. This won't work if I only check if it intersects with one piece because it isn't dynamic then. My code is a mess from me trying different things but here are some of the relevant parts.
Here is the setup for the sprites:
// load the texture and setup the sprite for the logo
void Game::setupSprite()
{
sf::IntRect pieceRect(m_wking.getPosition().x, m_wking.getPosition().y, 128, 128);
if (!m_boardTexture.loadFromFile("ASSETS\\IMAGES\\board.png"))
{
// simple error message if previous call fails
std::cout << "problem loading board" << std::endl;
}
m_boardSprite.setTexture(m_boardTexture);
m_boardSprite.setPosition(0.0f, 0.0f);
//pieces
if (!m_wkingTex.loadFromFile("ASSETS\\IMAGES\\PIECES\\wking.png"))
{
// simple error message if previous call fails
std::cout << "problem loading piece" << std::endl;
}
m_wking.setTexture(m_wkingTex);
m_wking.setPosition(0.0f, 0.0f);
sf::IntRect wkingRect(pieceRect);
}
If the mouse button is down
void Game::processMouseButtonDown(sf::Event t_event)
{
if (sf::Mouse::isButtonPressed(sf::Mouse::Button::Left)) {
std::cout << "Mouse button pressed\n";
sf::Vector2f mouse = m_window.mapPixelToCoords(sf::Mouse::getPosition(m_window));
if (isSpriteClicked() && !m_holdingPiece) {
m_holdingPiece = true;
}
}
}
Mouse button is up:
void Game::processMouseButtonUp(sf::Event t_event)
{
m_holdingPiece = false;
sf::IntRect pieceRect(m_wking.getPosition().x, m_wking.getPosition().y, 128, 128);
m_wking.setTextureRect(pieceRect);
}
If the sprite is clicked
bool Game::isSpriteClicked()
{
sf::Vector2f mouse = m_window.mapPixelToCoords(sf::Mouse::getPosition(m_window));
sf::FloatRect bounds(mouse.x, mouse.y, 1.0f, 1.0f);
if (bounds.intersects(wkingRect.I dont know what Im doing)) {
std::cout << "King has Been clicked!\n";
return true;
}
else {
return false;
}
}
And finally to move the sprite:
void Game::move()
{
if (m_holdingPiece) {
sf::Vector2f mouse = m_window.mapPixelToCoords(sf::Mouse::getPosition(m_window));
m_wking.setPosition(mouse);
}
}
Sorry if that seems like a lot of code, but I feel like it was all relevant to the problem.
Problem:
You have hardcoded all your variables so even if you create a list and iterate over it to run Game::move() it will move all the pieces instead of only one because the variable m_holdingPiece is the same for all pieces.
Solution:
You should use a more OOP aproach creating a class for the chess pieces while the Game class should probably just control when the game starts, when it ends and check whether moves are legal or not. Explaning all that would be too broad Stack Overflow and it's part of the work you must do, so I will just let an example of how a basic chessPiece class would be to make pieces dragabble.
Example:
Here is an example of how you can make a group of pieces draggable.
#include <SFML/Graphics.hpp>
#include <string>
class chessPiece: public sf::Drawable, public sf::Transformable
{
private:
sf::RenderWindow& window;
sf::Texture texture;
sf::Sprite sprite;
bool moving;
public:
chessPiece(sf::RenderWindow& windowRef, const std::string& fileName): window(windowRef)
{
if(!texture.loadFromFile(fileName))
exit(0);
this->sprite.setTexture(this->texture);
this->moving = false;
this->sprite.setScale(128/this->sprite.getGlobalBounds().width,128/this->sprite.getGlobalBounds().height);
}
chessPiece(chessPiece&& rval): window(rval.window)
{
this->texture = std::move(rval.texture);
this->sprite.setTexture(this->texture);
this->sprite.setScale(rval.sprite.getScale());
this->moving = std::move(rval.moving);
}
chessPiece(const chessPiece& lval): window(lval.window)
{
this->texture = lval.texture;
this->sprite.setTexture(this->texture);
this->sprite.setScale(lval.sprite.getScale());
this->moving = lval.moving;
}
void draw(sf::RenderTarget& target, sf::RenderStates states) const
{
states.transform *= this->getTransform();
target.draw(this->sprite,states);
}
void move(bool& movingAPiece)
{
sf::Vector2f mousePosition = window.mapPixelToCoords(sf::Mouse::getPosition(window));
if(this->moving)
{
this->setPosition(mousePosition.x - this->sprite.getGlobalBounds().width/2,mousePosition.y - this->sprite.getGlobalBounds().height/2);
if(!sf::Mouse::isButtonPressed(sf::Mouse::Button::Left))
{
this->moving = false;
movingAPiece = false;
}
}
else
{
if(sf::Mouse::isButtonPressed(sf::Mouse::Button::Left) && mousePosition.x > this->getPosition().x &&
mousePosition.y > this->getPosition().y && mousePosition.x < this->getPosition().x + this->sprite.getGlobalBounds().width &&
mousePosition.y < this->getPosition().y + this->sprite.getGlobalBounds().height && !movingAPiece)
{
this->moving = true;
movingAPiece = true;
}
}
}
};
int main()
{
sf::RenderWindow window(sf::VideoMode(1000,500),"Window");
std::vector<chessPiece> pieces = {chessPiece(window,"white-king.png"),chessPiece(window,"white-pawn.png")};
pieces[0].setPosition(400,400);
bool movingAPiece = false;
while(true)
{
sf::Event event;
if(window.pollEvent(event))
if(event.type == sf::Event::Closed)
window.close();
for(unsigned int i = 0; i < pieces.size(); i++)
pieces[i].move(movingAPiece);
window.clear();
for(unsigned int i = 0; i < pieces.size(); i++)
window.draw(pieces[i]);
window.display();
}
}

THREAD 1 :EXC_BAD_ACCESS error, this error appears at the handle function in the cpp file at the first line

this is a code that creates a chicken invaders game, the game(or whatever part i can do of the game) worked fine when i was just initializing one chicken, but when i made it a 2d array of chickens it showed me this error.
RenderWindow window;
int main(int, char const**)
{
chicken chick[4][7];
Sprite back;
Clock clock;
window.create(VideoMode(1800, 1000), "Menu");
while (window.isOpen())
{
Event event;
while (window.pollEvent(event))
{
if (event.type == Event::KeyPressed && event.key.code == Keyboard::Escape)
window.close();
if (event.type == Event::Closed)
window.close();
}
Texture texture;
if (!texture.loadFromFile(resourcePath() + "background.jpg"))
{
std::cout << "Error loading texture" << std::endl;
}
Time time;
time = clock.getElapsedTime();
if (time.asSeconds() >= 0.001)
{
chick[4][7].handle(window, clock);
clock.restart();
}
window.clear();
back.setTexture(texture);
back.setPosition(0, 0);
back.setTextureRect(IntRect(0, 0, 1800, 1000));
window.draw(back);
for (int i = 0; i < 4; i++)
for (int j = 0; j < 7; j++)
chick[i][j].initialize(window);
window.display();
}
}
///////////////////
this is the app file for the chicken class
using namespace sf;
class chicken
{
private:
RectangleShape chick;
bool flag;
public:
chicken();
void initialize(RenderWindow &window);
void handle(RenderWindow & window, Clock clock);
~chicken();
};
#endif /* chicken_hpp */
/////////////////////////////////////////////////////////////
this is the cpp file for the chicken class
using namespace sf;
chicken::chicken()
{
chick.setPosition(500, 500);
chick.setSize(Vector2f(200, 200));
chick.setOrigin(100, 100);
flag = true;
}
void chicken::initialize(RenderWindow & window)
{
Texture texture2;
if (!texture2.loadFromFile(resourcePath() + "chicken3.jpg"))
{
std::cout << "Error loading texture" << std::endl;
}
chick.setTexture(&texture2);
window.draw(chick);
}
void chicken::handle(RenderWindow & window, Clock clock)
{
if (flag) //the error appears here
{
chick.move( 10 , 0 ) ;
}
else
{
chick.move(-10 , 0);
}
if (chick.getPosition().x >= window.getSize().x)
flag = false;
if(chick.getPosition().x < 0)
flag = true;
}
chicken::~chicken()
{
}
In the future please include the entire error message in your question, however the issue here is you are accessing an out of bounds index in your chicken array.
your array size is 4x7 this means your valid indices are 0 - 3 and 0 - 6.
Your handle function is being called on chick[4][7], which is out of bounds.
If you are trying to call that function on only the last chick in the array it would be
chick[3][6].handle(window, clock);
to call it on every chicken you need a double for loop like
for(int i = 0; i < 4; ++i)
{
for(int j = 0; j < 7; ++j)
{
chick[i][j].handle(window, clock);
}
}

Focus sprite by leftclick and then remove focus by leftclick again. (C++ and SFML 2.2)

In the following code a sprite can by clicked with left mouse button and then moved around freely. Then you have to hit the right button to "free" it again.
Now what is, if I want to use left button for removing the focus, instead of right button. Is this possible and how? I can't imagine, how to do it. Many thanks in advance.
#include <SFML/Graphics.hpp>
#include <iostream>
#include <vector>
int main()
{
sf::RenderWindow mMainWindow(sf::VideoMode(600,600), "Map", sf::Style::Close);
mMainWindow.setFramerateLimit(60);
mMainWindow.setKeyRepeatEnabled(false);
sf::Image image;
image.create(50, 50, sf::Color::Red);
sf::Texture texture;
texture.loadFromImage(image);
std::vector<sf::Sprite> EnemyVector;
sf::Sprite* focus = nullptr;
bool move = false;
while (mMainWindow.isOpen())
{
sf::Event event;
bool creating = false;
bool leftclicked = false;
bool rightclicked = false;
sf::Vector2i mousePos;
while (mMainWindow.pollEvent(event))
{
switch (event.type)
{
case sf::Event::Closed:
mMainWindow.close();
break;
case sf::Event::KeyPressed:
creating = (event.key.code == sf::Keyboard::A);
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 (event.mouseButton.button == sf::Mouse::Right)
{
rightclicked = true;
mousePos = sf::Vector2i(event.mouseButton.x, event.mouseButton.y);
break;
}
}
}
if (creating)
{
sf::Sprite sprite;
mousePos = (mousePos == sf::Vector2i(0, 0) ? sf::Mouse::getPosition(mMainWindow) : mousePos);
sprite.setTexture(texture);
sprite.setColor(sf::Color::Red);
sprite.setOrigin(static_cast<float>(sprite.getTextureRect().width) / 2, static_cast<float>(sprite.getTextureRect().height) / 2);
sprite.setPosition(static_cast<float>(mousePos.x), static_cast<float>(mousePos.y));
focus=nullptr;
EnemyVector.push_back(sprite);
}
if (leftclicked)
{
for (auto& enemy = EnemyVector.rbegin(); enemy != EnemyVector.rend(); ++enemy)
{
if (enemy->getGlobalBounds().contains(static_cast<float>(mousePos.x), static_cast<float>(mousePos.y)))
{
focus = &(*enemy);
move = true;
break;
}
}
}
if(rightclicked)
{
focus = nullptr;
}
if(move)
{
if(focus!=nullptr)
{
focus->move((sf::Mouse::getPosition(mMainWindow).x - focus->getPosition().x),(sf::Mouse::getPosition(mMainWindow).y - focus->getPosition().y));
}
}
mMainWindow.clear();
for (auto& enemy = EnemyVector.rbegin(); enemy != EnemyVector.rend(); ++enemy)
{
mMainWindow.draw(*enemy);
}
mMainWindow.display();
}
return 0;
}
try this
if (leftclicked)
{
for (auto& enemy = EnemyVector.rbegin(); enemy != EnemyVector.rend(); ++enemy)
{
if (enemy->getGlobalBounds().contains(static_cast<float>(mousePos.x), static_cast<float>(mousePos.y)))
{
focus = &(*enemy);
move = !move;
break;
}
}
}

Moving Rectangle in SFML, how to make it smoother?

This is my code:
#include <SFML/Graphics.hpp>
#include <iostream>
#include <Windows.h>
int main()
{
sf::RenderWindow window(sf::VideoMode(800, 600), "SFML Game");
window.setFramerateLimit(200);
//Variable that keeps the game loop running
bool play = true;
//Event object holding all events
sf::Event event;
//States for button/events
bool Left = false;
bool Right = false;
bool space = false;
//Variables
int rectXPosition = 375; //Rectangles X position
int rectYPosition = 400; //Rectangles Y position
//Images
sf::Texture image1;
if (image1.loadFromFile("Images/GreekSoldier.png") == -1)
{
std::cout << "FAILED!!!" << "\n";
return 1;
}
//Render shapes
sf::RectangleShape rect;
rect.setSize(sf::Vector2f(77, 150)); //Width and height
rect.setPosition(375, 400); //Position
rect.setFillColor(sf::Color::White); //Color
rect.setTexture(&image1);
//Game loop
while (play == true)
{
//EVENTS
while (window.pollEvent(event))
{
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Left)
{
//Set the state to true
Left = true;
}
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Right)
{
Right = true;
}
if (event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Space)
{
space = true;
}
//Event type is window closed
if (event.type == sf::Event::Closed)
{
//Set play to false in order to stop the game loop
play = false;
}
}
//LOGIC
if (Left == true)
{
int x = 0;
for (x = 1; x < 2; x++)
{
rectXPosition-=5; //X position variable of the rectangle
rect.setPosition(rectXPosition, rectYPosition);
window.clear();
window.draw(rect);
window.display();
Sleep(5);
}
Left = false;
}
if (Right == true)
{
int x = 0;
for (x = 1; x < 2; x++)
{
rectXPosition+= 5; //X position variable of the rectangle
rect.setPosition(rectXPosition, rectYPosition);
window.clear();
window.draw(rect);
window.display();
Sleep(5);
}
Right = false;
}
if (space == true)
{
int x = 0;
for (x = 1; x < 15; x++)
{
rectYPosition-= 3; //X position variable of the rectangle
rect.setPosition(rectXPosition, rectYPosition);
window.clear();
window.draw(rect);
window.display();
Sleep(10);
}
for (x = 1; x < 15; x++)
{
rectYPosition+= 3; //X position variable of the rectangle
rect.setPosition(rectXPosition, rectYPosition);
window.clear();
window.draw(rect);
window.display();
Sleep(10);
}
space = false;
}
//RENDERING
window.clear();
//Draw the rectangle shape
window.draw(rect);
window.display();
}
//Clean up and close the window
window.close();
return 0;
}
How would I make the movement more smoother? How do I make it so the rectangle(the soldier) can jump and move at the same time? And how do I stop it so, when holding down left it doesn't take 3 seconds for it to actually move?
I've added a youtube video if you guys can't be bothered to compile it and just want to see what I'm talking about.
https://www.youtube.com/watch?v=g_JLwGh1Wgo
The KeyPressed events are generated when someone presses a key and then repeat every so often (but not every frame, which makes it not smooth). They are intended to be used for text input, for example.
Pressing another key will also interrupt the repetition of the events for the previous key, so your other problem has the same cause.
You should handle the KeyPressed events only for some instant actions, like pressing a menu button. In this particular example you should get rid of this altogether and use only sf::Keyboard by replacing the line:
if (Left == true)
with:
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left))
and so on...
I would explain why sf::Keyboard is the right thing here, but the documentation does it very well.
Another big problem is that you have multiple loops that don't let anything else happen in the game. You should get yourself familiar with the concepts of event loops, velocity and gravity (and make corresponding variables).
When the player presses Space you shouldn't hardcode the character to move N pixels up and N pixels down while stopping every other possible action in the game, but instead change the character's vertical velocity and use it to change the character's position in every frame (and also have that velocity be reduced by gravity).
The most important point to take from this is you must have only 1 main event loop that calls window.display.

Keyboard input problems with multiple players

I'm working on the motion for my paddles in my PONG clone and the program doesn't want to register SDL_KEYUP/DOWN sometimes. I think I have an idea of why this is but now how to fix it. So the way my game is looped currently I am using a state machine in my main() function that uses a Game class state which manages two paddles and a ball all of which are three different class instances. In side my Game class I run the events/logic/render from all three classes like so
void Game::events()
{
while (SDL_PollEvent(&event))
{
PlayerOne.events();
PlayerTwo.events();
Game_Ball.events();
}
}
void Game::logic()
{
PlayerOne.logic(delta.get_ticks());
PlayerTwo.logic(delta.get_ticks());
Game_Ball.logic();
}
void Game::render()
{
SDL_FillRect(screen, &screen->clip_rect, SDL_MapRGB(screen->format, 0, 0, 0));
PlayerOne.render();
PlayerTwo.render();
Game_Ball.render();
}
And the paddle class movement is managed like so (as fixed in the last question I had(:
void Paddle::events()
{
Uint8 *keystates = SDL_GetKeyState(NULL);
if (event.type == SDL_KEYDOWN)
{
if (player == 1)
{
if (keystates[SDLK_o] == 1)
{
yVel -= 100;
}
if (keystates[SDLK_k] == 1)
{
yVel += 100;
}
}
else
{
if (keystates[SDLK_w] == 1)
{
yVel -= 100;
}
if (keystates[SDLK_s] == 1)
{
yVel += 100;
}
}
}
if (event.type == SDL_KEYUP)
{
if (player == 1)
{
if (keystates[SDLK_o] == 0)
{
yVel += 100;
}
if (keystates[SDLK_k] == 0)
{
yVel -= 100;
}
}
else
{
if (keystates[SDLK_w] == 0)
{
yVel += 100;
}
if (keystates[SDLK_s] == 0)
{
yVel -= 100;
}
}
}
if (event.type == SDL_QUIT)
{
quit = true;
}
}
The problem here is that sometimes, if not most of the time, the keys don't react to move the player paddles up and down. but when they do it's less than half the time. I think this is because there is only a 1/3 chance that the program polls when a key is released or pressed, sometimes resulting in no change in the position. Basically, if PLAYERONE hits a key while Game is running PlayerTwo.events() it won't register the key press/release. Could this be a problem in the way I'm implementing the game loop in my main function?
int main(int argc, char* args[])
{
//init SDL
if (init() == false)
{
return 1;
}
//load everything
if (load_files() == false)
{
return 1;
}
delta.start();
currentState = new Game;
while (quit == false)
{
//handle state events
currentState->events();
// do state logic
currentState->logic();
//timer reset
delta.start();
//change state if needed
change_state();
//render state
currentState->render();
if (SDL_Flip(screen) == -1)
{
return 1;
}
}
clean_up();
return 0;
}
And here are the definitions and constructors
class Paddle
{
public:
int player;
SDL_Surface *sprite = NULL;
float x, y, w, h;
float yVel;
SDL_Rect *clip;
void events();
void logic(Uint32 deltaTicks);
void render();
Paddle();
~Paddle();
};
class Game : public GameState
{
private:
int server;
TTF_Font *score = NULL;
Paddle PlayerOne;
Paddle PlayerTwo;
Ball Game_Ball;
public:
SDL_Rect clip[2];
void events();
void logic();
void render();
Game();
~Game();
};
Game::Game()
{
//RESOURSES
PlayerOne.sprite = load_image("Game_sprite.png");
PlayerTwo.sprite = load_image("Game_sprite.png");
clip[0].x = 0;
clip[0].y = 0;
clip[0].w = 20;
clip[0].h = 20;
clip[1].x = 0;
clip[1].y = 20;
clip[1].w = 20;
clip[1].h = 100;
//PLAYER ONE
PlayerOne.x = 420;
PlayerOne.y = 100;
PlayerOne.w = 60;
PlayerOne.h = 100;
PlayerOne.clip = &clip[1];
PlayerOne.yVel = 0;
PlayerOne.player = 1;
//PLAYER TWO
PlayerTwo.x = 60;
PlayerTwo.y = 100;
PlayerTwo.w = 60;
PlayerTwo.h = 100;
PlayerTwo.clip = &clip[1];
PlayerTwo.yVel = 0;
PlayerTwo.player = 2;
//BALL
Game_Ball.Ball_Bound.x = 190;
Game_Ball.Ball_Bound.y = 190;
Game_Ball.Ball_Bound.w = 60;
Game_Ball.Ball_Bound.h = 60;
Game_Ball.clip = &clip[0];
}
Because that looks pretty clean to me. Other than another game loop implementation, what could I do to fix the keys not registering due to being in a different function? Or is it a loop implementation problem? If it is a loop problem, can you be through or include a reference as to how to fix it?
I don't see anything wrong in this code. However, the very possible cause might be simply limitation of how many keys are pressed on keyboard. Try using shift, ctrl and alt, keys for controls, they usually bypass the limitation.