Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Autor: Piotr Szawdyński
Biblioteki C++

Obsługa zdarzeń - klawiatura, mysz i inne

[lekcja] Rozdział 6. Teoria ogólna o zdarzeniach w bibliotece SFML 2.0; omówienie obsługi zdarzeń takich jak: zamykanie okna, obsługa myszy i klawiatury oraz ich praktyczne przykłady. Ponadto opis ogólny pozostałych zdarzeń biblioteki SFML takich jak np. joystick.

Wprowadzenie

Zanim przystąpimy do rysowania pierwszych obiektów na scenie musimy przerobić jeszcze kilka ważnych rzeczy, a jedną z nich są zdarzenia (ang. events). Zdarzenia odpowiadają bowiem za wszelką interakcję z oknem - począwszy od obsługi przycisku zamykającego okno, a skończywszy na obsłudze klawiatury, myszy czy też joysticka.

Jak jest zorganizowana obsługa zdarzeń w SFML

Zdarzenia w bibliotece SFML 2.0 są magazynowane przez okno. Każde okno posiada swój własny magazyn. Magazyn zdarzeń jest w rzeczywistości kolejką do której trafiają aktualne zdarzenia. Zdarzenia oczekujące należy systematycznie odczytywać - w przeciwnym wypadku możesz doprowadzić do sytuacji w której zdarzenia będą obsługiwane z dużym opóźnieniem bądź w skrajnych przypadkach nie zostaną obsłużone wcale.

W celu zapewnienia prawidłowej obsługi zdarzeń twórcy SFML zalecają odczytywać wszystkie oczekujące zdarzenia przy każdym obiegu pętli głównej programu. Jak to zrobić? Zaraz się tego dowiesz :)

Odczytywanie oczekujących zdarzeń

W poprzednim rozdziale utworzyliśmy obiekt okna typu sf::Window. Klasa sf::Window biblioteki SFML posiada metodę pollEvent. Metoda ta przyjmuje jako argument referencję na zmienną typu sf::Event, która w rzeczywistości jest klasą. W przypadku gdy uda się pobrać zdarzenie z kontenera to metoda pollEvent zwróci prawdę (wartość true), a zmienna typu sf::Event przekazana poprzez argument zostanie wypełniona informacjami o odczytanym zdarzeniu. Jeżeli w magazynie zdarzeń nie będzie więcej zdarzeń do odczytania - metoda pollEvent zwróci fałsz, tj. wartość false.

Skoro wiemy już jak sprawy się mają w teorii to zobaczmy teraz jak będzie to wyglądało w praktyce:
C/C++
//zmienna oknoAplikacji jest obiektem typu sf::Window

sf::Event zdarzenie;
if( oknoAplikacji.pollEvent( zdarzenie ) )
{
    //tu obsługa zdarzenia
}
Powyższy zapis odczyta tylko jeden komunikat z magazynu zdarzeń (pod warunkiem, że zdarzenie będzie istniało). Nam zależeć będzie jednak na odczytaniu wszystkich zdarzeń oczekujących w magazynie na obsłużenie, więc instrukcję warunkową if zastąpimy pętlą while:
C/C++
sf::Event zdarzenie;
while( oknoAplikacji.pollEvent( zdarzenie ) )
{
    //tu obsługa zdarzeń
}
Tak jak już wcześniej pisałem pętla odczytująca zdarzenia powinna znajdować się w pętli głównej programu - w praktyce więc Twój kod powinien być zbudowany mniej więcej tak:
C/C++
#include <SFML/Window.hpp>

int main()
{
    sf::Window oknoAplikacji( sf::VideoMode( 800, 600, 32 ), "Kurs SFML 2.0 - http://cpp0x.pl" );
    while( oknoAplikacji.isOpen() )
    {
        sf::Event zdarzenie;
        while( oknoAplikacji.pollEvent( zdarzenie ) )
        {
            //tu obsługa zdarzeń
        }
       
        oknoAplikacji.display();
    }
    return 0;
}

Obsługa zdarzeń

