Zdarzenia
Nasza gra nie miałaby większego sensu gdybyśmy nie potrafili reagować na polecenia wydawane przez gracza. Powinniśmy zaimplementować zatem obsługę takich
zdarzeń jak:
-
wciśnięcie klawisza klawiatury
-
zmiana pozycji myszki
Aby to zrobić użyjemy zdarzeń ( ang. events ) zdefiniowanych w bibliotece SDL. Oto kod funkcji odpowiedzialnej za obsługę zdarzeń:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| bool processInput( void )
{
// zmienna pomocnicza
SDL_Event event;
// czy są jakieś czekające na przetworzenie zdarzenia ?
if ( SDL_PollEvent( &event ) )
{
// sprawdzenie typu zdarzenia
switch( event.type )
{
// wyjście z aplikacji
case SDL_QUIT:
return false;
// użytkownik nacisnął któryś z klawiszy na klawiaturze
case SDL_KEYDOWN:
// jeżeli nacisnał klawisz eskape zamykamy aplikacje
if( event.key.keysym.sym == SDLK_ESCAPE )
{
return false;
}
break;
case SDL_MOUSEMOTION:
// tutaj będzie kod odpowiedzialny za obsługę zdarzeń myszki
break;
}
}
return true;
} |
Funkcja ta zwraca wartość
false
jeżeli aplikacja powinna się zakończyć i
true
w przeciwnym przypadku.
Przyjrzyjmy się kolejnym jej fragmentom:
-
Alokacja pamięci na informacje o zdarzeniu ( linia 4 )
-
Odpytanie biblioteki czy czekają na nas jakieś nie przetworzone zdarzenia ( linia 6 )
-
Obsługa zdarzeń konkretnego typu: ( linie od 10 do 26 )
-
SDL_QUIT - zamknięcie aplikacji przez system lub użytkownika
-
SDL_KEYDOWN - wciśnięcie dowolnego klawisza na klawiaturze
-
SDL_MOUSEMOTION - zmiana położenia myszki ( lub innego kontrolera ją zastępującego )
Składamy wszystko razem
Wykorzystując przedstawione fragmenty, stworzymy teraz działającą aplikację, która stanowić będzie podstawę naszej gry. Program nasz będzie na razie tylko
konstruował okno z podpiętym kontekstem Open GL oraz reagował na jego zamknięcie i wciśnięcie klawisza
Escape
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| /**
* @brief Główna metoda programu ( entry point )
* @param argc ilość argumentów przekazanych do programu
* @param argv tablica wartości argumentów
*/
int main( const int /*argc*/, const char** /*argv*/ )
{
try
{
// wywołanie funkcji inicjującej
initSDL();
// wykorzystanie funkcji obsługi zdarzeń
while( processInput() )
{
//tutaj obsługa pętli gry
}
}
catch( const std::exception& e )
{
std::cerr << "Exception caught: " << e.what() << std::endl;
}
SDL_Quit();
return 0;
} |
Open GL i renderowanie
Na tym etapie nasz program wyświetla jedynie czarne tło. Nauczmy się zatem jak wyświetlać podstawowe bryły geometryczne.
Zaczniemy od ustawienia biblioteki Open GL w odpowiedni stan. Napiszmy nową funkcję o nazwie
initOpenGL
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| /**
* @brief Iniclalizacja stanu poczatkowego niektorych
* wartosci Open GL.
* @param width szerokosc ekranu
* @param height wysokosc ekranu
*/
void initOpenGL( int width, int height )
{
glMatrixMode( GL_PROJECTION );
gluPerspective( 45.0f, width / ( GLfloat ) height, 0.1f, 100.0f );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
} |
-
Linia 9 to sposób na powiedzenie bibliotece Open GL że będziemy od tego momentu operować na macierzy rzutowania perspektywicznego.
Macierz ta ogólnie mówiąc jest wykorzystywana do matematycznego przedstawienia wycinka przestrzeni ( w kształcie ostrosłupa ) widzianego przez obserwatora w naszym trójwymiarowym świecie i
odwzorowania tego wycinka na ekranie.
-
Linia 10 to wywołanie funkcji z biblioteki pomocniczej GLU, której parametry to kolejno:
-
kąt widzenia obserwatora podawany w stopniach
-
stosunek szerokości do wysokości ekranu
-
odległość tzw. bliskiej płaszczyzny obcinania
-
odległość tzw. dalekiej płaszczyzny obcinania
-
Linia 11 to analogicznie do polecenia z linii 9 ustawienie Open GL na tryb pracy z macierzą widoku. Macierz widoku odpowiada
za przekształcenia wyświetlanych obiektów. Możliwe do uzyskania przekształcenia to:
-
przesunięcie -
glTranslate
-
skalowanie -
glScale
-
obrót wokół wybranej osi -
glRotate
-
Polecenie z linii 12 ładuje tzw. macierz jednostkową do ustawionej macierzy widoku.
-
Ostatnia linia to ustawienie sposobu prezentacji wielokątów tak aby renderowane były jedynie ich krawędzie. Gdy poznamy jak działa oświetlenie,
będziemy wyświetlać wypełnione bryły, a na razie ograniczymy się jedynie do widoku ich krawędzi.
Aby lepiej zrozumieć wymienione wyżej pojęcia spójrzmy na rysunek przedstawiający widok przykładowej sceny składającej się z sześcianu. Na rysunku
zobaczymy wspomniany sześcian ( kolor czerwony ) oraz wizualizację ostrosłupa obserwatora ( kolor prawie biały ). Ostrosłup obserwatora obejmuje
wycinek przestrzeni który pojawi się na naszych ekranach w rezultacie operacji renderowania.
Rys. 2 Przykładowa scena.
a.) środek układu współrzędnych zwany globalnym układem współrzędnych - pozycja obserwatora
b.) obiekt sceny i jego lokalny układ współrzędnych
Rys. 3 Przykładowa scena, widok z góry.
-
na obu rysunkach strzałki w kolorze zielonym wskazują oś -Z, kolor czerwony to oś +X, natomiast niebieski to oś +Y
-
wymiary ostrosłupa widzenia w Open GL ( na rys. 2 i rys. 3 kolor prawie biały ) modyfikujemy za pomocą macierzy projekcji
( GL_PROJECTION_MATRIX, patrz funkcja
gluPerspective
) natomiast położenie, orientację oraz wymiary
wielokątów za pomocą macierzy widoku ( GL_MODELVIEW_MATRIX )
Tak naprawdę obserwator zawsze znajduje się w punkcie ( 0, 0, 0 ) i patrzy w kierunku -Z. Wynika to ze specyfiki operacji rzutowania i konstrukcji
macierzy projekcji. Dlatego aby zmienić pozycję z której widzimy naszą scenę, oraz kierunek w jakim patrzymy będziemy musieli odpowiednio
modyfikować macierz widoku. Z pomocą przyjdzie nam funkcja z biblioteki GLU -
gluLookAt
.