Petle Gry - rozwiazania dla gier realtime
Transkrypt
Petle Gry - rozwiazania dla gier realtime
Pętle Gry rozwiązania dla gier realtime Jan Darowski 14 maja 2014 Jan Darowski Pętle Gry Problemy 1 Utrzymanie płynności gry 2 Różny charakter obiektów gry (budynek, gracz, system osiągnięć) 3 Rózne akcje wykonywane na poszczególnych obiektach (animowanie, wybieranie przeciwnika) 4 Synchronizacja ze światem zewnętrznym (użytkownik, inne instancje gry) 5 Sprawiedliwe aktualizowanie wszystkich obiektów (niezależnie od obciążenia) 6 Utrzymanie kolejności aktualizowania obiektów Jan Darowski Pętle Gry Najprostsze rozwiązania while (1) { foreach ( GameObject & go , gameObjects ) go . update () ; } Zależne od sprzętu Zależne od obciążenia Brak podziału wykonywanych akcji według częstotliwości Jan Darowski Pętle Gry Najprostsze rozwiązania while (1) { wait ( lastUpdate + INTERVAL - time () ) ; lastUpdate = time () ; foreach ( GameObject & go , gameObjects ) go . update () ; } Zależne od obciążenia Brak podziału wykonywanych akcji według częstotliwości Jan Darowski Pętle Gry Zmienny czas wywołania while (1) { foreach ( GameObject & go , gameObjects ) go . update ( time () - lastUpdate ) ; lastUpdate = time () ; } Niezależne od obciążenia lub sprzętu 100% wykorzystanie procesora Konieczność implementacji obsługi czasu wewnątrz update() Dobre przy liczeniu fizyki i animacji - brak lagów Jan Darowski Pętle Gry Wiele niezależnych pętli gry while (1) { wait ( lastUpdate1 + INTERVAL - time () ) ; lastUpdate1 = time () ; foreach ( GameObject & go , gameObjects ) go . update11 () ; foreach ( GameObject & go , gameObjects ) go . update12 () ; if ( lastUpdate2 + INTERVAL2 > time () ) { lastUpdate2 = time () ; foreach ( GameObject & go , gameObjects ) go . update2 () ; } } Ponownie - zależność od obciążenia Możliwość rzadszego wykonywania ciężkich akcji Jan Darowski Pętle Gry Rozwiązanie Ale w końcu mamy Qt i sensowne timery. Jan Darowski Pętle Gry Rozwiązanie Ale w końcu mamy Qt i sensowne timery. Niezależność od sprzętu Pasuje do kodu sterowanego zdarzeniami Częściowa niezależność od obciążenia Możliwość updateowania różnych rzeczy co różny czas Dodatkowe nakłady na timery (wywoływane systemowo) Możliwość wykonywania ciężkich działań gdy procesor jest wolny (QtConcurrent) Brak kontroli nad kolejnością wykonywania działań Jan Darowski Pętle Gry Predykcja zachowań Co zrobić kiedy obiektów gry jest zbyt dużo i nie nadążamy z ich aktualizacją? Udawać że aktualizujemy Koniecznie aktualizować grafikę Aktualizować fizykę (a przynajmniej część, np bez detekcji kolizji) Pomijać część informacji o zmianach, być może nie są już potrzebne (małe bufory na zdarzenia jednego typu) Jan Darowski Pętle Gry Update() Do tej pory skupialiśmy się na samej strukturze pętli gry. Trzeba jednak zintegrować ją z odpowiednią implementacją obiektów. Możliwe podejścia: Jan Darowski Pętle Gry Update() Do tej pory skupialiśmy się na samej strukturze pętli gry. Trzeba jednak zintegrować ją z odpowiednią implementacją obiektów. Możliwe podejścia: Obiekty zawierają tylko dane, aktualizuje je główna funkcja w pętli gry. Każda klasa opisująca jakiś obiekt zawiera osobną implementację update() Dziedziczenie funkcji update(). Być może wielodziedziczenie Strategie (Brainzzzz) Strategia na dekoratorach Animatory Jan Darowski Pętle Gry Dziedziczenie update() 1 Problemy z wielodziedziczeniem 2 Możliwe nieintuicyjne dziedziczenia (budynek - statek matka) 3 Spora nadmiarowość kodu - niektóre akcje mogą mieć duże części wspólne 4 Długie ścieżki dziedziczenia - problemy w utrzymaniu kodu 5 Mnożenie liczby klas posiadających te same atrybuty przez niewielkie różnice w zachowaniu (żołnierz, żołnierz z super umiejętnościami) Jan Darowski Pętle Gry Strategie - Brainzzzz Każdy obiekt posiada wskaźnik na swój mózg - obiekt opisujący działanie głównego obiektu 1 Możliwość oddzielenia opisu atrybutów od logiki 2 Znowu - dużo klas i wielodziedziczenie 3 Redundancja kodu 4 Możliwość zmiany zachowań obiektu w trakcie gry (uśpiony guardian, patrolujący, ścigający) Jan Darowski Pętle Gry Dekoratory Sposób na zastąpienie dziedziczenia, można użyć zarówno na samym obiekcie jak i na jego mózgu. Jan Darowski Pętle Gry Wady i zalety 1 Możliwość tworzenia dowolnych kombinacji obiekt zachowanie 2 Brak dziedziczenia 3 Mała redundancja kodu 4 Brak sposobu na łatwe podzielenie akcji według okresu wykonywania 5 Możliwość pomyłki przy kolejności inicjowania dekoratora (złe zagnieżdżenie) Jan Darowski Pętle Gry Animatory - pochodzenie Niektóre silniki, takie jak Irrlicht wykorzystują Animatory, rozwiązanie pozwalające szybko dodawać logikę typowych zachowań do dowolnego obiektu gry. Jan Darowski Pętle Gry Animatory - pochodzenie Niektóre silniki, takie jak Irrlicht wykorzystują Animatory, rozwiązanie pozwalające szybko dodawać logikę typowych zachowań do dowolnego obiektu gry. 1 Animowanie kolejnych klatek 2 Zatrzymywanie się przy zderzeniu 3 „Wchodzenie” na niewielkie podwyższenia 4 Grawitacja Dodawanie animatora jest realizowane przez wywołanie metody silnika samej gry, na węźle danego obiektu. Jan Darowski Pętle Gry Animatory - podstawowe założenia 1 Obiekty gry jedynie przechowują dane - nie zawierają logiki zachowań 2 Animatory są częścią silnika, nie obiektów 3 Każdy animator może działać na potencjalnie dowolnym obiekcie gry 4 Każdy obiekt może być obsługiwany przez wiele animatorów 5 Animatory mogą mieć różny czas aktualizacji 6 Animatory mają łatwy dostęp do danych zawartych w silniku (Gameplay) 7 Każdy animator dba o kolejność aktualizowanych przez siebie obiektów Jan Darowski Pętle Gry Animatory - architektura Każdy animator posiada częstotliwość aktualizacji, priorytet, listę aktualizowanych obiektów i wskaźnik do Gameplay Gameplay inicjalizuje wszystkie animatory Dla każdego występującego czasu aktualizacji tworzy osobną warstwę Warstwa składa się z posortowanych po priorytecie animatorów i timera o odpowiednim okresie aktualizacji Timer co określony czas wywołuje update() wszystkich animatorów należących do danej warstwy Update() animatora polega na wykonaniu tej samej akcji na wszystkich obiektach z listy, zawsze w tej samej kolejności Animatory mogą korzystać ze swoich wyników zapisywanych w mapie QString - QVariant Jan Darowski Pętle Gry Animatory - zady i walety Relatywnie mało timerów Dowolne mieszanie obiektów z zachowaniami Dopuszczalne różne czasy aktualizacji Stała kolejność aktualizowania obiektów Bezpieczeństwo wywołania animatorów gwarantowane globalnie, nie osobno dla każdego obiektu Krótki kod dodający nowy obiekt o typowym zachowaniu Brak pewności czy obiekt posiada parametry wymagane przez dany animator Niewygodne wczytywanie obiektów - konieczność niezależnego pamiętania animatorów dla każdego obiektu Łatwość debugowania - można dowolny, mały fragment działania obiektów testować niezależnie Brak redundancji kodu Jan Darowski Pętle Gry Skryptowanie - Po co? Czemu dobrze jest skryptować fragmenty logiki gry? Brak konieczności rekompilacji przy niewielkich zmianach Możliwość szybszej pracy przy szlifowaniu zachowań i sprawdzaniu różnych wariantów Mniejsze wymagania dot. osoby pracującej nad logiką Ułatwia pisania kodu łatwego w dostosowaniu do innych projektów Jan Darowski Pętle Gry Skryptowanie -Jak to robić poprawnie Skryptować jedynie najwyższą warstwę logiki Skrypty powinny być możliwie proste Skomplikowane fragmenty trzymać w kodzie - są od razu sprawdzane przez kompilator, są szybsze Skryptowane powinny być fragmenty, które wynikają z mechaniki opisywalnej graczowi Przekazywać minimum danych, z maksymalną wartością Jan Darowski Pętle Gry Skryptowanie -Bridge Pattern Pozwala na niezależne łączenie rodzajów obiektów ze sposobami implementacji. Przykład: Animatory, Warunki zwycięstwa, Menu HUD Przykład Inkscape: Narzędzia rysowania, konwertery, filtry Jan Darowski Pętle Gry Skryptowanie w Qt QScriptEngine engine ; QObject * someObject = new MyObject ; QScriptValue objectValue = engine . newQObject ( someObject ) ; engine . globalObject () . setProperty ( " myObject " , objectValue ) ; engine . globalObject () . setProperty ( " foo " , 123) ; qDebug () << engine . evaluate ( " foo * 2 + myObject . scale " ) . toNumber () ; Jan Darowski Pętle Gry Skryptowanie w Qt QScriptEngine engine ; QObject * someObject = new MyObject ; QScriptValue objectValue = engine . newQObject ( someObject ) ; engine . globalObject () . setProperty ( " myObject " , objectValue ) ; engine . globalObject () . setProperty ( " foo " , 123) ; qDebug () << engine . evaluate ( " foo * 2 + myObject . scale " ) . toNumber () ; Connect: QScriptEngine eng ; QLineEdit * edit = new QLineEdit (...) ; QScriptValue handler = eng . evaluate ( " ( function ( text ) { print ( ’ text was changed to ’, text ) ; }) " ) ; q ScriptConnect ( edit , SIGNAL ( textChanged ( const QString &) ) , QScriptValue () , handler ) ; Jan Darowski Pętle Gry Dziękuję za uwagę. Jan Darowski Pętle Gry