Blog > Komentarze do wpisu
Programowanie Gier - główna pętla gry (ang. game loop)

Poniższy wpis jest tłumaczeniem popularnego postu Koena Wittersa opisującego różne podejścia do pętli gry i ich wady i zalety. Jest to również moje pierwsze podejście do tłumaczenia tekstu jeśli wiec macie jakieś uwagi dajcie znać.

Zapraszam do lektury.


Główna pętla (ang. game loop) stanowi serce każdej gry i żadna, ale to żadna, nie może się bez niej obyć. Niestety w Internecie brakuje dobrych materiałów na ten temat, co może stanowić niemały problem szczególnie, jeśli dopiero zaczynasz przygodę z programowaniem gier. Masz jednak szczęście :), trafiłeś właśnie na post, który poświęca temu zagadnieniu należną mu uwagę. Pracując jako programista gier, widziałem sporo gier mobilnych i zawsze zaskakiwała mnie różnorodność podejścia do tematu. Dziwić może, że tak prosty problem rozwiązywany jest na tak wiele różnych sposobów. Poniżej zaprezentuję najpopularniejsze implementacje i omówię ich wady i zalety, powiem też, która z nich, jest według mnie najlepsza.

 

Pętla gry

 

Każda gra składa się z szeregu wykonywanych po sobie czynności: odczytywania działań użytkownika, aktualizacji stanu gry, obsługi sztucznej inteligencji – AI, odtwarzania muzyki i efektów dźwiękowych oraz ostatecznie wyświetlenia (renderowania) gry. Ta sekwencja czynności obsługiwana jest właśnie przez pętlę gry, która to, jak wspomniałem na wstępie jest sercem każdej gry. W tym wpisie nie będziemy zajmować się jednak żadnym z wymienionych właśnie zagadnień, skupimy się natomiast na samej pętli gry. Aby uprościć nieco jej analizę, ograniczymy jej rolę do dwóch zadań: aktualizacji stanu gry i jej wyświetlania. Poniższy kod przedstawia pętlę gry w jej najprostszej postaci:

 

bool game_is_running = true;

while( game_is_running ) {
	update_game();
	display_game();
}

 

Problemem tej pętli jest to, że nie bierze ona pod uwagę upływającego czasu. Oznacza to, że na wolniejszych komputerach będzie ona działała wolniej, na szybszych z kolei, szybciej. Dawniej, gdy prędkość sprzętu, na którym uruchamiany będzie nasz program łatwiej było przewidzieć, nie stanowiło to większego problemu, dziś ze względu na różnorodność dostępnych platform i konfiguracji, musimy w jakiś sposób dodać do naszej pętli odpowiednią kontrolę. Możemy to zrobić na kilka sposobów, zanim jednak przejdziemy do szczegółów wprowadźmy dwa pojęcia:

 

Liczba klatek na sekundę - FPS (ang. frames per second)

 

Opierając się na powyższej implementacji pętli, wartość FPS reprezentuje ile razy w ciągu sekundy wykonana zostanie metoda display_game().

 

Prędkość Gry (ang. game speed)

 

Określa ile razy w ciągu sekundy uaktualniony zostanie stan gry - wykonana zostanie metoda update_game()

 

 

Liczba wyświetlanych klatek na sekundę (FPS) bazująca na stałej prędkości gry

 

Implementacja

 

const int FRAMES_PER_SECOND = 25;
const int SKIP_TICKS = 1000 / FRAMES_PER_SECOND;

DWORD next_game_tick = GetTickCount();
// GetTickCount() returns the current number of milliseconds
// that have elapsed since the system was started

int sleep_time = 0;

bool game_is_running = true;

while( game_is_running ) {
	update_game();
	display_game();

	next_game_tick += SKIP_TICKS;
	sleep_time = next_game_tick - GetTickCount();
	if( sleep_time >= 0 ) {
		Sleep( sleep_time );
	}
	else {
		// Shit, we are running behind!
	}
}

 

