How to clear event input after a key press? - c++

When I press F, my controller fires – everything works fine. However, when F is pressed twice, it fires, and after processing the first event (200ms later), it fires again, and that's not what I want. I would like to clear all the input after the first firing.
My code is:
sf::Event event;
while (window.waitEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
else if (event.type == sf::Event::KeyPressed)
{
if (event.key.code == sf::Keyboard::F) //fire
{
controleur.Shot(45, 100); //this step during 200ms
}
}
}

Just use a variable "isShooting" to prevent your controler to shot.
void Controleur::Shot(int a, int b)
{
if (!this->isShooting)
{
this->isShooting = true;
// do stuff
}
}
And when your actions are finished , just set the variable to false, then you can shoot again.
EDIT: nevermind, i misunderstood your problem
You could use sf::Input::IsKeyDown from sf::Input to see wether your key is pressed or not when computing events.
Another way to do it is flushing your event queue after the end of Shot() with this code
// consumes all events in the queue
while (window.pollEvent(event));
But it is not a very good thing to do i think.

Related

How to get persistent input in SDL2 c++

So I noticed that when getting input with SDL_GetKeyboardState(NULL), when holding a specific button, it is going to first write outlets say a, and after 1 second its gonna continue aaaaaaaa normally. I want to when I hold the button a that it automatically goes aaaaaa.
Here is a video if you don't understand my poor explanations:
https://streamable.com/oub0w3
There is a delay between it writes out first a, and writing out aaaaa about 1 second. How can I change that? (I want there to be no delay)
Here is my code:
while (gameRunning) {
SDL_Event event;
const Uint8* keystates = SDL_GetKeyboardState(NULL);
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) {
gameRunning = false;
}
if (keystates[SDL_SCANCODE_W]) {
entities[0].setY(entities[0].getY() - 1);
}
if (keystates[SDL_SCANCODE_S]) {
entities[0].setY(entities[0].getY() + 1);
}
if (keystates[SDL_SCANCODE_A]) {
entities[0].setX(entities[0].getX() - 1);
}
if (keystates[SDL_SCANCODE_D]) {
entities[0].setX(entities[0].getX() + 1);
}
}
You're misusing SDL_GetKeyboardState(nullptr).
It should be used in the main loop, not in the event loop:
while (gameRunning)
{
SDL_Event event;
while (SDL_PollEvent(&event))
{
if (event.type == SDL_QUIT)
gameRunning = false;
}
const std::uint8_t *keystates = SDL_GetKeyboardState(nullptr);
if (keystates[SDL_SCANCODE_W])
entities[0].setY(entities[0].getY() - 1);
if (keystates[SDL_SCANCODE_S])
entities[0].setY(entities[0].getY() + 1);
// An so on...
}
If you want the repetition to start immediately, you need to make your own implementation of the repeating letters.
The additional "a" characters you receive as events are (I assume) generated by the operating system, so unless you have some settings on your OS you can change to make repetition start immediately, you need your program to do it.
(I am assuming SDL is not the one generating these characters, which could be a possibility)
To do this, you would make a system check the amount of time elapsed and kept track of how long keys are being pressed, and outputting "key" events that it generated itself, much like the OS is doing.

When events should be used in SFML?

