Detecting if a key was pressed, not if it is always down - c++

I'm new to SFML and I have trouble finding a solution to checking if a key is pressed during one frame. The problem I've been facing is the fact that with the Keyboard and Mouse classes, it seems impossible to use a system where one first checks for the current input state before any Update() call of objects and then after all Update() you get a previous input state for the next frame so that one can do the following:
bool Entity::KeyboardCheckPressed(sf::Keyboard::Key aKey)
{
//this part doesn't work
if (KeyboardState->isKeyPressed(aKey) and !PreviousKeyboardState->isKeyPressed(aKey))
{
return true;
}
return false;
}
But this doesn't seem to work with SFML, and other sources tell me that I'm suppose to use the Event class with its type and key.codelike the following example:
bool Entity::KeyboardCheckPressed(sf::Keyboard::Key aKey)
{
if (Event->type == sf::Event::KeyPressed)
{
if (Event->key.code == aKey)
{
return true;
}
}
return false;
}
But this results in the sf::Event::KeyPressed doing the same as KeyboardState->isKeyPressed(aKey), so then I tried the method where you set key repeat to false: window.setKeyRepeatEnabled(false);with no results what so ever. I also found out that the sf::Event::KeyPressed works only as intended inside of this part in the main.cpp:
while (window.pollEvent(event))
{
}
The problem with this is that I want to handle Input inside of my Entity objects' Update()function, and I can't put the whole Update loop inside of the while (window.pollEvent(event)). So here I am, struggling to find a solution. Any help is appreciated.

In general, if you have a thing which you can check the current state of, and you want to check if that state changed between frames, you simply use a variable, declared outside the application loop, to store the previous state, and compare it to the current state.
bool previousState = checkState();
while (true) {
// your main application loop
bool newState = checkState();
if (newState == true && previousState == false) {
doThingy("the thing went from false to true");
} else if (newState == false && previousState == true) {
doThingy("the thing went from true to false");
} else {
doThingy("no change in the thing");
}
// this is done unconditionally every frame
previousState = newState;
}

Related

How do I assign a value in an array when a mouse is pressed SFML C++

I am trying to make a program where you are allowed to select between an option of shapes, and then drawing it. To allow for multiple shapes I created a vector of a class which creates shapes (Shapes are set up with the chosen function). My problem is the mouse click is too long, so it assigns it to everything in the vector, so you can't create a new shape. Is there a problem in my logic, or is there a problem in the code?
Here is my attempt:
for (auto& it : onCanvas) {
if (Mouse::isButtonPressed(Mouse::Left)) {
if (mousepointer.getGlobalBounds().intersects(circleOption.getGlobalBounds())) {
it.chosen(circles);
}
if (mousepointer.getGlobalBounds().intersects(rectOption.getGlobalBounds())) {
it.chosen(rectangle);
}
if (mousepointer.getGlobalBounds().intersects(triOption.getGlobalBounds())) {
it.chosen(triangles);
}
if (mousepointer.getGlobalBounds().intersects(it.shape.getGlobalBounds()) || it.dragging) {
it.shape.setPosition(mousepointer.getPosition());
it.dragging = true;
}
}
if (!Mouse::isButtonPressed) {
it.dragging = false;
}
win.draw(it.shape);
}
Your source-code is a bit incomplete (what is onCanvas and mousepointer). But I guess the problem is that this snippet is called multiple times while your mouse is clicked. To avoid that you can do two thing.
In the first solution you use events, so you only add shapes when the state of the mousebutton changes (you can additionally listen to the MouseButtonReleased to simulate a full click):
if (event.type == sf::Event::MouseButtonPressed)
{
if (event.mouseButton.button == sf::Mouse::Left)
{
// Hit Detection
}
}
or second solution you remember the last state of the button (probably do the mouse check once outside of the for loop):
bool mouse_was_up = true;
if (mouse_was_up && Mouse::isButtonPressed(Mouse::Left)) {
mouse_was_up = false;
for (auto& it : onCanvas) {
// Hit Detection
}
}
else if (!Mouse::isButtonPressed(Mouse::Left))
mouse_was_up = true;
I would rather stick to the first solution because when your click is too short and your gameloop is in another part of the game logic, you can miss the click.