Niewątpliwą zaletą tego rozwiązania jest jego prostota. Wiemy, że metoda update_game() będzie wywoływana dokładnie 25 razy na sekundę. Oznacza to, iż pisanie kodu gry powinno być dość proste. Przykładowo, implementacja powtórki (ang. replay) przy takim podejściu do pętli jest wręcz trywialna. Przy założeniu, że nie wykorzystujemy żadnych wartości losowych podczas gry wystarczy, że będziemy zapamiętywać - logować - zachowania gracza, tak abyśmy je mogli później odtworzyć. Na maszynie, na której pracujesz łatwo będzie dobrać idealną wartość stałej FRAMES_PER_SECOND, ale co stanie się, gdy naszą grę uruchomimy na szybszym lub wolniejszym komputerze?

 

Sprzęt wolniejszy

 

Jeśli komputer, na którym uruchamiamy grę będzie na tyle mocny, aby wyświetlić zdefiniowaną przez nas liczbę klatek na sekundę - FPS, nie będziemy mieli żadnego problemu. Pojawią się one jednak w momencie, gdy sprzęt nasz nie będzie w stanie sprostać zdefiniowanej wartości FPS. Spowoduje to, że gra zacznie działać wolniej. W najgorszym wypadku może dojść do sytuacji, w której w pewnym momentach, nasza gra będzie działała szybciej / normalnie, a w innych znacznie, znacznie wolniej, co sprawi, że pętla nie będzie mogła wykonać wszystkich niezbędnych operacji na czas i gra stanie się kompletnie niegrywalna.

 

Sprzęt szybszy

 

W tym wypadku gra nie będzie miała żadnych problemów z działaniem, podczas wykonywania pętli będziemy jednak tracić wiele cennych cykli zegara (wykorzystywanie jedynie 25-30 FPS w sytuacji, gdy moglibyśmy osiągnąć 300 FPS... to wstyd!). Nasza gra straci również nieco na atrakcyjności, co może być szczególnie widoczne podczas wyświetlania szybko poruszających się obiektów. Z drugiej jednak strony szczególnie, gdy celujemy w urządzenia mobilne, wspomniana wada może okazać się zaletą. Ograniczenie gry tak, aby nie wykorzystywała stale maksymalnej mocy obliczeniowej naszego urządzenia, ograniczy zużycie baterii.

 

Wnioski

 

Wykorzystanie tego podejścia (liczba wyświetlanych klatek na sekundę (FPS) bazująca na stałej prędkości gry) jest nieskomplikowanym rozwiązaniem, które możemy zaimplementować dość szybko, z którym jednak wiąże się kilka wad. Wysoka, pożądana wartość FPS spowoduje, że nasza gra może mieć problemy na wolniejszym, starszym sprzęcie. Niska wartość natomiast sprawi, że może ona sporo stracić wizualnie, gdy uruchamiana będzie na nowszych, szybszych komputerach.

 

 

Prędkość gry bazująca na zmiennej wartości wyświetlanych klatek - FPS

 

Implementacja

 

Kolejnym podejściem do implementacji pętli gry jest oprogramowanie jej w taki sposób, aby prędkość gry była tak wysoka jak to możliwe, w oparciu o wartość FPS. W tym wypadku, aktualizując grę pod uwagę bierzemy różnice czasu pomiędzy aktualną i poprzednio wyświetlaną ramką (klatką).

 

DWORD prev_frame_tick;
DWORD curr_frame_tick = GetTickCount();

bool game_is_running = true;
while( game_is_running ) {
	prev_frame_tick = curr_frame_tick;
	curr_frame_tick = GetTickCount();
	update_game( curr_frame_tick - prev_frame_tick );
	display_game();
}

 