I am confused about how to get inputs from Mouse or Keyboard. As an example, I want to draw little dots on my Mouse position when I pressed the button of my Mouse. Which implementation should I follow?
I have used window.pollEvent function to catch the mouse pressed event in the code below.
#include <SFML/Graphics.hpp>
#include <iostream>
int main()
{
sf::RenderWindow window(sf::VideoMode(640,480), "Paint");
std::vector<sf::CircleShape> dots;
while (window.isOpen()) {
sf::Event event;
if (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
}
if (event.type == sf::Event::MouseButtonPressed) {
sf::CircleShape shape(10);
shape.setFillColor(sf::Color::Black);
shape.setPosition(event.mouseButton.x, event.mouseButton.y);
dots.push_back(shape);
}
}
window.clear(sf::Color::White);
for (auto& i : dots) {
window.draw(i);
}
window.display();
}
return 0;
}
or should I do it in the way like this?
#include <SFML/Graphics.hpp>
#include <iostream>
int main()
{
sf::RenderWindow window(sf::VideoMode(640,480), "Paint");
std::vector<sf::CircleShape> dots;
while (window.isOpen()) {
sf::Event event;
if (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
}
}
if (sf::Mouse::isButtonPressed(sf::Mouse::Left)) {
sf::CircleShape shape(10);
shape.setFillColor(sf::Color::Black);
shape.setPosition(sf::Mouse::getPosition().x, sf::Mouse::getPosition().y);
dots.push_back(shape);
}
window.clear(sf::Color::White);
for (auto& i : dots) {
window.draw(i);
}
window.display();
}
return 0;
}
If the latter is the appropriate one, then where the if block that checks if the mouse button is pressed should be located, before window.clear() or between window.clear() and window.draw() ?
I could not understand the different between them thoroughly. SFML documentation, for example, shows the latter implemenatation for gunfire action but I could not figure out why. Thanks...
You are essentially asking about the two ways for handling user input:
Events: handling objects that represent an occurrence.
Real-time input: querying the input device about its real-time state.
Your first approach – calling sf::Window::pollEvent() – relies on events. It is an asynchronous mechanism; the button may not be pressed when your code handles the event. Event handling is usually the way to go if all you are interested in is whether the state of the input device has undergone a changeX, e.g., a button has been pressed or released.
Your second approach – calling sf::Mouse::isButtonPressed() – is based on real-time input. It consists of querying the mouse whether a given button is pressed at the moment of calling the function. This approach for handling user input is usually the way to go if you just want to find out the current state of the input device.
XNote, however, that events can be repeated (e.g., if you keep a key pressed for a long time), and therefore they may not necessarily imply a change in the state of the input device. You can disable this with sf::Window::SetKeyRepeatEnabled(), though.

window.display() alone toggles between the last and current buffer displayed

I made this code that shows a timer and pauses when you press spacebar:
#include <SFML/Graphics.hpp>
#include <iostream>
using namespace sf;
using namespace std;
void events();
bool pause, exitPause;
char key;
double timeFrame, timeTot = 0;
Clock timer;
Text text;
Font font;
RenderWindow window(VideoMode(800, 600), "Window", Style::Close);
int main()
{
font.loadFromFile("C:/Windows/Fonts/arial.ttf");
text.setFont(font);
text.setCharacterSize(15);
window.setFramerateLimit(120);
while (window.isOpen())
{
for (Event event; window.pollEvent(event);) {
if (event.type == Event::Closed)
window.close();
if (event.type == Event::TextEntered) {
key = std::tolower(static_cast<char>(event.text.unicode));
if (key == ' ') {
pause = !pause;
if (!pause) {
timer.restart();
}
}
}
}
if (!pause) {
timeFrame = timer.restart().asSeconds();
timeTot += timeFrame;
text.setString(to_string(timeTot));
window.clear();
window.draw(text);
}
window.display();
}
}
If you test, you will see something curious. When pausing by pressing the spacebar, window.display alternates between the last and the current displayed number.
But if I put window.clear and window.draw together with window.display, the problem does not happen.
if (!pause) {
timeFrame = timer.restart().asSeconds();
timeTot += timeFrame;
text.setString(to_string(timeTot));
}
window.clear();
window.draw(text);
window.display();
I thought windows.display, alone, would only show the last buffer.
What is the problem?
The moment you pause you stop updating the draw buffers. SFML is always double-buffered, and in each iteration you always need to parse input, update whatever needs updating, redraw the "hidden" frame, and then flip the buffers. This is basically a "Game Loop" pattern.
In your code you always parse input, update the timer and pause state based on that, and you always flip the buffers (with window.display()). You only redraw the "hidden" frame buffer if the state is not paused, however.
So, you are seeing the expected output, and you found the correct solution.
As an aside, there are indeed several style issues in your code, including uninitialized variables, which is always dangerous in C++.

SFML thread synchronization?