Skoro wiemy już jak odczytywać zdarzenia to dobrze by było umieć je jeszcze obsłużyć. Zanim to jednak nastąpi zapoznajmy się najpierw z typami zdarzeń, jakie mamy do dyspozycji. Wszystkie typy zdarzeń, które możemy obsłużyć zostały umieszczone w klasie sf::Event.
Nazwa zdarzeniaOpis zdarzenia
ClosedZostało wysłane żądanie zamknięcia okna.
ResizedOkno zmieniło swój rozmiar.
LostFocusOkno straciło zaznaczenie.
GainedFocusOkno odzyskało zaznaczenie.
TextEnteredZostał wprowadzony znak, który jest uznawany za tekst (np. litery, znaki specjalne, spacja, cyfry). Dodatkowo: ENTER, BACKSPACE i ESC.
KeyPressedKlawisz został wciśnięty.
KeyReleasedKlawisz został puszczony.
MouseWheelMovedScroll myszy został przekręcony.
MouseButtonPressedPrzycisk myszy został wciśnięty.
MouseButtonReleasedPrzycisk myszy został puszczony.
MouseMovedPołożenie zwracane przez mysz zostało zmienione.
MouseEnteredMysz powróciła do obszaru okna.
MouseLeftMysz opuściła obszar okna.
JoystickButtonPressedPrzycisk joysticka został wciśnięty.
JoystickButtonReleasedPrzycisk joysticka został puszczony.
JoystickMovedPołożenie zwracane joystick zostało zmienione.
Typ otrzymanego zdarzenia możemy odczytać z pola type należącego do klasy sf::Event. Dla niektórych zdarzeń otrzymanych przez metodę pollEvent ustawiane są dodatkowo niektóre pola klasy sf::Event, które umożliwiają odczytanie dodatkowych informacji na temat zdarzenia jakie miało miejsce. Poniższa tabela zawiera informacje o tym jakie dodatkowe pola klasy sf::Event są ustawione w zależności od zdarzenia:
Nazwa zdarzeniaOpis ustawionych pól klasy sf::Event (bez pola type)
Resized
  • zdarzenie.size.width - zawiera nową szerokość okna. Szerokość jest wyrażona w pikselach.
  • zdarzenie.size.height - zawiera nową wysokość okna. Wysokość jest wyrażona w pikselach.
TextEntered
  • zdarzenie.text.unicode - zawiera kod znaku, który został wprowadzony. Znak jest w formacie kodującym UTF-32.
KeyPressed,
KeyReleased
  • zdarzenie.key.code - zawiera kod znaku, który został wciśnięty bądź puszczony.
  • zdarzenie.key.alt - informuje czy klawisz Alt był wciśnięty czy nie.
  • zdarzenie.key.control - informuje czy klawisz Control był wciśnięty czy nie.
  • zdarzenie.key.shift - informuje czy klawisz Shift był wciśnięty czy nie.
MouseButtonPressed,
MouseButtonReleased
  • zdarzenie.mouseButton.button - zawiera informację o przyciskach myszy, które zostały wciśnięte bądź puszczone.
MouseMoved
  • zdarzenie.mouseMove.x - wskazuje aktualną pozycję myszy (względem okna) w osi X.
  • zdarzenie.mouseMove.y - wskazuje aktualną pozycję myszy (względem okna) w osi Y.
MouseWheelMoved
  • zdarzenie.mouseWheel.delta - informuje o pokręceniu scrollem myszy. Wartość dodatnia jeżeli do przodu, wartość ujemna jeżeli do tyłu.
JoystickButtonPressed,
JoystickButtonReleased
  • zdarzenie.joystickButton.joystickId - numer joysticka (może być 0 lub 1).
  • zdarzenie.joystickButton.button - numer przycisku, który został wciśnięty. Wartość w przedziale od 0 do 15 włącznie.
JoystickMoved
  • zdarzenie.joystickMove.joystickId - numer joysticka (może być 0 lub 1).
  • zdarzenie.joystickMove.axis - zawiera przesunięcie pozycji w osiach X i Y.
  • zdarzenie.joystickMove.position - zawiera obecną pozycję w osiach X i Y. Wartość w przedziale od -100 do 100 włącznie (za wyjątkiem POV, która jest w przedziale od 0 do 360]).

Obsługa zdarzenia zamykającego okno

Skoro mamy już ogromną wiedzę teoretyczną na temat zdarzeń za sobą, zajmijmy się teraz nimi od strony praktycznej. Pierwszym zdarzeniem, które zostanie omówione to obsługa przycisku zamykającego okno. W Bibliotece SFML wygląda to następująco:
C/C++
if( zdarzenie.type == sf::Event::Closed )
{
    //zostało wysłane żądanie zamknięcia okna - tu jego obsługa
}
Samo przyjście zdarzenia nie powoduje jednak zamknięcia okna - jest to tylko informacja, że użytkownik chciał zamknąć okno. Dzięki temu mamy możliwość zapisania stanu gry przed zamknięciem aplikacji, czy też wyświetlenie dodatkowego komunikatu potwierdzającego zamknięcie aplikacji.

Zamykanie okna

Skoro już wiemy jak podpiąć się pod zdarzenie zamknięcia okna, sprawmy teraz by się ono zamknęło. Do tego celu wykorzystać musimy metodę close, która należy do klasy sf::Window. Kompletny kod zamykający okno może wyglądać tak:
C/C++
if( zdarzenie.type == sf::Event::Closed )
{
    //tu np. zapisanie stanu gry
    oknoAplikacji.close();
}