Oczywiście kod gry w tym wypadku będzie nieco bardziej skomplikowany, ponieważ implementując metodę update_game() będziemy musieli brać pod uwagę różnicę czasu pomiędzy kolejnymi ramkami. Implementacja ta jest nieco trudniejsza, ale oczywiście możliwa do wykonania. Na pierwszy rzut oka pętla taka wygląda też na idealne podejście do problemu optymalnej pętli gry i w swojej karierze widziałem wielu utalentowanych programistów, którzy zdecydowali się na takie właśnie podejście. Kilku z nich najprawdopodobniej później żałowało, że nie widziało tego wpisu przed napisaniem swojego kodu :) za chwilę pokaże, że rozwiązanie to może mieć bardzo duże, negatywne implikacje zarówno na wolnym jak i na szybkim (tak to nie pomyłka - na szybkim) sprzęcie.

 

Sprzęt wolniejszy

 

Na sprzęcie wolniejszym, z różnych przyczyn od czasu do czasu mogą wystąpić spowolnienia, co sprawi, że nasza gra stanie się nieco ociężała. Może to wystąpić np. w grach wykorzystujących grafikę 3D, gdy próbujemy wyświetlać zbyt wiele wielokątów jednocześnie. Spadająca liczba wyświetlanych klatek (ang. frame rate) wpłynie na to, jak szybko nasza gra będzie reagować na to, co robi użytkownik (ang. user input), z drugiej strony zmieni to także czas i sposób reakcji gracza na wyświetlaną na ekranie akcje. Opóźnienie odbije się również na aktualizacji stanu gry, będzie on zmieniany w większych krokach / odstępach (ang. time chunks), co jeszcze bardziej obniży czas reakcji naszego gracza, jak i czas reakcji AI. W efekcie sprawi to, że nawet proste manewry mogą stać się bardzo trudne do wykonania, bądź kompletnie się nie udać. Przykładowo przeszkoda, którą ominęlibyśmy z łatwości przy wysokim FPS, może być niemożliwa do pokonania, gdy FPS spadnie do zbyt niskiej wartości. Znacznie poważniejszym problemem z wolnym sprzętem jest to, że w wypadku, gdy wykorzystujemy np. fizykę, nasza symulacja może dosłownie „eksplodować ”!

 

Sprzęt szybszy

 

Z pewnością zastanawiasz się, jakie problemy z powyższa pętlą mogą pojawić się na szybszym sprzęcie. Zanim to wyjaśnimy, przypomnijmy sobie kilka informacji na temat tego, jak operacje matematyczne wykonywane są na komputerach. Pamięć przeznaczona na zmienne typu float i double jest ograniczona, co wiąże się z tym, iż pewne wartości nie mogą być poprawnie reprezentowane. Przykładowo wartość 0.1 nie może być przedstawiona poprawnie binarnie, gdy wykorzystujemy typ double, w związku z czym jest ona zaokrąglana. Spójrz na poniższy przykładowy kod w Pythonie:

 

>>> 0.1
0.10000000000000001

 

Na razie nie wygląda to na nic szczególnie poważnego, ale konsekwencje tej „drobnostki” już mogą takie być. Załóżmy, że pracujemy nad grą wyścigami samochodowymi (ang. car race-game). Załóżmy dalej, że prędkość samochodu to 0.001 jednostek na milisekundę (ang. units/ms). Po 10 sekundach nasze auto przemieści się więc o 10.0 jednostek. Jeśli teraz rozbijemy nasze obliczenia przebytego dystansu tak, jak zrobiłaby to nasza pętla gry otrzymamy poniższą funkcję przyjmującą, jako parametr wartość FPS:

 

>>> def get_distance( fps ):
... 	skip_ticks = 1000 / fps
... 	total_ticks = 0
... 	distance = 0.0
... 	speed_per_tick = 0.001
... 	while total_ticks < 10000:
... 		distance += speed_per_tick * skip_ticks
... 		total_ticks += skip_ticks
... return distance

 

