SFML community forums

Help => Window => Topic started by: EvgehaRYTP on March 13, 2023, 09:08:06 am

Title: Problem with sf::Event being used in classes
Post by: EvgehaRYTP on March 13, 2023, 09:08:06 am
I have an Application class which is the main class and is included in main. The Menu class is connected to it (the first thing the user sees when he opens the game), which is one of the game states. (MenuState, GameState, PauseStae ..). So, sf:: Event in the app class works as expected, but creating a new one in the subsequent menu and game classes does not work as we would like. For the test, it simply outputs the text to the console if some key is pressed in the events. Displayed with a very long delay and every other time, and if you hold it down, it starts to fly away. I need this to process button clicks, because for example, if the positions of the button to start the game in the menu and the exit button in the game itself are the same, then it can throw back and forth from the game to the menu, from the menu to the game (through the usual check of pressing the button / mouse, several work at a time). Please help fix.
P.S. Sorry for my english, i use a translator

class Application
#include "Application.h"
#include <Windows.h>
 
void Application::init()
{
    int scrX = GetSystemMetrics(SM_CXSCREEN);
    int scrY = GetSystemMetrics(SM_CXSCREEN);
   
    this->window = new sf::RenderWindow(sf::VideoMode(1920, 1080), "Space");
 
    this->states.push(new Menu(this->window, &this->states));
}
 
Application::Application()
{
    init();
}
 
Application::~Application()
{
    delete window;
 
    while (!states.empty())
    {
        delete states.top();
        states.pop();
    }
}
 
void Application::updateEvent()
{
    while (window->pollEvent(event))
    {
        if (event.type == sf::Event::Closed)
            window->close();
 
        if (event.type == sf::Event::MouseButtonPressed)
        {
            if (event.key.code == sf::Mouse::Left)
            {
                std::cout << "MS LEFT PRESS FOR EVENTS" << std::endl;
            }
        }
        if (event.type == sf::Event::KeyPressed)
        {
            if (event.key.code == sf::Keyboard::A)
            {
                std::cout << "AAAAAA";
            }
        }
    }
}
 
void Application::update()
{
    this->updateEvent();
    this->deltaTime = clock.restart();
    if (!states.empty())
    {
     
        states.top()->update(deltaTime);
        //states.top()->updateEvents(event);
 
        if (states.top()->getExit())
        {
            states.top()->endState();
            delete states.top();
            states.pop();
        }      
    }
    else
    {
        this->window->close();
    }
}
 
void Application::render()
{
    this->window->clear();
 
    if (!states.empty())
        states.top()->render();
 
    this->window->display();
}
 
void Application::run()
{
    while (window->isOpen())
    {
        update();
        render();
    }
}
 

class Menu
#include "Menu.h"
 
void Menu::initButton()
{
    //m_texture.loadFromFile("res\\textures\\sdgagasd.png");
    //this->buttons["BUTTON_PLAY"] = new Button(350.f, 350.f, 40.f, 40.f, this->font, "Play", sf::Color::Yellow, sf::Color::White, sf::Color::Blue);
    //this->buttons["BUTTON_EXIT"] = new Button(400.f, 400.f, 40.f, 40.f, this->font, "Exit", sf::Color::Yellow, sf::Color::White, sf::Color::Blue);
    this->buttons["BUTTON_PLAY"] = new Button(200.f, 300.f, 0.5f, "buttonPlay1.png", "buttonPlayHover.png");
    this->buttons["BUTTON_LOAD"] = new Button(200.f, 400.f, 0.5f, "btnLoad.png", "btnLoadHover.png");
    this->buttons["BUTTON_OPTIONS"] = new Button(200.f, 500.f, 0.5f, "btnOptions.png", "btnOptionsHover.png");
    this->buttons["BUTTON_EXIT"] = new Button(200.f, 600.f, 0.5f, "buttonExitIdle.png", "buttonExitHover.png");
}
 
void Menu::init()
{
    this->font.loadFromFile("res\\fonts\\wiguru-13.ttf");
    this->initButton();
}
 
Menu::Menu(sf::RenderWindow* window, std::stack<State*>* states) :
    State(window, states)
{
    this->init();
}
 
Menu::~Menu()
{
    for (auto it = buttons.begin(); it != buttons.end(); it++)
    {
        delete it->second;
    }
}
 
void Menu::endState()
{
    std::cout << "End Menu" << std::endl;
}
 
void Menu::updateKeyBinds(const sf::Time deltaTime)
{
    this->checkEnd();
}
 
void Menu::updateEvents()
{
    while (this->window->pollEvent(eventsMenu))
    {
        if (eventsMenu.type == sf::Event::KeyPressed)
        {
            if (eventsMenu.key.code == sf::Keyboard::A)
            {
                std::cout << "AAA_AAA";
            }
        }
    }
}
 
 
void Menu::update(const sf::Time deltaTime)
{
    this->updateMouse();
    this->updateKeyBinds(deltaTime);
    updateEvents();
 
    for (auto it = buttons.begin(); it != buttons.end(); it++)
    {
        it->second->update(mousePosView);
    }
 
    if (this->buttons["BUTTON_PLAY"]->getisPressed())
    {
        this->states->push(new Game(this->window, this->states));
    }
 
    if (this->buttons["BUTTON_EXIT"]->getisPressed())
    {
        this->exit = true;
    }
}
 
void Menu::render(sf::RenderTarget* target)
{
    if (!target)
        target = this->window;
 
    for (auto it = buttons.begin(); it != buttons.end(); it++)
    {
        it->second->render(target);
    }
}
 
Title: Re: Problem with sf::Event being used in classes
Post by: eXpl0it3r on March 14, 2023, 11:04:29 pm
If you call pollEvent in a loop, you're reading all the available events until the internal "queue" is empty. Since you do that in the Application every time before you call the state update function, there are no events to be read in the state class, as you've just read all of them.

Or said, differently, you can only ever have one active pollEvent-loop.
If you want the application to continue processing events, then you'll have to change the design and pass the event itself down to the state inside the Application pollEvent-loop, that way the state has a chance to process the event as well.
Title: Re: Problem with sf::Event being used in classes
Post by: EvgehaRYTP on March 15, 2023, 03:55:00 pm
If you call pollEvent in a loop, you're reading all the available events until the internal "queue" is empty. Since you do that in the Application every time before you call the state update function, there are no events to be read in the state class, as you've just read all of them.

Or said, differently, you can only ever have one active pollEvent-loop.
If you want the application to continue processing events, then you'll have to change the design and pass the event itself down to the state inside the Application pollEvent-loop, that way the state has a chance to process the event as well.
Thank you very much! Indeed, it was necessary to add a virtual function to the state class and everything works correctly in each state. I would also like to ask, I am new to programming and I wonder if it is necessary to pass sf:: Event with the & operator in the function parameters?
void updateEvents(sf::Event& event)
 
That is, is it necessary to change its original initialization? (I don't know how to put it right). Thank you!
Title: Re: Problem with sf::Event being used in classes
Post by: eXpl0it3r on March 15, 2023, 04:30:40 pm
It's generally recommended for larger types to pass by reference, so you don't have to unnecessarily copy all the data. For events it's somewhere in between, I'd however still recommend to take the reference, even more so the const reference, so you're not able to change the event, as the event should be consumed as is,