I'm new to SFML, been trying to have a multi-threading game system (all of the game logic on the main thread, and the rendering in a dedicated thread using sf::Thread; mainly for practicing with threads) as explained in this page ("Drawing with threads" section):
Unfortunately my program has a long processing time during it's update() and makes the rendering process completely out of control, showing some frames painted and some others completely empty. If it isn't obvious my rendering thread is trying to paint something that isn't even calculated, leaving this epileptic effect.
What I'm looking for is to allow the thread to render only when the main logic has been calculated. Here's what I got so far:
void renderThread()
{
while (window->isOpen())
{
//some other gl stuff
//window clear
//window draw
//window display
}
}
void update()
{
while (window->isOpen() && isRunning)
{
while (window->pollEvent(event))
{
if (event.type == sf::Event::Closed || sf::Keyboard::isKeyPressed(sf::Keyboard::Escape))
{
isRunning = false;
}
else if (m_event.type == sf::Event::Resized)
{
glViewport(0, 0, m_event.size.width, m_event.size.height);
}
}
// really resource intensive process here
time = m_clock.getElapsedTime();
clock.restart().asSeconds();
}
}
Thanks in advance.
I guess the errors happen because you manipulate elements that are getting rendered at the same time in parallel. You need to look into mutexes.
Mutexes lock the element you want to manipulate (or draw in the the other thread) for as long as the manipulation takes and frees it afterwards.
While the element is locked it can not be accessed by another thread.
Example in pseudo-code:
updateThread(){
renderMutex.lock();
globalEntity.manipulate();
renderMutex.unlock();
}
renderThread(){
renderMutex.lock();
window.draw(globalEntity);
renderMutex.unlock();
}

Checking for input while sleeping

Two functions: CheckInput and SendKey
CheckInput checks for Alt-J to be pressed. When Alt-J is pressed, a bool is toggled which allows for SendKey to keep sending the Enter key every 500ms.
I need for CheckInput to still be able to get user input and change the bool - thus stopping enter being sent - whilst the SendKey is still waiting within the sleep, otherwise right now I have to hold down Alt-J until the next 500ms is up.
Any ideas on how to do this? It just seems so basic yet I can't wrap my head around it...
( Basically, a script that presses enter every 500ms and can be toggled on and off without needing to wait 500ms )
void CheckInput() {
if ((GetKeyState(0x12) & 0x8000) && (GetKeyState(0x4A) & 0x8000)) {
Active = !Active;
}
}
void SendKey() {
if (Active) {
keybd_event(0x0D, 0x0A, 0, 0);
Sleep(16);
keybd_event(0x0D, 0X0A, KEYEVENTF_KEYUP, 0);
Sleep(500);
}
}
int main()
{
while (1) {
thread t1(CheckInput);
thread t2(SendKey);
t1.join();
t2.join();
}
return 0;
}
This would go much smoother if you could use a Windows event loop.
If you can't, you should simulate it.
Create a thread-safe queue that can store event information. You need two types of events - keystrokes and timer ticks.
One thread will sleep for 500ms and send a timer tick event - in a loop. The other thread will call Console.ReadKey and send a keystroke event - also in a loop.
Your main thread will wait for events to arrive from the queue and handle them.
As the commenter mentioned, you need to put your loops in the threads, like in this example:
atomic_int temp;
std::thread t( [&temp]()
{
while( temp!= -1){
std::this_thread::sleep_for(std::chrono::milliseconds(500));
temp=0;
}
});
You can use a condition variable and have one thread to wait upon it.
The second thread(or the main loop) would wait for the keyboard input in a loop and signal the condition variable once the input arrives.
I've figured out to just check if the time since last time that Enter was pressed is over 500ms ago, so that it works off of system-time. Unfortunately I still have'nt found a way to have two simultaneous processes running at once, but this seems to work like I needed it to:
void SendKey() {
if (Active){
keybd_event(0x0D, 0x0A, 0, 0);
Sleep(16);
keybd_event(0x0D, 0X0A, KEYEVENTF_KEYUP, 0);
}
}
void CheckInput() {
auto t1 = Clock::now() + chrono::seconds(1);
int temp = 0;
while (temp == 0) {
if ((GetKeyState(0x12) & 0x8000) && (GetKeyState(0x4A) & 0x8000)) {
Active = !Active;
}
if (Clock::now() > t1) {
SendKey();
temp = 1;
}
}
}
int main()
{
while (1) {
thread t1(CheckInput);
t1.join();
}
return 0;
}
This basically stores the time that CheckInput was run at, and sets a set goal thats 1 second in the future. Then it will just keep checking the inputs and it will also check if the time has passed that set goal, which when it has, then it will call SendKey, before repeating again.