Teraz obliczmy dystans samochodu dla wartości FPS = 40:

 

>>> get_distance( 40 )
10.000000000000075

 

Wow... to nie 10.0, tak jak się spodziewaliśmy! Co się więc stało? Ponieważ podzieliliśmy nasze obliczenia na 400 części, błąd zaokrąglenia znacznie wzrósł. Sprawdźmy teraz rezultat działania naszej funkcji przy wartości FPS = 100:

 

>>> get_distance( 100 )
9.9999999999998312

 

Tym razem błąd jest jeszcze większy. Odpowiadają za to operacje dodawania wykonywane, w tym wypadku, jeszcze częściej. Sprawdźmy ile wynosi różnica dystansu wyliczona w obu przypadkach dla wartości FPS = 40 oraz FPS = 100:

 

>>> get_distance( 40 ) - get_distance( 100 )
2.4336088699783431e-13

 

Mogłoby się wydawać, że wartość jest na tyle mała, iż można ją zignorować, ponieważ nie będzie tak naprawdę zauważalna w grze. Prawdziwy problem zacznie się jednak, gdy ta złą wartość zaczniemy wykorzystywać, jako bazę dla dalszych obliczeń. Może to spowodować, że początkowo mały błąd urośnie do ogromnych rozmiarów, powodując, że nasza gra stanie się niegrywalna przy wysokich wartościach FPS. Jak duże jest prawdopodobieństwo, że z podobną sytuacją spotkamy się w praktyce? Wydaje mi się, że w zupełności wystarczające, aby brać je pod uwagę. Widziałem w przeszłości grę implementującą tego typu pętlę i faktycznie miała ona poważne problemy, gdy wyświetlana liczba klatek na sekundę była wysoka. Programiście w końcu udało się zlokalizować błąd, ale jego poprawienie wiązało się z przepisaniem sporej ilości istniejącego kodu.

 

Wnioski

 

Podejście to, wyglądające na pierwszy rzut oka na bardzo dobre, może spowodować problemy zarówno, gdy nasza gra uruchamiana jest na wolnym jak i na szybkim sprzęcie. Dodatkowo implementacja kodu aktualizującego stan gry jest w tym wypadku trudniejsza, w porównaniu z implementacją wykorzystującą pętlę bazującą na stałej wartości FPS. Nie warto więc chyba tego podejścia wykorzystywać.

 

 

Stała prędkość gry z ograniczoną maksymalna wartością FPS

 

Pierwsze z prezentowanych rozwiązań (liczba wyświetlanych klatek na sekundę (FPS) bazująca na stałej prędkości gry) ma problemy z działaniem na wolniejszych maszynach. W tym wypadku zarówno prędkość gry jak i częstotliwość wyświetlania klatek na ekranie (ang. framerate) spadnie. Jednym z możliwych rozwiązań jest aktualizacja stanu gry z częstotliwością odpowiadającą częstotliwości odświeżania, przy jednoczesnym zmniejszeniu częstotliwości renderowania obrazu. Możemy to osiągnąć modyfikując nasza pętlę gry tak, jak to pokazałem poniżej.

 

Implementacja

 

const int TICKS_PER_SECOND = 50;
const int SKIP_TICKS = 1000 / TICKS_PER_SECOND;
const int MAX_FRAMESKIP = 10;

DWORD next_game_tick = GetTickCount();
int loops;

bool game_is_running = true;
while( game_is_running ) {
	loops = 0;
	while( GetTickCount() > next_game_tick && loops < MAX_FRAMESKIP) {
		update_game();
		next_game_tick += SKIP_TICKS;
		loops++;
	}
	display_game();
}

 

