2. Wstęp
Elementy składowe
Całość
Wszystko to iluzja
Cel: Jak oszukać gracza?
Jak uzyskać efekt, który będzie zgodny z wyobrażeniami gracza
najmniejszym nakładem obliczeń i pamięci.
Czyli...
Wszystkie chwyty dozwolone – liczy się tylko efekt.
Filip Sobalski
Programowanie gier 2D
3. Wstęp
Elementy składowe
Całość
Duszki i inne potworności
Sprite
W dosłownym tłumaczeniu znaczy „duszek”. Podstawowy element
graficzny, czyli - w skrócie - obrazek, który zmienia położenie. W nowych
implementacjach to będzie obiekt zawierający także wszystkie informacje
o postaci, potworku czy innym elemencie gry.
Sprite Engine
Odpowiada za rysowanie Sprite’ów, zwykle także za ich animację,
detekcję kolizji, etc... Kiedyś implementowany sprzętowo, teraz
najczęściej przez oprogramowanie.
Filip Sobalski
Programowanie gier 2D
5. Wstęp
Elementy składowe
Całość
Grafika
Ruch
Detekcja kolizji
Bit blit
Bit-blitting
Blokowy (vide: scanlines) transfer danych z obrazka źródłowego do
docelowego (zwykle bufor graficzny) często z użyciem operatora
rastrowego (ROP). ROP to nic innego jak operator bitowy - AND, OR,
XOR.
Zwykle użyjemy do tego biblioteki korzystającej wyłącznie z CPU (np.
SDL) lub zlecimy rysowanie wyspecjalizowanemu sprzętowi (np.
OpenGL).
Filip Sobalski
Programowanie gier 2D
6. Wstęp
Elementy składowe
Całość
Grafika
Ruch
Detekcja kolizji
Przezroczystość
maskowanie
Najpierw rysujemy 1-bitową maskę z operacją AND a potem w tym
samym miejscu rysujemy właściwy obrazek z operacją OR. Dany
piksel może być przezroczysty lub nie.
colorkey
Używane przy grafice z paletą. Wybieramy indeks palety, który ma
być przezroczysty i każdy piksel źródłowy o tej wartości jest
ignorowany przy blitting’u.
alpha blending
Dziś to najczęściej stosowane rozwiązanie. ROP zastępujemy
operatorem matematycznym mieszającym kolory z uwzględnieniem
dodatkowego kanału alpha.
Filip Sobalski
Programowanie gier 2D
9. Wstęp
Elementy składowe
Całość
Grafika
Ruch
Detekcja kolizji
Przezroczystość - alpha blending
Mieszamy składowe piksli w proporcjach określonych przez kanał alfa.
s - piksel źródłowy
d - piksel docelowy
k ∈ {R, G , B}
dk , sk , sA ∈ [0, 1]
blended componentk = dk × (1 − sA ) + sk × sA
Filip Sobalski
Programowanie gier 2D
10. Wstęp
Elementy składowe
Całość
Grafika
Ruch
Detekcja kolizji
Kolejność rysowania - warstwy
Stała ilość warstw - dla każdej warstwy osobna lista sprite’ów
Dowolna ilość warstw - nr warstwy przypisany do sprite’a
Sortujemy co klatkę. [stabilny algorytm sortowania]
Każdego nowego sprite’a wstawiamy w odpowiednie miejsce. [a’la
insertion sort]
Filip Sobalski
Programowanie gier 2D
11. Wstęp
Elementy składowe
Całość
Grafika
Ruch
Detekcja kolizji
Viewport clipping
Viewport
Zwykle zdefiniowany przez położenie bezwzględne (w układzie wsp.
świata) i wymiary; określa, który fragment sceny widać na ekranie.
Rysujemy tylko te sprite’y, których aktualne obrazki nakładają się na nasz
viewport. Wygodnie tutaj zastosować bounding box ale o tym później.
Położenie pod którym narysujemy sprite’a to prostu różnica wektorowa
jego położenia i położenia viewportu. Warto jednak wyróżnić sprite’y
których położenie będziemy definiować względem viewportu - użyteczne
do wyświetlania punktacji, ilości naboi etc.
Uwaga
W każdej klatce rysujemy wszystko od nowa. Można rysować tylko to
co zmieniło się od ostatniej klatki, jednak dziś nie ma sensu stosować tej
techniki.
Filip Sobalski
Programowanie gier 2D
12. Wstęp
Elementy składowe
Całość
Grafika
Ruch
Detekcja kolizji
Podwójne buforowanie
Podwójne buforowanie razem z synchronizacją pionową (v-sync)
rozwiązuje problem tearing’u.
1
Narysuj w buforze off-screen scenę.
2
Zamień miejscami wartości wskaźników pokazujących na bufor
off-screen i display.
3
Narysuj na ekranie bufor display w momencie kiedy monitor skończył
rysować poprzednią klatkę ale nie zaczął następnej.
W praktyce wszystkim zajmie się biblioteka. Nas interesuje bufor
off-screen oraz musimy pamiętać o wywołaniu funkcji typu SwapBuffers
po narysowaniu sceny.
Filip Sobalski
Programowanie gier 2D
14. Wstęp
Elementy składowe
Całość
Grafika
Ruch
Detekcja kolizji
Dyskretyzacja ruchu
Jak?
Dlaczego nie ma już w obudowach przycisku turbo?
Lag
Czas w sekundach mierzony od ostatniej klatki.
Skalujemy wszystko przez lag.
Caveat
Nawet jeśli nie rysujemy z podpikslową precyzją wszystko trzymamy we
float’ach! Obcinamy wtedy kiedy potrzebujemy int’a.
Filip Sobalski
Programowanie gier 2D
15. Wstęp
Elementy składowe
Całość
Grafika
Ruch
Detekcja kolizji
Animacja
frame ← frame + animation speed × lag
Nie obcinamy części ułamkowych!
Zawijamy: (lub odbijamy w przypadku oscylacji)
i f ( f r a m e >= t o t a l f r a m e s )
f r a m e −= t o t a l f r a m e s ;
Filip Sobalski
Programowanie gier 2D
16. Wstęp
Elementy składowe
Całość
Grafika
Ruch
Detekcja kolizji
Animacja - wyrównywanie klatek
Sposób nr 1
Każda klatka (obrazek) animacji ma tę samą wielkość. Położenie sprite’a
określamy względem stałego punktu obrazka (np. lewego-górnego rogu [0, 0]).
Sposób nr 2
Każda klatka animacji ma określony punkt względem którego będziemy
ją pozycjonować na ekranie - nazwijmy go grab point. Znajdujemy punkt
odniesienia na naszym animowanym obiekcie i jego pozycja w każdej
klatce będzie stanowiła grab point.
Sposób nr 2 jest rozszerzeniem sposobu nr 1.
Filip Sobalski
Programowanie gier 2D
17. Wstęp
Elementy składowe
Całość
Grafika
Ruch
Detekcja kolizji
Grab point
Ten punkt będzie wydawał się stać w miejscu podczas animacji.
Rysunek: przykładowe umieszczenie gp
Gdzie rysujemy lewy-górny róg sprite’a?
sprite screen pos ← sprite pos − frame gp
Filip Sobalski
Programowanie gier 2D
18. Wstęp
Elementy składowe
Całość
Grafika
Ruch
Detekcja kolizji
Przemieszczenie
Prędkość to nie problem...
sprite pos ← sprite pos + sprite velocity × lag
Co z przyśpieszeniem?
sprite velocity ← sprite velocity + sprite accelleration × lag
Przykład - grawitacja
sprite velocity ← sprite velocity + (0, gravity ) × lag
Czy widać tutaj jakiś problem?
Filip Sobalski
Programowanie gier 2D
26. Wstęp
Elementy składowe
Całość
Grafika
Ruch
Detekcja kolizji
Ograniczenie wielokątami
Co z wklęsłymi?
1
Podziel na trójkąty (tesselacja)
2
Zachłannie łącz z powrotem w wypukłe wielokąty.
3
Dla każdego wielokąta z otrzymanego zbioru wylicz bounding box i
dodaj kolejną fazę...
Filip Sobalski
Programowanie gier 2D
27. Wstęp
Elementy składowe
Całość
Grafika
Ruch
Detekcja kolizji
Co robimy kiedy nastąpi kolizja?
Przewidujemy kolizję z wyprzedzeniem ;)
Cofamy do poprzedniej pozycji - kiepski efekt przy dużej prędkości
Cofamy się małymi kroczkami wzdłuż wektora przemieszczenia aż
kolizja przestanie zachodzić
Jeśli metoda detekcji pozwala to liczymy wektor „zagłębienia” i
odejmujego go od pozycji. (Wielokąty FTW!)
Filip Sobalski
Programowanie gier 2D
28. Wstęp
Elementy składowe
Całość
Grafika
Ruch
Detekcja kolizji
Potencjalne problemy
Niski framerate i/lub duża prędkość - jeden sprite przeskoczy drugi.
Nie ma dobrego rozwiązania. Można ruch pomiędzy klatkami
sprowadzić do postaci ciągłej.
Zakleszczenie:
1
2
3
Zmiana klatki animacji na „większą” w trakcie kolizji/zaraz po
cofnięciu. (Częste przy teście per-pixel.)
Błędy zaokrągleń.
Dwa sprite’y poruszające się na raz.
Specjalne przypadki - np. gracz spada na płaską powierzchnię czy
chodzi po niej, ślizganie się, etc.
Niska wydajność.
Filip Sobalski
Programowanie gier 2D
29. Wstęp
Elementy składowe
Całość
Grafika
Ruch
Detekcja kolizji
Wydajność, wydajność, wydajność...
Naiwne podejście - sprawdzić każdy obiekt z każdym - nieakceptowalnie
powolne przy dużej liczbie obiektów.
Propozycje:
Wykluczyć z testów obiekty dla których detekcja kolizji nam jest nie
potrzebna.
Podzielić obiekty na klasy i wykluczyć z testów pary klas, które na
pewno nie będą kolidować lub włączyć jedynie te pary, których
kolizja nas interesuje.
Indeksowanie przestrzenne - np. quadtrees (octrees w 3D), siatka
obszarów.
Przy skomplikowanych testach dodać więcej faz (ale nie za dużo!).
Liczyć przekształcenia tylko w ostatniej fazie.
Zrezygnować z powyższych sposobów detekcji kolizji jeśli specyfika
gry na to pozwala. ;) (KISS)
Filip Sobalski
Programowanie gier 2D
31. Wstęp
Elementy składowe
Całość
Ograniczenie ilości FPS
Prosty sposób - sleep/delay w głównej pętli - nie skaluje się.
Uniwersalny sposób - sleep/delay adaptujący się do czasu wykonania
pętli
I zapewne wiele innych...
Filip Sobalski
Programowanie gier 2D
32. Wstęp
Elementy składowe
Całość
Adaptive Frame Limiter
v o i d A d a p t i v e F r a m e L i m i t e r : : onFrame ( )
{
f l o a t d i f f = ( 1 . 0 f / m maxFps ) − m l a g ;
float delay = d i f f + 0.9 f ∗ m lastDelay ;
m lastDelay = delay ;
i f ( d e l a y <= 0 . 0 f ) r e t u r n ;
m pSystem−>d e l a y ( ( d e l a y ∗ 1 0 0 0 . 0 f ) ) ;
}
Filip Sobalski
Programowanie gier 2D
33. Wstęp
Elementy składowe
Całość
Główna pętla
1
Wyczyść bufor graficzny
2
Uaktualnij stan wejścia
3
Uaktualnij timery synchroniczne
4
Wylicz nową pozycję sprite’ów + logika (+zaznacz sprite’y do
usunięcia i zapisz te do dodania)
5
Sprawdź kolizje (+zaznacz sprite’y do usunięcia i zapisz te do
dodania)
6
Usuń zaznaczone sprite’y
7
Dodaj sprite’y w kolejce
8
Narysuj sprite’y
9
Podmień bufory (swap buffers)
10
Poczekaj chwilę aby ograniczyć FPS
Filip Sobalski
Programowanie gier 2D