Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: why pollEvent is always used with a while loop?  (Read 4767 times)

0 Members and 2 Guests are viewing this topic.

glenjoker

  • Newbie
  • *
  • Posts: 6
    • View Profile
why pollEvent is always used with a while loop?
« on: February 26, 2015, 04:02:42 pm »
Why pollEvent is always used with a while loop like
while (window.pollEvent(event))
{
    //doing something accordingly to the type of events happened...
}
?

why can it be
if (window.pollEvent(event))
{
    //doing something accordingly to the type of events happened...
}
?

Is it solely because event could be not only just one event but a queue of events, and therefore it need to use while loop to process every event in the queue? If this is indeed the case, the rise another problem. I was just reading this book SFML game development, and in the first chapter, it teaches you to write a program that allows you to move a circle by press 'w', 'a', 's' and 'd', whose code is shown below:

#include<SFML/Graphics.hpp>
class Game;
int main()
{
    Game game;
    game.run();
}
class Game
{
public:
    Game();
    void run();
private:
    void processEvents();
    void update(sf::Time deltaTime);
    void render();
    void handlePlayerInput(sf::Keyboard::Key key, bool isPressed);

    sf::RenderWindow mWindow;
    sf::CircleShape mPlayer;
    bool mIsMovingUp = false;
    bool mIsMovingDown = false;
    bool mIsMovingRight = false;
    bool mIsMovingLeft = false;
};
Game::Game():mWindow(sf::VideoMode(640,480),"SFML Application"),mPlayer()
{
    mPlayer.setRadius(40.f);
    mPlayer.setPosition(100.f,100.f);
    mPlayer.setFillColor(sf::Color::Cyan);
}
void Game::run()
{
    sf::Clock clock;
    while (mWindow.isOpen())
    {
        sf::Time deltaTime = clock.restart();
        processEvents();
        update(deltaTime);
        render();
    }
}
void Game::processEvents()
{
    sf::Event event;
    while(mWindow.pollEvent(event))
    {
        switch(event.type)
        {
            case sf::Event::KeyPressed:
                handlePlayerInput(event.key.code, true);
                break;
            case sf::Event::KeyReleased:
                handlePlayerInput(event.key.code, false);
                break;
            case sf::Event::Closed:
                mWindow.close();
                break;
        }
    }
}
void Game::handlePlayerInput(sf::Keyboard::Key key, bool isPressed)
{
    if(key == sf::Keyboard::W)
        mIsMovingUp = isPressed;
    else if(key == sf::Keyboard::S)
        mIsMovingDown = isPressed;
    else if(key == sf::Keyboard::A)
        mIsMovingLeft = isPressed;
    else if(key == sf::Keyboard::D)
        mIsMovingRight = isPressed;
}
void Game::update(sf::Time deltaTime)
{
    sf::Vector2f movement(0.f, 0.f);
    if (mIsMovingUp)
        movement.y -= 100.f;
    if (mIsMovingDown)
        movement.y += 100.f;
    if (mIsMovingLeft)
        movement.x -= 100.f;
    if (mIsMovingRight)
        movement.x += 100.f;

    mPlayer.move(movement * deltaTime.asSeconds());
}
void Game::render()
{
    mWindow.clear();
    mWindow.draw(mPlayer);
    mWindow.display();
}

In this code, if the object 'event' contained two events at a time while processEvents() was run, say a Event::KeyPressed and a Event::KeyReleased, then after the while loop terminated after finishing processing the two events, the values of four bool type object, 'mIsMovingUp' and the other three, would be set to be false, which would not result in a movement of the circle, but the user indeed had pressed one of the four keys,  and was expecting to see the circle moving.
Therefore, I personally think that using while here with pollEvent would only jeopardize this program, and if would be a much better choice. Am I right?

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11034
    • View Profile
    • development blog
    • Email
Re: why pollEvent is always used with a while loop?
« Reply #1 on: February 26, 2015, 04:12:27 pm »
Please use the [code=cpp][/code] tag for posting code.