W tym wypadku stan naszej gry aktualizowany będzie 50 razy na sekundę i renderowany tak szybko, jak to tylko możliwe. Warto zauważyć, że gdy rysowanie wykonywane jest częściej niż 50 razy na sekundę, kolejne rysowane klatki będą się powtarzać, wiec tak naprawdę ramki różniące się od siebie, będą wyświetlane na stałym, maksymalnym poziomie 50 ramek / klatek na sekundę. W momencie, gdy nasza gra uruchamiana będzie na wolniejszych komputerach, częstotliwość renderowania obrazu może spaść do minimalnego dozwolonego poziomu MAX_FRAMESKIP. W praktyce oznacza to, że w momencie, gdy wartość FPS spadnie poniżej 5 (FRAMES_PER_SECOND / MAX_FRAMESKIP), gra zwolni.

 

Sprzęt wolniejszy

 

Na wolnych komputerach liczba wyświetlanych klatek na sekundę spadnie, ale sama gra powinna wciąż działać poprawnie / z normalna prędkością. Jeśli maszyna okaże się faktycznie zbyt słaba, gra zacznie działać wolniej i wyświetlany obraz nie będzie płynny.

 

Sprzęt szybszy

 

W tym wypadku gra nie powinna mieć żadnych problemów, ale podobnie jak w opcji pierwszej tak i tu tracić będziemy cenne cykle zegara, które mógłby by być wykorzystane przykładowo, do generowania płynniejszego obrazu (wyższa wartość frame rate). Odnalezienie właściwego balansu pomiędzy częstotliwością aktualizacji gry i możliwością jej poprawnego działania na wolniejszych komputerach jest w tym wypadku kluczowa.

 

Wnioski

 

Uzyskanie stałej prędkości gry przy jednoczesnym ograniczeniu maksymalnej wartości FPS jest rozwiązaniem nieskomplikowanym w implementacji i sprawia, że kod naszej gry jest stosunkowo prosty. Oczywiście podejście takie ma ciągle swoje wady. Ustawienie zbyt wysokiej wartości maksymalnej FPS może doprowadzić do problemów na słabszym sprzęcie, nie tak poważnych jednak jak te, które omówiliśmy w pierwszym przykładzie. Wykorzystanie z kolei wartości zbyt niskiej sprawi, że gra nasza nie będzie tak atrakcyjna, jak mogłaby być gdyby wykorzystywała w pełni możliwości szybkiego komputera.

 

 

Stała prędkość gry niezależna od zmiennej wartości FPS

 

Implementacja

 

Czy istnieje zatem, możliwość dalszego usprawnienia powyższego rozwiązania, tak aby nasza gra działała szybciej na wolnym sprzęcie, a jednocześnie lepiej wykorzystywała możliwości szybszych komputerów i wyglądała na nich atrakcyjniej? Na szczęście dla nas jest to możliwe. Stan gry nie musi być przecież aktualizowany 60 razy na sekundę. W większości zastosowań wystarczające powinno być odczytywanie akcji użytkownika, zamiany AI i aktualizacja stanu gry 25 razy na sekundę. Spróbujmy więc zmodyfikować naszą pętlę tak, aby wywoływała ona metodę update_game() dokładnie 25 razy na sekundę. Z drugiej strony, odświeżanie ekranu powinno odbywać się tak często, jak jest to tylko możliwe na sprzęcie, którym dysponujemy. Oczywiście niska prędkość odświeżania (slow frame rate) nie powinna odbić się negatywnie na aktualizacji stanu gry. Poniższy kod pokazuje, jak możemy to wszystko osiągnąć.

 

const int TICKS_PER_SECOND = 25;
const int SKIP_TICKS = 1000 / TICKS_PER_SECOND;
const int MAX_FRAMESKIP = 5;

DWORD next_game_tick = GetTickCount();
int loops;
float interpolation;

bool game_is_running = true;
while( game_is_running ) {
	loops = 0;
	while( GetTickCount() > next_game_tick && loops < MAX_FRAMESKIP) {
		update_game();
		next_game_tick += SKIP_TICKS;
		loops++;
	}
	interpolation = float( GetTickCount() + SKIP_TICKS - next_game_tick )
					/ float( SKIP_TICKS );
	display_game( interpolation );
}

 

