Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

[SFML] Obsługa przycisku

Ostatnio zmodyfikowano 2017-07-14 20:32
Autor Wiadomość
Euvarios
Temat założony przez niniejszego użytkownika
[SFML] Obsługa przycisku
» 2017-07-14 19:23:35
Witam, staram się stworzyć kilka projektów przy pomocy SFML i postanowiłem skupić się na czymś, co ułatwi mi pracę w przyszłości - edytor GUI. Na początek stworzyłem klasy przycisków. Zawiera ona metody takie jak: sprawdzenie czy kursor znajduje się w obrębie przycisku, sprawdzenie czy przycisk został przyciśnięty, oraz czy został kliknięty. Całość działa, mam jednak problem z umiejscowieniem metody aktualizacji logiki przycisku. Na początek potrzebne pliki (nie są długie, wyciąłem z nich zbędne dla tematu klasy) :
gui.h : https://pastebin.com/XuvJ9Ctz
gui.cpp : https://pastebin.com/wsSZamYF
main.cpp : https://pastebin.com/FXz3p30X

A to najważniejsze metody przycisku (pominąłem ten sprawdzający, czy kursor jest w jego obrębie):

C/C++
void RectangularButton::update( sf::Event action ) {
    checkIfPressed( action );
    checkIfClicked( action );
}

void RectangularButton::checkIfPressed( sf::Event action ) {
    if( isMouseOver() && action.type == sf::Event::MouseButtonPressed && action.mouseButton.button == sf::Mouse::Left )
         pressed = true;
   
}

void RectangularButton::checkIfClicked( sf::Event action ) {
    if( pressed ) {
        if( isMouseOver() && action.type == sf::Event::MouseButtonReleased && action.mouseButton.button == sf::Mouse::Left ) {
            clicked = true;
            pressed = false;
        }
        else if( action.type == sf::Event::MouseButtonReleased && action.mouseButton.button == sf::Mouse::Left )
             pressed = false;
       
    }
    else clicked = false;
   
}

Oto pętla główna programu:

C/C++
int main() {
    sf::RenderWindow mainWindow( sf::VideoMode( 800, 600, 32 ), "Test GUI" );
    sf::Event action;
    RectangularButton button( & mainWindow, sf::Vector2f( 250.f, 125.f ), sf::Vector2f( 275.f, 290.f ), 3 );
   
    while( mainWindow.isOpen() ) {
        while( mainWindow.pollEvent( action ) ) {
            if( action.type == sf::Event::Closed ) mainWindow.close();
           
            button.update( action );
        }
        std::cout << "Przycisniety: " << button.isPressed() << std::endl;
        std::cout << "Klikniety: " << button.isClicked() << std::endl;
       
        mainWindow.clear();
        system( "cls" ); //Tymczasowa linijka, by wygodniej sprawdzało się powyższe wyniki
        button.draw();
        mainWindow.display();
    }
    return 0;
}

Chodzi o moment obsługi klawiatury i myszki. Z tego co zrozumiałem wszystkie eventy myszki i klawiatury wrzucane są na pewien stos, gdzie ostatni event jest na jego szczycie. mainWindow.pollEvent(action) ma służyć do wyciągania tego eventu i przypisanie go do action.
Dobra i teraz pojawia się problem. Są dwa scenariusze.

1. Daję przycisk.update(sf::Event) poza pętlą wyciągającą wszystkie eventy z danego obrotu programu. Dzięki temu logika aktualizowana jest zawsze. Problem jednak występuje gdy użytkownik zacznie wciskać wiele przycisków w tym samym czasie. Np. "A", "LPM" i "Spacja" (i mimo, że wydarzyły się w tym samym obrocie programu, zostaną wrzucone do stosu w określonej kolejności). Pętla przejdzie przez wszystkie eventy ale po wyjściu z pętli tylko ostatni zostanie w action. Przycisk uzna, że nie został przyciśnięty (bo użyto spacji) mimo, że użytkownik użył na nim LPM.

2. Daję przycisk.update(sf::Event) do pętli wyciągającą wszystkie eventy z danego obrotu programu. Nie ma tu możliwości błędu. Nawet jak użytkownik wciśnie na raz 3 przyciski, przycisk.update(sf::Event) wykona się dla każdego z nich. Brzmi pięknie, jaki jest problem? Przy odpowiednich okolicznościach aktualizacja może nie nastąpić w danym obrocie. Kiedy? Otóż kiedy nie nastąpi żaden event mainWindow.pollEvent(action) zwraca false, czyli pętla while się nie wykona - nie wykona się przycisk.update(sf::Event). Wyobraźcie sobie taką sytuację. Użytkownik wciska przycisk i zwalnia LPM. Clicked przyjmuje wartość true. Użytkownik po zwolnieniu przycisku niczego nie dotyka. Nie następuje event. Nie wykonuje się przycisk.update(sf::Event), wartość nie może zmienić się na false tak jak powinna, zamist tego trwa ciągle przy true.

Czy ktoś ma pomysł na rozwiązanie tego problemu? Jak powinienem umiejscowić przycisk.update(sf::Event), żeby wszytko działało? Za wszystkie odpowiedzi z góry dziękuję.
P-163299
pekfos
» 2017-07-14 20:11:44
Z tego co zrozumiałem wszystkie eventy myszki i klawiatury wrzucane są na pewien stos
To źle zrozumiałeś. To kolejka, nie stos.

Jak powinienem umiejscowić przycisk.update(sf::Event), żeby wszytko działało?
przycisk.onEvent(sf::Event) w pętli eventów, przycisk.update() poza pętlą eventów. Jakbyś sam przeczytał to wszystko co napisałeś, to pewnie sam byś na to wpadł. Co od eventów, to w eventach, co ma być zawsze, to wołać zawsze.
P-163309
Euvarios
Temat założony przez niniejszego użytkownika
» 2017-07-14 20:32:27
Dziękuję za odpowiedź. Masz rację, myślałem, że .pollEvent() działa na zasadzie stosu, okazało się, że to kolejka. Masz również rację, że rozwiązanie było stosunkowo proste. Uparłem się jednak (niepotrzebnie), by ograniczyć ilość metod koniecznych do działania obiektu do minimum. obiekt.draw() oraz obiekt.update() wydawały mi się idealne pod względem prostoty. Przerobię program i dodam osobną metodę na logikę i eventy. Jeszcze raz dziękuję za pomoc.
P-163311
« 1 »
  Strona 1 z 1