SDL_KEYDOWN triggering twice

I am following lazy foo's tutorial, however I realized every time I press press s or p, SDL_KEYDOWNtriggers twice. How can this be fixed?
Here is the code snippet:
while(SDL_PollEvent(&e) != 0) {
if(e.type == SDL_QUIT) {
quit = true;
}
else if(e.type == SDL_KEYDOWN) {
if(e.key.keysym.sym == SDLK_s) {
if(timer.isStarted()) {
timer.stop();
printf("stop\n");
}
else {
timer.start();
printf("start\n");
}
}
else if(e.key.keysym.sym == SDLK_p) {
if(timer.isPaused()) {
timer.unpause();
printf("unpause\n");
}
else {
timer.pause();
printf("pause\n");
}
}
}
}
Pressing s once:
start
stop
TL;DR: Check if e.key.repeat equals to 0 before handling the events.
SDL generates fake repeated keypresses if you hold a key long enough. This is used mostly for text input.
The original key press has .repeat == 0, and fake presses have .repeat == 1.
For convenience reasons probably (I'd argue that it's rather inconvenient), since SDL 2.0.5 the actual key press generates two events instead of one. One has .repeat set to 0, and other (new) one has it set to 1.

Handle mouse events SFML

I have some trouble with events in SFML. I am making a turnbased game, when the mouse has moved or left mouse button is clicked i check whos turn it is and then spawn a projectile at that objects position, when the projectile has collided with either terrain or opponent it gets destroyed and the turn is changed.
The behaviour is not what i am expecting though. When clicking shoot the projectile sometimes wont spawn at all (and it changes the turn immedeately). I have disabled all collisions so it cant be that. Im 90% sure that the issue is with how i handle events, so id really appretiate input on how i can make it better.
Of what ive learned is that you should not execute the functions in the while poll event, its only for registering what has happend most recently,so i put them outside instead. This does not solve my problem though...
sf::Event event;
sf::Vector2i mousePos;
while (_window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
_window.close();
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape)) {
_window.close();
}
if(event.type == sf::Event::MouseMoved) { // <- this is how you check to see if an event
mousePos = sf::Mouse::getPosition();
moved = true;
}
if(event.type == sf::Event::MouseButtonPressed) { // <- this is how you check to see if an event
mousePressed = true;
}
}
if (mousePressed && tank1Turn)
{
sf::Vector2f spawnPos = _t1->getPos() + sf::Vector2f(0, -150);
sf::Vector2f initVel = _t1->getInitVel();
cout << endl;
_p = new Projectile(spawnPos, initVel);
tank1Turn = false;
tank1Firing = true;
mousePressed = false;
}
if (mousePressed && tank2Turn) {
sf::Vector2f spawnPos = _t2->getPos()+sf::Vector2f(0,-150);
sf::Vector2f initVel = _t2->getInitVel();
_p = new Projectile(spawnPos, initVel);
tank2Turn = false;
tank2Firing = true;
mousePressed = false;
}
if (tank1Turn && moved) {
_t1->aim(mousePos);
moved = false;
mousePressed = false;
}
if (tank2Turn && moved) {
_t2->aim(mousePos);
moved = false;
mousePressed = false;
}
}
Consider this a comment. This isn't an answer (I don't have enough reputation to comment so I have to make this an answer) but here are some things I noticed:
You should either replace the ifs in your event loop with if-elses, or use a switch
sf::Keyboard::isKeyPressed is for real time input (put it inside your game update loop). In your event loop it should be sf::Event::KeyPressed and check which key it was with evt.key.code
You aren't checking which mouse button was pressed. Do it with evt.mouseButton.button (sf::Mouse::Button::Left for the left mouse)
Of what ive learned is that you should not execute the functions in the while poll event, its only for registering what has happend most recently,so i put them outside instead.
Where did you read that? I don't think that's true
There shouldn't just be an array of ifs below the event loop. Code should be structured better

