When events should be used in SFML? - c++

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.

Related

Show video while saving it

I have a folder full of images, I need to save these images into the video while doing so I want to show the user the video being played from these images (frames). I can run two separate processes one for the saving and one for the showing but this is not what I am looking for, I want to do both in one step. If you know a solution please let me know.
My code uses C++ with OpenCV but feel free to share with me any code written with any language, or event a concept.
I use gStreamer, ffmpeg as well for the video generation, so I am not looking how to save a video or how to show the video I am looking for a process that can do both in one operation.
here's my quick brew using SFML. You have to link SFML and include the include folder. Simply set directory to your target folder, I use std::filesystem to add all files from that folder to a std::vector<std::string>.
#include <SFML/Graphics.hpp>
#include <vector>
#include <string>
#include <iostream>
#include <filesystem>
void add_frame_to_video(std::string path) {
//your encoding stuff ...
}
int main() {
auto dimension = sf::VideoMode(1280u, 720u);
sf::RenderWindow window;
window.create(dimension, "Video Rendering");
sf::Texture frame_texture;
std::vector<std::string> frame_paths;
std::string directory = "resources/";
for (auto i : std::filesystem::directory_iterator(directory)) {
frame_paths.push_back({ i.path().string() });
}
std::size_t frame_ctr = 0u;
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
}
}
if (frame_ctr >= frame_paths.size()) {
window.close();
return EXIT_FAILURE;
}
const auto& current_frame = frame_paths[frame_ctr];
add_frame_to_video(current_frame);
if (!frame_texture.loadFromFile(current_frame)) {
std::cout << "Error: couldn't load file \"" << current_frame << "\".\n";
window.close();
return EXIT_FAILURE;
}
sf::Sprite frame_sprite;
frame_sprite.setTexture(frame_texture);
++frame_ctr;
window.clear();
window.draw(frame_sprite);
window.display();
}
}
this will create a window that shows each picture per frame using sf::Texture to load and sf::Sprite to display the image. If no more frames are available, the window gets closed and the program terminates. If you want to add time between frames, you can set e.g. window.setFramerateLimit(10); to have 10 frames / second.

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.

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();
}

How to clear event input after a key press?

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.