Wykorzystując tą implementację, metoda update_game() pozostaje prosta. Niestety komplikuje się za to metoda display_game(). Konieczna jest bowiem implementacja funkcji przewidującej (ang. prediction function) wykorzystującej jako argument interpolację. Za chwile wyjaśnimy sobie jak oba mechanizmy działają, ale najpierw pokażmy, dlaczego są one potrzebne.

 

Dlaczego interpolacja?

 

Stan gry aktualizowany jest 25 razy na sekundę, tak wiec, jeśli nie skorzystamy z interpolacji, przy renderowaniu poszczególne klatki wyświetlane będą z ta sama prędkością. Warto przy tym pamiętać, że 25 klatek na sekundę to nic złego, filmy przykładowo, wyświetlane są z prędkością 24 klatek na sekundę. Dlatego też 25 FPS powinno wystarczyć, aby nasza gra była wizualnie atrakcyjna, oczywiście szybko poruszające się obiekty zyskają na atrakcyjności przy wyższych wartościach FPS. Co możemy zrobić, to sprawić, że ich ruch będzie płynniejszy pomiędzy poszczególnymi klatkami. To właśnie do tego przydadzą się nam: funkcja przewidująca i interpolacja.

 

Interpolacja i przewidywanie

 

Tak jak już wspomniałem, aktualizacja stanu gry odbywa się ze stałą prędkością (określoną ilość razy na sekundę) dlatego też, kiedy rysujemy/renderujemy scenę możliwe jest, iż znajdzie się ona pomiędzy dwoma kolejnymi aktualizacjami stanu (org. ang. gametick). Wyobraźmy sobie dalej, że właśnie po raz 10-ty zaktualizowaliśmy stan gry i zaczynamy rysować/renderować naszą scenę. Rysowanie odbędzie się więc pomiędzy 10-ą a 11-ą aktualizacją. Możliwie więc jest, że rysowanie powinno się odbyć na pozycji 10.3. Wartość interpolacji w tym wypadku to właśnie 0.3. Rozważmy następującą sytuację: posiadamy samochód, który porusza się wraz z każdą aktualizacją stanu następująco:

 

position = position + speed;

 

Jeśli podczas 10-go taktu pozycja auta to 500, a prędkość równa jest 100, wtedy przy 11 aktualizacji pozycja powinna wynieść 600. Powstaje pytanie, gdzie umieścić samochód podczas wyświetlania? Możemy w tym celu wykorzystać pozycję wyliczoną podczas ostatniej aktualizacji stanu gry (w tym wypadku jest to 500). Lepszym podejściem jest jednak określenie w przybliżeniu, gdzie auto powinno się znaleźć dla wartości 10.3. Odpowiednie obliczenie przedstawiam poniżej:

 

view_position = position + (speed * interpolation)

 

W naszym przykładzie samochód rysowany byłby na pozycji 530. Zmienna ‘interpolation’ reprezentuje wartość pomiędzy poprzednią, a następną aktualizacją stanu gry (poprzednia = 0.0, następna = 1.0). Teraz musimy opracować funkcję przewidującą, obliczającą gdzie kamera/auto powinno się znaleźć w momencie, gdy scena jest rysowana. Funkcja ta może być oparta na prędkości obiektu, sterowaniu, bądź prędkości obrotowej. Nie musi być skomplikowana, ponieważ wykorzystujemy ją jedynie do „wygładzenia” ruchu pomiędzy poszczególnymi klatkami. Przy takim podejściu jest oczywiście możliwe, że nasz obiekt zostanie narysowany na innym na moment przed wykryciem kolizji. Ponieważ jednak stan gry aktualizowany jest 25 razy na sekundę, jeśli się stanie coś podobnego, to błąd będzie widoczny przez ułamek sekundy i powinien być tak naprawdę niezauważalny dla ludzkiego oka.

 