Obsługa klawiatury

Zdarzenia klawiatury obsługuje się analogicznie do zdarzenia zamknięcia okna. Najpierw sprawdzamy czy zdarzenie jest typu sf::Event::KeyPressed bądź sf::Event::KeyReleased w zależności od tego jakie nas interesuje. Klasa Event posiada pole o nazwie key z którego możemy odczytać kod klawisza, który został wciśnięty oraz jaki był stan klawiszy funkcyjnych (ALT, CTRL i SHIFT) w chwili gdy zaszło to zdarzenie. Kody klawiszy są zdefiniowane przez bibliotekę SFML i ich definicje są umieszczone w klasie sf::Keyboard w pliku Keyboard.hpp. Dla małych liter jak również cyfr - kod klawisza jest taki sam jak dla kodu ASCII. Oznacza to, że zapis 'p' jest równoważny zapisowi sf::Keyboard::P, a zapis '6' jest równoważny zapisowi sf::Keyboard::Num6. Specjalnej filozofii w korzystaniu ze zdarzeń klawiatury nie ma, więc przejdźmy teraz do przykładu:
C/C++
if( zdarzenie.type == sf::Event::KeyPressed && zdarzenie.key.code == sf::Keyboard::Escape )
{
    //obsługa zajścia zdarzenia wciśnięcia klawisza ESC
    oknoAplikacji.close(); //np. zamknięcie aplikacji
}

Obsługa myszy

Jak zapewne zauważyłeś obsługa zdarzeń w bibliotece SFML 2.0 nie wymaga specjalnie dużych umiejętności programowania, jak również wielu linii kodu. Mysz jest równie prosta w obsłudze co klawiatura. Tabela, która została przedstawiona w niniejszym rozdziale zawiera pola klasy, które są modyfikowane dla poszczególnych zdarzeń - w tym myszy. Przykładowo zdarzenie MouseButtonPressed wypełnia pole mouseButton.button dla klasy typu sf::Event. Mysz tak samo jak klawiatura posiada definicje przycisków. Definicje te są następujące:
  • sf::Mouse::Left - lewy przycisk myszy;
  • sf::Mouse::Right - prawy przycisk myszy;
  • sf::Mouse::Middle - środkowy przycisk myszy (może być to scroll myszy);
  • sf::Mouse::XButton1 - pierwszy dodatkowy przycisk myszy;
  • sf::Mouse::XButton2 - drugi dodatkowy przycisk myszy.
Jeżeli będziemy chcieli aby okno SFML zamykało się w momencie gdy zostanie wciśnięty środkowy klawisz, wystarczy napisać następujący kod:
C/C++
if( zdarzenie.type == sf::Event::MouseButtonPressed && zdarzenie.mouseButton.button == sf::Mouse::Middle )
     oknoAplikacji.close();

Pozostałe zdarzenia myszy obsługuje się analogicznie - przypominam, że szczegółowe informacje o wszystkich zdarzeniach zostały zawarte w jednej z tabel niniejszego rozdziału.

Przykład

Podsumujmy teraz całą wiedzę wyniesioną z niniejszego rozdziału jednym, krótkim kodem programu:
C/C++
#include <SFML/Window.hpp>

int main()
{
    sf::Window oknoAplikacji( sf::VideoMode( 800, 600, 32 ), "Kurs SFML 2.0 - http://cpp0x.pl" );
    while( oknoAplikacji.isOpen() )
    {
        sf::Event zdarzenie;
        while( oknoAplikacji.pollEvent( zdarzenie ) )
        {
            if( zdarzenie.type == sf::Event::Closed )
                 oknoAplikacji.close();
           
            if( zdarzenie.type == sf::Event::KeyPressed && zdarzenie.key.code == sf::Keyboard::Escape )
                 oknoAplikacji.close();
           
            if( zdarzenie.type == sf::Event::MouseButtonPressed && zdarzenie.mouseButton.button == sf::Mouse::Middle )
                 oknoAplikacji.close();
           
        }
        oknoAplikacji.display();
    }
    return 0;
}

Obsługa innych zdarzeń

Inne zdarzenia, które są dostępne w bibliotece SFML 2.0 obsługuje się analogicznie do tych, które zostały już omówione. Nie wydaje mi się jednak aby przytaczanie ich obszernego opisu wraz z przykładami miało na chwilę obecną większy sens dlatego też pozwolę sobie w tym miejscu zakończyć niniejszy rozdział by móc omówić kolejne interesujące aspekty biblioteki SFML :)
Poprzedni dokument Następny dokument
Pierwsza aplikacja Bezpośredni dostęp do klawiatury, myszy i joysticka