No, you're wrong because of this assumption:
if the object 'event' contained two events at a time
"the object 'event'" can never contain multiple events. The events get queued up internally and by calling pollEvent you'll get the top event of the queue. If you just poll one event per frame, you'll run into the issue, that events get processed delayed, e.g. if you press up and down, your character would move up in frame 1 and right in frame 2 instead of moving up and right in frame 1. Additionally if the queue gets filled quickly with for example mouse move events, your actions might get processed a few hundred milliseconds later.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

glenjoker

  • Newbie
  • *
  • Posts: 6
    • View Profile
Re: why pollEvent is always used with a while loop?
« Reply #2 on: February 26, 2015, 06:00:41 pm »
Thanks for the heads-up, and apologies for not using the tag. I'm new here, so I do not know the rule.  It actually took me quite a while to find if there is a icon for code font, but I didn't see one.

If the events indeed get queued up, and there are several events being processed per frame, then what if , as I said earlier in my post, there are two events being processed in one frame (I'm not sure if I understand it right. Is one frame equivalent to the main loop in run() being executed once?). One is KeyPressed (assume 'w' is the key that was pressed), and the other one is KeyReleased. These two events should be processes during function call of processEvents(), and when the call ends, the value of 'mIsMovingUp' would still be false ( during the function call, the while loop in processEvents() would be executed twice. In the first time, mIsMovingUp would be changed to be true, and in the twice time, it would be changed to be false). As a result, update(deltaTime) would not make any changes, and the circle would not move as well.

Jesper Juhl

  • Hero Member
  • *****
  • Posts: 1405
    • View Profile
    • Email
Re: why pollEvent is always used with a while loop?
« Reply #3 on: February 26, 2015, 06:11:04 pm »
You should process all events each frame but process them individually.
So let's say there is a keydown and a keyup event queued. You'd first procss the keydown event completely and update your simulation (move stuff etc) then process the keyup event completely etc etc for all queued events. Then when there are no more events to process you draw the final state of your simulation.
Then you repeat for the next frame.
« Last Edit: February 26, 2015, 06:13:27 pm by Jesper Juhl »

glenjoker

  • Newbie
  • *
  • Posts: 6
    • View Profile
Re: why pollEvent is always used with a while loop?
« Reply #4 on: February 27, 2015, 03:44:42 am »
What you described totally makes sense, but the problem is that the while loop that processes event is in the function processEvents(), and the function update(st::Time) is not inside this loop, which indicates that as long as the program begins to update simulation (it's when update() is called), it's out of this while loop in processEvents() already. Then, after function call update() terminates, it will go on to call render() to plot one frame.

Although I tested it myself with update() inside the while loop of processEvents(), the speed of the circle became non-constant. But why?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: why pollEvent is always used with a while loop?
« Reply #5 on: February 27, 2015, 07:42:10 am »
What Jesper Juhl said is usually not true. Indeed, if you get the pressed and released events for the same key within the same iteration of your game loop, depending on how you process these events, nothing may happen. But this will never happen unless you have super powers or an incredibly slow game loop...
Laurent Gomila - SFML developer

Jesper Juhl

  • Hero Member
  • *****
  • Posts: 1405
    • View Profile
    • Email
Re: why pollEvent is always used with a while loop?
« Reply #6 on: February 27, 2015, 07:55:14 am »
Well, it's true in the sense that that's how I usually process events :-)
But you are of course right in that getting a keydown/up event pair for the same key in a single frame is pretty unrealistic.
« Last Edit: February 27, 2015, 07:57:39 am by Jesper Juhl »

glenjoker

  • Newbie
  • *
  • Posts: 6
    • View Profile
Re: why pollEvent is always used with a while loop?
« Reply #7 on: February 27, 2015, 08:43:44 am »
I tested it out by using two static ints which were used to count the number of frames and events respectively, and printing out the number frames that had been produced whenever the while loop in the function run() was iterated and the number of events that had been processed whenever the while loop in the function processEvents() was iterated.
And indeed a KeyPressed event was always tens of frames apart from the following KeyReleased event. One other thing that interested me during the testing was that there was always an event whose type I did not know being processed immediately after every KeyPressed event in the same frame as the KeyPressed event. Could you tell me what the event was and why it was processed?

Moreover, in order to deal with case where there were one KeyPressed event and one KeyReleased event in the same queue and thus no update would be made (though it's very unlikely as you said), I shifted the function update() to inside the while loop in the function processEvents(), and run the program again, but this time the motion of the circle became extremely slow and unsteady as compared to before. I tested the number of frames produced per second as well as the value of deltaTime, and they were generally around the same amount before the change and after the change, so I was expecting it run as well as before it's changed, but it didn't. Why is it so?

The code used to print the number frames and loops as well as the changed code are below:

Before change:
#include<SFML/Graphics.hpp>
#include <iostream>
class Game;

class Game
{
public:
    Game();
    void run();
private:
    void processEvents();
    void update(sf::Time deltaTime);
    void render();
    void handlePlayerInput(sf::Keyboard::Key key, bool isPressed);

    sf::RenderWindow mWindow;
    sf::CircleShape mPlayer;
    bool mIsMovingUp = false;
    bool mIsMovingDown = false;
    bool mIsMovingRight = false;
    bool mIsMovingLeft = false;
    static int counter, frame;
};
int Game::counter = 1, Game::frame = 1;
int main()
{
    Game game;
    game.run();
}
Game::Game():mWindow(sf::VideoMode(640,480),"SFML Application"),mPlayer()
{
    mPlayer.setRadius(40.f);
    mPlayer.setPosition(100.f,100.f);
    mPlayer.setFillColor(sf::Color::Cyan);
}
void Game::run()
{
    sf::Clock clock;
    while (mWindow.isOpen())
    {
        sf::Time deltaTime = clock.restart();
        processEvents();
        update(deltaTime);
        render();
        ++frame;
    }
}
void Game::processEvents()
{
    sf::Event event;
    while(mWindow.pollEvent(event))
    {
        std::cout << "loop NO" << counter << std::endl;
        ++counter;
        std::cout << "frame NO" << frame << std::endl;
        switch(event.type)
        {
            case sf::Event::KeyPressed:
                handlePlayerInput(event.key.code, true);
                std::cout << "subloopPressed" << std::endl;
                break;
            case sf::Event::KeyReleased:
                handlePlayerInput(event.key.code, false);
                std::cout << "subloopReleased" << std::endl;
                break;
            case sf::Event::Closed:
                mWindow.close();
                break;
        }
    }
}
void Game::handlePlayerInput(sf::Keyboard::Key key, bool isPressed)
{
    if(key == sf::Keyboard::W)
        mIsMovingUp = isPressed;
    else if(key == sf::Keyboard::S)
        mIsMovingDown = isPressed;
    else if(key == sf::Keyboard::A)
        mIsMovingLeft = isPressed;
    else if(key == sf::Keyboard::D)
        mIsMovingRight = isPressed;
}
void Game::update(sf::Time deltaTime)
{
    sf::Vector2f movement(0.f, 0.f);
    if (mIsMovingUp)
        movement.y -= 100.f;
    if (mIsMovingDown)
        movement.y += 100.f;
    if (mIsMovingLeft)
        movement.x -= 100.f;
    if (mIsMovingRight)
        movement.x += 100.f;

    mPlayer.move(movement * deltaTime.asSeconds());
}
void Game::render()
{
    mWindow.clear();
    mWindow.draw(mPlayer);
    mWindow.display();
}
 



After change:
#include<SFML/Graphics.hpp>
#include <iostream>
class Game;

class Game
{
public:
    Game();
    void run();
private:
    void processEvents(sf::Time deltaTime);
    void update(sf::Time deltaTime);
    void render();
    void handlePlayerInput(sf::Keyboard::Key key, bool isPressed);

    sf::RenderWindow mWindow;
    sf::CircleShape mPlayer;
    bool mIsMovingUp = false;
    bool mIsMovingDown = false;
    bool mIsMovingRight = false;
    bool mIsMovingLeft = false;
    static int counter, frame;
};
int Game::counter = 1, Game::frame = 1;
int main()
{
    Game game;
    game.run();
}
Game::Game():mWindow(sf::VideoMode(640,480),"SFML Application"),mPlayer()
{
    mPlayer.setRadius(40.f);
    mPlayer.setPosition(100.f,100.f);
    mPlayer.setFillColor(sf::Color::Cyan);
}
void Game::run()
{
    sf::Clock clock;
    while (mWindow.isOpen())
    {
        sf::Time deltaTime = clock.restart();
        processEvents(deltaTime);
        render();
        ++frame;
    }
}
void Game::processEvents(sf::Time deltaTime)
{
    sf::Event event;
    while(mWindow.pollEvent(event))
    {
        std::cout << "loop NO" << counter << std::endl;
        ++counter;
        std::cout << "frame NO" << frame << std::endl;
        switch(event.type)
        {
            case sf::Event::KeyPressed:
                handlePlayerInput(event.key.code, true);
                std::cout << "subloopPressed" << std::endl;
                break;
            case sf::Event::KeyReleased:
                handlePlayerInput(event.key.code, false);
                std::cout << "subloopReleased" << std::endl;
                break;
            case sf::Event::Closed:
                mWindow.close();
                break;
        }
        update(deltaTime);
    }
}
void Game::handlePlayerInput(sf::Keyboard::Key key, bool isPressed)
{
    if(key == sf::Keyboard::W)
        mIsMovingUp = isPressed;
    else if(key == sf::Keyboard::S)
        mIsMovingDown = isPressed;
    else if(key == sf::Keyboard::A)
        mIsMovingLeft = isPressed;
    else if(key == sf::Keyboard::D)
        mIsMovingRight = isPressed;
}
void Game::update(sf::Time deltaTime)
{
    sf::Vector2f movement(0.f, 0.f);
    if (mIsMovingUp)
        movement.y -= 100.f;
    if (mIsMovingDown)
        movement.y += 100.f;
    if (mIsMovingLeft)
        movement.x -= 100.f;
    if (mIsMovingRight)
        movement.x += 100.f;

    mPlayer.move(movement * deltaTime.asSeconds());
}
void Game::render()
{
    mWindow.clear();
    mWindow.draw(mPlayer);
    mWindow.display();
}
 

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11034
    • View Profile
    • development blog
    • Email
Re: why pollEvent is always used with a while loop?
« Reply #8 on: February 27, 2015, 09:38:33 am »
One other thing that interested me during the testing was that there was always an event whose type I did not know being processed immediately after every KeyPressed event in the same frame as the KeyPressed event. Could you tell me what the event was and why it was processed?
Probably TextEntered event, but you can see for yourself by checking the event type.

Moreover, in order to deal with case where there were one KeyPressed event and one KeyReleased event in the same queue and thus no update would be made (though it's very unlikely as you said), I shifted the function update() to inside the while loop in the function processEvents(),
As you already noticed yourself a lot more frames get rendered compared to the amount of events that get process. If your update function is inside the event loop, your circle will only update when there's an event and not every frame.

You should really not bother about cases that will never happen and even if it somehow would happen, it's the expected case.
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

glenjoker

  • Newbie
  • *
  • Posts: 6
    • View Profile
Re: why pollEvent is always used with a while loop?
« Reply #9 on: February 27, 2015, 11:59:18 am »
Quote
As you already noticed yourself a lot more frames get rendered compared to the amount of events that get process. If your update function is inside the event loop, your circle will only update when there's an event and not every frame.

Yes, the circle will only be updated when there is an event happened, but is it the same when update() is out of the event loop? I mean even though in this case, the circle would be updated in every frame, but if this is no event, the update makes no change.

In both cases, if I press a key, say 'w', without releasing, then approximately the same number of events will be processed in a unit time, and since I mentioned earlier, the values of deltaTime roughly the same as well for both cases, I would expect that the circle moves by roughly the same distance in both cases, but it turned out to be very different. Why exactly did this happen?

glenjoker

  • Newbie
  • *
  • Posts: 6
    • View Profile
Re: why pollEvent is always used with a while loop?
« Reply #10 on: February 27, 2015, 01:50:46 pm »
Did some tests just now, and found out the update() did make changes every time a frame was rendered, which was why the circle moved faster.

Thanks for all your helps. I really appreciate it.

 

anything