Sprzęt wolniejszy

 

W większości wypadków wykonanie metody update_game() zajmie znacznie mniej czasu niż wykonanie metody display_game(). W praktyce, możemy z dużym prawdopodobieństwem założyć, że nawet na wolnych komputerach funkcja update_game() wykonywana będzie 25 razy na sekundę, co pozwoli na bezproblemowa interpretację akcji użytkownika i aktualizację stanu gry nawet wtedy, gdy scena gry odświeżana będzie z prędkością przykładowo 15 klatek na sekundę.

 

Sprzęt szybszy

 

Na szybkich maszynach prędkość naszej gry będzie stała i wyniesie 25 aktualizacji na sekundę, oczywiście samo odświeżanie ekranu będzie odbywało się znacznie częściej. Interpolacja sprawi, że gracz będzie miał wrażenie, iż wszystko działa płynniej, ze znacznie większą prędkością, dzięki czemu gra stanie się atrakcyjniejsza wizualnie. Jest to swojego rodzaju oszukiwanie, jeśli chodzi o liczbę wyświetlanych klatek gry – FPS, ponieważ tak naprawdę nie aktualizujemy stanu gry co klatkę, a jedynie odświeżamy jego przybliżoną, wizualną reprezentację. Dzięki temu sceny naszej gry rysowane/renderowane są z wyższą częstotliwością, niż w drugiej z opisanych wcześniej metod.

 

Wnioski

 

Pętla gry, w której aktualizacja stanu gry jest niezależna od liczby wyświetlanych na sekundę klatek, wydaje się być najlepszą z przedstawionych implementacji. W tym wypadku jednak musimy pamiętać o opracowaniu funkcji przewidującej w metodzie display_game(), co nie jest trudne do osiągniecia.

 

Wnioski końcowe

 

Pętla gry to znacznie więcej niż mogłoby się na pierwszy rzut oka wydawać. Przedstawiliśmy tu cztery możliwe implementacje, z których jednej powinieneś zdecydowanie unikać. Jest to ta, gdzie zmienna wartość FPS odpowiada za prędkość gry. Stała prędkość odświeżania (frame rate) może być przydatnym i prostym rozwiązaniem w przypadku gier przeznaczonych na urządzenia mobilne. Jeśli jednak chcesz, aby możliwości sprzętu, na którym uruchomiona jest twoja gra wykorzystane były optymalnie, najlepszym podejściem jest pętla gry, w której wartości odświeżania ekranu (liczba wyświetlanych na sekundę klatek - FPS) jest niezależna od prędkości gry (aktualizacji jej stanu) i wykorzystuje funkcję przewidującą i interpolację. Jeśli nie chcesz spędzać czasu programując funkcję przewidującą (prediction function) możesz z powodzeniem wykorzystać rozwiązanie oparte na maksymalnej wartości odświeżania (maximum frame rate). W tym wypadku jednak dobranie odpowiedniego maksimum dla maszyn wolnych i szybkich może okazać się niełatwe.

 

I to już wszystko. Teraz możesz już zabrać się za pisanie fantastycznej gry o stworzeniu, której zawsze marzyłeś!

 

Koen Witters

Translated by Damian/MORT

środa, 23 kwietnia 2014, m0rt1m3r

Related Posts Plugin for WordPress, Blogger...

Polecane wpisy

Komentarze
Gość: Wx, *.free.aero2.net.pl
2014/06/19 14:41:46
Świetnie, trafiłem tu ze strony z oryginalnym artykułem. Na pewno mi się przyda, bo właśnie zaczynam tworzyć proste gry w 2D. Pozdrawiam




PowerBuilder Tetris
D - Tetris



Programowanie iOS

C# ToolBox

SQL / TSQL / PLSQL ToolBox

Linux / Unix ToolBox





Zaprzyjaznione Strony

Sprite Bandits

Cake Time