Better implantation of key pressed down

I am currently executing a GetAsyncKeyState event handler when the 'c' key is pressed down in C++.
Here is my code:
bool isKeyPressed = false;
void someFuntionOne()
{
if( GetAsyncKeyState( 'C' ) & 0x8000)
{
if(isKeyPressed)
{
isKeyPressed = false;
}
else
{
isKeyPressed = true;
}
}
}
void someFunctionTwo()
{
if(isKeyPressed)
{
// Do something
}
}
So bassically I want to know if the 'C' has been pressed not held down so I use my boolean variable isKeyPressed to tell me if the key was pushed down at any point. If it was pressed, set isKeyPressed to true and if it was pressed again set isKeyPressed to false.
The problem I have with this is I am running a huge OpenGL/C++ program that takes a lot of time to go around and execute the entire program in a single iteration so if the user presses down on the 'C' key, the program may not catch that event in time because it is still executing the rest of the other code and is currently not at the section of the code where it checks if GetAsyncKeyState( 'C' ) & 0x8000.
Is there a better way I can check for this event where the program is always listening for the event to happen at any point of execution time? How can I implement this?
Usually whoever delivers the events to your application will queue them until the application has the chance to process them, i.e. at the end of each main loop iteration. So ideally you would react to the events at that stage. An (ugly) alternative is something like:
bool keyWasPressed = false;
void processKeyEvent() {
if( <C is pressed>) {
keyWasPressed = true;
}
}
void someFunction() {
if(keyWasPressed) {
keyWayPressed = false;
// Do something
}
}

Why isn't my game pausing? (Windows C++ Keyboard Input Statemachine)

I'm writing a game in C++ using the Windows API which has a Splash Screen at the start, before gameplay begins, and can be paused.
I store the state of the game in an enum, game_state {PAUSED, PLAYING, SPLASHSCREEN}, and rely on Keyboard input to control the game.
The game was working properly, switching between paused and playing, but when I tried to add a splashscreen to begin the game on, the pause functionality stopped working, and I'm not sure why...
if(Keyboard.GetKey(VK_RETURN) && game_state == SPLASHSCREEN)
{
game_state = PLAYING;
Keyboard.SetKey(VK_RETURN, false);
}
if(Keyboard.GetKey(VK_RETURN))
{
if(game_state == PAUSED)
{
game_state = PLAYING;
}
else
{
game_state = PAUSED;
}
Keyboard.SetKey(VK_RETURN, false);
}
//If Paused, go to Pause Screen
if(game_state == PAUSED)
{
pauseScreen();
}
//If Splash Screen, go to Splash Screen
if(game_state == SPLASHSCREEN)
{
splashScreen();
}
//If not paused, do game processing
if(game_state == PLAYING)
{
gamePlay();
}
GetKey() returns true if the key is held down.
game_state is an enum global containing the current state of the game.
SetKey() sets the specified key as down (true) or up (false)
Oh, and all splashScreen() pauseScreen() and gamePlay() do are display sprites representing each state (at the moment)
SetKey
void Keyboard::SetKey(WPARAM key, bool key_down)
{
if(key_down)
{
m_keys[key] = true;
}
else
{
m_keys[key] = false;
}
}
GetKey
bool Keyboard::GetKey(WPARAM key)
{
if(m_keys[key])
{
m_keys[key] = false;
return true;
}
else
{
return false;
}
}
Remove m_keys[key] = false; from the Keyboard::GetKey method. As it is being set to false in the first check, it prevents the next check from seeing that it was pressed.
Calling GetKey() sets the key as released - since it checks to see if the key is pressed and the state is splashscreen before checking anything else - the key will always be released when checking it again.
Alter GetKey or alter the way the code is written.
My best guess is that your splash screen has the focus and it will take over the message loop, then you don't get the key event. Just a guess, can't really know without seeing the window creation/registration code of your splash and main windows.