Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00226 009234 10485081 na godz. na dobę w sumie
C++ dla programistów gier. Wydanie II - książka
C++ dla programistów gier. Wydanie II - książka
Autor: Liczba stron: 480
Wydawca: Helion Język publikacji: polski
ISBN: 978-83-246-0967-3 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> programowanie >> c++ - programowanie
Porównaj ceny (książka, ebook, audiobook).

Poznaj nowoczesne metody tworzenia gier komputerowych

Rynek gier komputerowych jest niezwykle wymagający. Gracze stawiają tego rodzaju programom coraz wyższe wymagania, co z kolei przekłada się na konieczność stosowania coraz doskonalszych technik ich tworzenia. Będąc programistą gier komputerowych, na pewno doskonale zdajesz sobie z tego sprawę. Jeśli chcesz, aby kolejna stworzona przez Ciebie gra spełniała oczekiwania nawet najbardziej wybrednych graczy, wykorzystaj język C++. Jego możliwości sprawiają, że jest doskonałym narzędziem do tworzenia gier.

'C++ dla programistów gier. Wydanie II' to przewodnik po języku C++ opisujący go z punktu widzenia programowania specyficznych aplikacji, jakimi są gry. Książka przedstawia najefektywniejsze techniki C++ i metody rozwiązywania problemów, przed którymi stają programiści gier. Czytając ją, dowiesz się, jak zarządzać pamięcią i stosować wzorce projektowe oraz STL. Poznasz możliwości wykorzystania języków skryptowych do usprawnienia procesu tworzenia gry komputerowej. Każde z rozwiązań opatrzone jest przykładem, dzięki czemu łatwo będzie Ci zaimplementować je w swoich pracach.

Dołącz do elitarnej grupy programistów gier komputerowych.

Znajdź podobne książki Ostatnio czytane w tej kategorii

Darmowy fragment publikacji:

C++ dla programistów gier. Wydanie II Autor: Michael J. Dickheiser T³umaczenie: Przemys³aw Szeremiota ISBN: 978-83-246-0967-3 Tytu³ orygina³u: C++ For Game Programmers (Second edition) Format: B5, stron: 480 Poznaj nowoczesne metody tworzenia gier komputerowych (cid:129) Wykorzystaj najefektywniejsze techniki oferowane przez C++ (cid:129) Popraw czytelnoœæ kodu i wydajnoœæ programów (cid:129) Zastosuj wzorce projektowe Rynek gier komputerowych jest niezwykle wymagaj¹cy. Gracze stawiaj¹ tego rodzaju programom coraz wy¿sze wymagania, co z kolei przek³ada siê na koniecznoœæ stosowania coraz doskonalszych technik ich tworzenia. Bêd¹c programist¹ gier komputerowych, na pewno doskonale zdajesz sobie z tego sprawê. Jeœli chcesz, aby kolejna stworzona przez Ciebie gra spe³nia³a oczekiwania nawet najbardziej wybrednych graczy, wykorzystaj jêzyk C++. Jego mo¿liwoœci sprawiaj¹, ¿e jest doskona³ym narzêdziem do tworzenia gier. „C++ dla programistów gier. Wydanie II” to przewodnik po jêzyku C++ opisuj¹cy go z punktu widzenia programowania specyficznych aplikacji, jakimi s¹ gry. Ksi¹¿ka przedstawia najefektywniejsze techniki C++ i metody rozwi¹zywania problemów, przed którymi staj¹ programiœci gier. Czytaj¹c j¹, dowiesz siê, jak zarz¹dzaæ pamiêci¹ i stosowaæ wzorce projektowe oraz STL. Poznasz mo¿liwoœci wykorzystania jêzyków skryptowych do usprawnienia procesu tworzenia gry komputerowej. Ka¿de z rozwi¹zañ opatrzone jest przyk³adem, dziêki czemu ³atwo bêdzie Ci zaimplementowaæ je w swoich pracach. (cid:129) Podstawy jêzyka C++ (cid:129) Korzystanie z szablonów (cid:129) Obs³uga wyj¹tków (cid:129) Zarz¹dzanie pamiêci¹ (cid:129) Poprawa wydajnoœci aplikacji (cid:129) Wzorce projektowe (cid:129) Biblioteka STL (cid:129) Stosowanie jêzyków skryptowych (cid:129) Zarz¹dzanie obiektami (cid:129) Serializacja Do³¹cz do elitarnej grupy programistów gier komputerowych Wydawnictwo Helion ul. Koœciuszki 1c 44-100 Gliwice tel. 032 230 98 63 e-mail: helion@helion.pl Spis treści Wprowadzenie ................................................................................ 13 Część I Elementy C++ .............................................................. 21 Rozdział 1. Dziedziczenie ................................................................................. 23 Klasy ................................................................................................................................ 23 Dziedziczenie ................................................................................................................... 24 Polimorfizm i funkcje wirtualne ...................................................................................... 27 Dziedziczyć czy nie dziedziczyć? ................................................................................... 30 Zasada 1. Dziedziczenie kontra zawieranie .............................................................. 30 Zasada 2. Zachowanie kontra dane ........................................................................... 31 Kiedy stosować dziedziczenie, a kiedy go unikać ........................................................... 31 Implementacja dziedziczenia (zaawansowane) ............................................................... 33 Analiza kosztowa (zaawansowane) ................................................................................. 35 Alternatywy (zaawansowane) .......................................................................................... 38 Dziedziczenie a architektura programu (zaawansowane) ............................................... 39 Wnioski ............................................................................................................................ 41 Zalecana lektura ............................................................................................................... 41 Rozdział 2. Dziedziczenie wielobazowe .............................................................. 43 Stosowanie dziedziczenia wielobazowego ...................................................................... 43 Wszystko w jednym .................................................................................................. 44 Wersja z zawieraniem ............................................................................................... 44 Dziedziczenie zwyczajne .......................................................................................... 46 Ratunek w dziedziczeniu wielobazowym ................................................................. 47 Problemy dziedziczenia wielobazowego ......................................................................... 47 Niejednoznaczność .................................................................................................... 48 Topografia ................................................................................................................. 48 Architektura programu .............................................................................................. 51 Polimorfizm ..................................................................................................................... 51 Kiedy stosować dziedziczenie wielobazowe, a kiedy go unikać ..................................... 53 Implementacja dziedziczenia wielobazowego (zaawansowane) ..................................... 54 Analiza kosztowa (zaawansowane) ................................................................................. 55 Rzutowanie ................................................................................................................ 55 Funkcje wirtualne z drugiej (kolejnej) klasy nadrzędnej .......................................... 57 Wnioski ............................................................................................................................ 58 Zalecana lektura ............................................................................................................... 58 6 C++ dla programistów gier Rozdział 3. O stałości, referencjach i o rzutowaniu ............................................ 59 Stałość .............................................................................................................................. 59 Koncepcja stałości ..................................................................................................... 60 Stałość a wskaźniki ................................................................................................... 60 Stałość a funkcje ........................................................................................................ 61 Stałość a klasy ........................................................................................................... 64 Stałe, ale zmienne — słowo mutable ........................................................................ 65 Słowo porady odnośnie const .................................................................................... 66 Referencje ........................................................................................................................ 67 Referencje kontra wskaźniki ..................................................................................... 67 Referencje a funkcje .................................................................................................. 68 Zalety referencji ........................................................................................................ 69 Kiedy stosować referencje ........................................................................................ 71 Rzutowanie ...................................................................................................................... 72 Potrzeba rzutowania .................................................................................................. 72 Rzutowanie w konwencji C++ .................................................................................. 73 Wnioski ............................................................................................................................ 76 Zalecana lektura ............................................................................................................... 76 Rozdział 4. Szablony ........................................................................................ 77 W poszukiwaniu kodu uniwersalnego ............................................................................. 77 Podejście pierwsze: lista wbudowana w klasę .......................................................... 78 Podejście drugie: makrodefinicja .............................................................................. 79 Podejście trzecie: dziedziczenie ................................................................................ 80 Podejście czwarte: uniwersalna lista wskaźników void ............................................ 81 Szablony .......................................................................................................................... 83 Szablony klas ............................................................................................................. 83 Szablony funkcji ........................................................................................................ 85 Powrót do problemu listy — próba z szablonem ...................................................... 85 Wady ................................................................................................................................ 87 Złożoność .................................................................................................................. 87 Zależności .................................................................................................................. 87 Rozdęcie kodu ........................................................................................................... 88 Obsługa szablonów przez kompilator ....................................................................... 88 Kiedy stosować szablony ................................................................................................. 88 Specjalizacje szablonów (zaawansowane) ...................................................................... 89 Pełna specjalizacja szablonu ..................................................................................... 90 Częściowa specjalizacja szablonu ............................................................................. 91 Wnioski ............................................................................................................................ 91 Zalecana lektura ............................................................................................................... 92 Rozdział 5. Obsługa wyjątków .......................................................................... 93 Jak sobie radzić z błędami ............................................................................................... 93 Ignorować! ................................................................................................................ 93 Stosować kody błędów .............................................................................................. 94 Poddać się (z asercjami)! ........................................................................................... 95 Stosować wywołania setjmp() i longjmp() ................................................................ 95 Stosować wyjątki C++ .............................................................................................. 96 Stosowanie wyjątków ...................................................................................................... 97 Wprowadzenie ........................................................................................................... 98 Zrzucanie wyjątków .................................................................................................. 98 Przechwytywanie wyjątków .................................................................................... 100 Spis treści 7 Odporność na wyjątki .................................................................................................... 103 Pozyskiwanie zasobów ............................................................................................ 104 Konstruktory ............................................................................................................ 107 Destruktory .............................................................................................................. 109 Analiza kosztowa ........................................................................................................... 109 Kiedy stosować wyjątki ................................................................................................. 111 Wnioski .......................................................................................................................... 112 Zalecana lektura ............................................................................................................. 112 Część II Wydobywanie mocy C++ ............................................. 113 Rozdział 6. Wydajność ................................................................................... 115 Wydajność i optymalizacje ............................................................................................ 115 W czasie programowania ........................................................................................ 117 Pod koniec programowania ..................................................................................... 118 Rodzaje funkcji .............................................................................................................. 119 Funkcje globalne ..................................................................................................... 119 Statyczne funkcje klas ............................................................................................. 120 Niewirtualne składowe klas .................................................................................... 120 Funkcje wirtualne przy dziedziczeniu pojedynczym .............................................. 121 Funkcje wirtualne przy dziedziczeniu wielobazowym ........................................... 123 Rozwijanie funkcji w miejscu wywołania ..................................................................... 124 Potrzeba rozwijania w funkcji ................................................................................. 124 Funkcje rozwijane w miejscu wywołania ............................................................... 125 Kiedy stosować rozwijanie zamiast wywołania? .................................................... 127 Jeszcze o narzutach wywołań funkcji ............................................................................ 128 Parametry funkcji .................................................................................................... 128 Wartości zwracane .................................................................................................. 130 Funkcje puste ........................................................................................................... 132 Unikanie kopiowania ..................................................................................................... 133 Argumenty wywołań ............................................................................................... 133 Obiekty tymczasowe ............................................................................................... 134 Jawność intencji ...................................................................................................... 135 Blokowanie kopiowania .......................................................................................... 135 Dopuszczanie kopiowania ....................................................................................... 136 Przeciążanie operatorów ......................................................................................... 137 Konstruktory i destruktory ............................................................................................. 139 Pamięci podręczne i wyrównywanie danych w pamięci (zaawansowane) ................... 143 Wzorce odwołań do pamięci ................................................................................... 144 Rozmiar obiektów ................................................................................................... 145 Rozmieszczanie składowych w obiektach .............................................................. 146 Wyrównanie pamięci ............................................................................................... 146 Wnioski .......................................................................................................................... 147 Zalecana lektura ............................................................................................................. 148 Rozdział 7. Przydział pamięci .......................................................................... 149 Stos ................................................................................................................................ 149 Sterta .............................................................................................................................. 150 Wydajność przydziału ............................................................................................. 151 Fragmentacja pamięci ............................................................................................. 152 Inne problemy ......................................................................................................... 154 Przydziały statyczne ...................................................................................................... 155 Zalety i wady przydziału statycznego ..................................................................... 156 Kiedy korzystać z przydziałów statycznych ........................................................... 157 8 C++ dla programistów gier Przydziały dynamiczne .................................................................................................. 158 Łańcuch wywołań ................................................................................................... 158 Globalne operatory new i delete .............................................................................. 159 Operatory new i delete dla klas ............................................................................... 161 Własny menedżer pamięci ............................................................................................. 163 Kontrola błędów ...................................................................................................... 163 Przeglądanie stert .................................................................................................... 166 Zakładki i wycieki ................................................................................................... 166 Sterty hierarchiczne ................................................................................................. 167 Inne rodzaje przydziałów ........................................................................................ 169 Narzut mechanizmu zarządzania pamięcią ............................................................. 170 Pule pamięci .................................................................................................................. 171 Implementacja ......................................................................................................... 172 Podłączanie puli do sterty ....................................................................................... 175 Pule uniwersalne ..................................................................................................... 176 W nagłych wypadkach… ............................................................................................... 177 Wnioski .......................................................................................................................... 178 Zalecana lektura ............................................................................................................. 179 Rozdział 8. Wzorce projektowe w C++ ............................................................ 181 Czym są wzorce projektowe? ........................................................................................ 181 Wzorzec Singleton ......................................................................................................... 183 Przykład: menedżer plików ..................................................................................... 183 Implementacja Singletona ....................................................................................... 184 Wzorzec Façade ............................................................................................................. 185 Fasada dla systemu „w budowie” ............................................................................ 188 Fasada dla przeróbek ............................................................................................... 189 Wzorzec Observer ......................................................................................................... 190 Wzorzec Visitor ............................................................................................................. 195 Wnioski .......................................................................................................................... 199 Zalecana lektura ............................................................................................................. 199 Rozdział 9. Kontenery STL .............................................................................. 201 Przegląd STL ................................................................................................................. 201 Korzystać czy nie korzystać? ........................................................................................ 203 Wykorzystanie gotowego kodu ............................................................................... 203 Wydajność ............................................................................................................... 204 Wady ....................................................................................................................... 205 Kontenery sekwencyjne ................................................................................................. 206 Kontener vector ....................................................................................................... 206 Kontener deque ....................................................................................................... 211 Kontener list ............................................................................................................ 214 Kontenery asocjacyjne ................................................................................................... 217 Kontenery set i multiset ........................................................................................... 218 Kontenery map i multimap ...................................................................................... 222 Kontenery haszowane ............................................................................................. 226 Adaptory kontenerów .................................................................................................... 230 Stos .......................................................................................................................... 231 Kolejka .................................................................................................................... 231 Kolejka priorytetowa ............................................................................................... 232 Wnioski .......................................................................................................................... 233 Zalecana lektura ............................................................................................................. 235 Spis treści 9 Rozdział 10. STL: algorytmy i zagadnienia zaawansowane ................................. 237 Obiekty funkcyjne (funktory) ........................................................................................ 237 Wskaźniki funkcji ................................................................................................... 237 Funktory .................................................................................................................. 238 Adaptory funktorów ................................................................................................ 240 Algorytmy ...................................................................................................................... 241 Algorytmy niemodyfikujące ................................................................................... 242 Algorytmy modyfikujące ........................................................................................ 245 Algorytmy sortujące ................................................................................................ 248 Uogólnione algorytmy numeryczne ........................................................................ 249 Ciągi znaków ................................................................................................................. 250 Ciągle bez ciągów ................................................................................................... 250 Klasa string .............................................................................................................. 252 Wydajność ............................................................................................................... 254 Pamięć ..................................................................................................................... 255 Alternatywy ............................................................................................................. 256 Alokatory (zaawansowane) ........................................................................................... 257 Kiedy STL nie wystarcza (zaawansowane) ................................................................... 260 Wnioski .......................................................................................................................... 262 Zalecana lektura ............................................................................................................. 262 Rozdział 11. Poza STL: własne struktury i algorytmy ......................................... 265 Grafy — studium przypadku ......................................................................................... 265 Powtórka z grafów .................................................................................................. 265 Ogólniej o grafie ...................................................................................................... 267 W kwestii kosztów .................................................................................................. 267 Koszt przebycia krawędzi a jakość rozgrywki ........................................................ 268 Grafy w C++ .................................................................................................................. 269 Zaprząc grafy do pracy .................................................................................................. 271 „Inteligencja” grafów .................................................................................................... 276 Życie na krawędzi ................................................................................................... 276 Aktualizacja, wracamy do C++ ............................................................................... 279 Implementacja wyznaczania trasy ........................................................................... 285 A może A* (zaawansowane) ................................................................................... 293 Inne zastosowania grafów ............................................................................................. 294 Optymalizacja przejść w interfejsie użytkownika ................................................... 296 Brakujące ścieżki powrotne .................................................................................... 298 Zagubione plansze menu ......................................................................................... 298 Wnioski .......................................................................................................................... 299 Zalecana lektura ............................................................................................................. 299 Część III Techniki specjalne ..................................................... 301 Rozdział 12. Interfejsy abstrakcyjne ................................................................. 303 Interfejsy abstrakcyjne ................................................................................................... 303 Implementacja interfejsów w C++ ................................................................................ 305 Interfejsy abstrakcyjne w roli bariery ............................................................................ 306 Nagłówki i wytwórnie ............................................................................................. 308 Z życia ..................................................................................................................... 310 Interfejsy abstrakcyjne w roli charakterystyk klas ........................................................ 312 Implementacje ......................................................................................................... 313 Pytanie o interfejs .................................................................................................... 315 Rozszerzanie gry ..................................................................................................... 317 Nie wszystko złoto… ..................................................................................................... 319 Wnioski .......................................................................................................................... 320 Zalecana lektura ............................................................................................................. 321 10 C++ dla programistów gier Rozdział 13. Wtyczki ....................................................................................... 323 Po co komu wtyczki ...................................................................................................... 323 Wtyczki do cudzych programów ............................................................................. 324 Wtyczki do własnych programów ........................................................................... 325 Architektura wtyczek ..................................................................................................... 326 Interfejs wtyczek ..................................................................................................... 326 Tworzenie konkretnych wtyczek ............................................................................ 327 Obsługa różnych rodzajów wtyczek ....................................................................... 328 Ładowanie wtyczek ................................................................................................. 329 Menedżer wtyczek ................................................................................................... 331 Komunikacja dwukierunkowa ................................................................................. 333 Żeby miało ręce i nogi… ............................................................................................... 335 Wtyczki w praktyce ....................................................................................................... 335 Stosowanie wtyczek ................................................................................................ 336 Wady ....................................................................................................................... 336 Inne platformy ......................................................................................................... 337 Wnioski .......................................................................................................................... 338 Zalecana lektura ............................................................................................................. 338 Rozdział 14. C++ a skrypty .............................................................................. 339 Po co jeszcze jeden język, i to skryptowy? ................................................................... 339 Złe wieści… ............................................................................................................ 340 I wieści dobre .......................................................................................................... 341 Rozważania o architekturze ........................................................................................... 344 Engine kontra gameplay .......................................................................................... 345 Zintegrowany interpreter skryptów — nie tylko w sterowaniu rozgrywką ................... 349 Konsola .................................................................................................................... 349 Interaktywne sesje diagnostyczne ........................................................................... 350 Szybkie prototypowanie .......................................................................................... 352 Automatyzacja testów ............................................................................................. 353 Wnioski .......................................................................................................................... 355 Zalecana lektura ............................................................................................................. 356 Lua ........................................................................................................................... 356 Python ...................................................................................................................... 357 GameMonkey Script ............................................................................................... 357 AngelScript .............................................................................................................. 358 Rozdział 15. Informacja o typach w czasie wykonania ....................................... 359 Praca bez RTTI .............................................................................................................. 359 Używanie i nadużywanie RTTI ..................................................................................... 361 Standardowe RTTI w C++ ............................................................................................ 363 Operator dynamic_cast ............................................................................................ 363 Operator typeid ........................................................................................................ 365 Analiza RTTI w wydaniu C++ ................................................................................ 366 Własny system RTTI ..................................................................................................... 368 Najprostsze rozwiązanie .......................................................................................... 368 Z obsługą dziedziczenia pojedynczego ................................................................... 372 Z obsługą dziedziczenia wielobazowego ................................................................ 375 Wnioski .......................................................................................................................... 378 Zalecana lektura ............................................................................................................. 378 Spis treści 11 Rozdział 16. Tworzenie obiektów i zarządzanie nimi .......................................... 379 Tworzenie obiektów ...................................................................................................... 379 Kiedy new nie wystarcza ......................................................................................... 380 Wielka selekcja ....................................................................................................... 381 Wytwórnie obiektów ..................................................................................................... 382 Prosta wytwórnia ..................................................................................................... 382 Wytwórnia rozproszona .......................................................................................... 383 Jawne rejestrowanie obiektów wytwórczych .......................................................... 384 Niejawne rejestrowanie obiektów wytwórczych ..................................................... 385 Identyfikatory typów obiektów ............................................................................... 387 Od szablonu ............................................................................................................. 388 Obiekty współużytkowane ............................................................................................ 389 Bez wspólnych obiektów ........................................................................................ 390 Ignorowanie problemu ............................................................................................ 391 Niech się właściciel martwi ..................................................................................... 392 Zliczanie odwołań ................................................................................................... 393 Uchwyty .................................................................................................................. 396 Inteligentne wskaźniki ............................................................................................. 398 Wnioski .......................................................................................................................... 401 Zalecana lektura ............................................................................................................. 402 Rozdział 17. Utrwalanie obiektów .................................................................... 405 Przegląd zagadnień dotyczących utrwalania jednostek gry .......................................... 405 Jednostki kontra zasoby .......................................................................................... 406 Najprostsze rozwiązanie, które nie zadziała ........................................................... 406 Czego potrzebujemy ................................................................................................ 407 Implementacja utrwalania jednostek gry ....................................................................... 409 Strumienie ............................................................................................................... 409 Zapisywanie ............................................................................................................ 412 Wczytywanie ........................................................................................................... 416 Wespół w zespół ............................................................................................................ 419 Wnioski .......................................................................................................................... 420 Zalecana lektura ............................................................................................................. 421 Rozdział 18. Postępowanie z dużymi projektami ................................................ 423 Struktura logiczna a struktura fizyczna ......................................................................... 423 Klasy i pliki ................................................................................................................... 425 Pliki nagłówkowe .......................................................................................................... 426 Co ma się znaleźć w pliku nagłówkowym? ............................................................ 426 Bariery włączania .................................................................................................... 427 Dyrektywy #include w plikach implementacji ........................................................ 430 Dyrektywy #include w plikach nagłówkowych ...................................................... 432 Wstępnie kompilowane pliki nagłówkowe ............................................................. 434 Wzorzec implementacji prywatnej .......................................................................... 437 Biblioteki ....................................................................................................................... 440 Konfiguracje .................................................................................................................. 443 Konfiguracja diagnostyczna .................................................................................... 444 Konfiguracja dystrybucyjna .................................................................................... 444 Konfiguracja diagnostyczna zoptymalizowana ....................................................... 445 Wnioski .......................................................................................................................... 445 Zalecana lektura ............................................................................................................. 446 12 C++ dla programistów gier Rozdział 19. Zbrojenie gry ............................................................................... 447 Stosowanie asercji ......................................................................................................... 447 Kiedy stosować asercje ........................................................................................... 448 Kiedy nie stosować asercji ...................................................................................... 450 Własne asercje ......................................................................................................... 451 Co powinno się znaleźć w ostatecznej wersji ......................................................... 452 Własna przykładowa implementacja asercji ........................................................... 454 Zawsze świeżo ............................................................................................................... 455 Wycieki pamięci ...................................................................................................... 456 Fragmentacja pamięci ............................................................................................. 456 Dryf zegara .............................................................................................................. 456 Kumulacja błędu ..................................................................................................... 457 Co robić? ................................................................................................................. 457 Postępowanie ze „złymi” danymi .................................................................................. 458 Wykrywać asercjami ............................................................................................... 459 Radzić sobie samemu .............................................................................................. 459 Kompromis .............................................................................................................. 461 Wnioski .......................................................................................................................... 463 Zalecana lektura ............................................................................................................. 463 Skorowidz .................................................................................... 465 Rozdział 8. Wzorce projektowe w C++ W tym rozdziale przygotujemy grunt na najbardziej zaawansowane elementy C++, które przyjdzie nam omawiać w miarę zbliżania się do końca książki. Odejdziemy na chwilę od szczegółów implementacyjnych i wejdziemy na nieco wyższy poziom abs- trakcji projektowej, którą stanowią właśnie wzorce projektowe (ang. design patterns). Lektura tego rozdziału zaowocuje lepszym zrozumieniem tego, jak bardzo język C++ odszedł od starego C; łatwiej też będzie docenić to, w jaki sposób język programowa- nia wspiera projektowanie na wysokim, naturalniejszym dla projektanta poziomie. Czym są wzorce projektowe? Każdy pamięta, że kiedy pierwszy raz uczył się programowania, to jego pierwsze wrażenie z kontaktu z językiem programowania — dowolnym językiem programo- wania — było wrażeniem obcości i niezrozumiałości: z początku widać tylko niejasne symbole i formacje tylko szczątkowo przypominające jakieś konstrukcje języka natu- ralnego. Wkrótce jednak przychodzi oswojenie z symbolami i składnią, a także świa- domość możliwości algorytmicznych wynikających z tej składni. Po opanowaniu podstaw języka okazuje się, że można czytać i postrzegać kod programu niejako w sposób na- turalny, bez biedzenia się nad rozbiorem poszczególnych instrukcji. Skrótowce takie jak x += 1 tracą swoją obcość i stają się naturalnym sposobem inkrementacji zmiennej. Po przyswojeniu pojęć zmiennych i operatorów zaczynamy oswajać się ze struktura- mi sterującymi: w miarę zaznajamiania się z koncepcjami sterowania wykonaniem programu okazuje się, że można dość swobodnie rozmawiać z innymi programistami o naturze pętli i instrukcji wyboru. Potem poznaje się podstawowe struktury, najważ- niejsze funkcje standardowe i coraz bardziej zaawansowane elementy języka. Na tym etapie zrozumienie struktury programu osiąga nowy poziom, tak samo jak zdolność do komunikowania się ze współpracownikami prawie że w samym języku programo- wania — w każdym razie przekazywanie współpracownikom koncepcji programi- stycznych przychodzi coraz łatwiej. Pojawia się przyspieszenie postępów nauki, po- nieważ co łatwiejsze koncepcje stają się intuicyjne, a te coraz bardziej skomplikowane również w miarę postępów stanowią coraz mniejsze łamigłówki. 182 Część II ♦ Wydobywanie mocy C++ Taka jest natura poznawania i opanowywania każdego systemu, który złożoność i wy- rafinowanie opiera na zestawie prostszych, niepodzielnych dalej elementów; progra- mowanie jest znakomitym przykładem takiego systemu. Szczególnie dobrymi przy- kładami są języki przewidziane do programowania obiektowego, bo ze swojej natury przenoszą uczącego się na coraz wyższe poziomy abstrakcji, pozwalając zaszczepiać zrozumienie niskopoziomowych konstrukcji do łatwiej przyswajalnych (albo bardziej logicznych) struktur wysokiego poziomu. I wtedy dochodzimy do wzorców. Wzorce projektowe są całkiem już abstrakcyjny- mi (oderwanymi od języka programowania) strukturami, które zarówno ujmują naturę typowych problemów projektowych, rozwiązywanych od nowa w każdym kolejnym projekcie, jak i proponują powszechnie przyjęte i wypróbowane rozwiązania tych problemów. Przykładami takich problemów są: (cid:141) Jak zapewnić globalny dostęp do zestawu klas bez zaśmiecania globalnej przestrzeni nazw i z utrzymaniem pożądanej dla projektowania obiektowego hermetyzacji abstrakcji. (cid:141) Jak napisać kod operujący na wspólnych elementach radykalnie odmiennych klas, ze wspólnym, dobrze pomyślanym interfejsem do nowych funkcji. (cid:141) Jak umożliwić obiektom różnych klas utrzymywanie referencji do siebie wzajemnie bez ryzykowania wiszących wskaźników i znikania obiektów na granicach zasięgów. W tym rozdziale skupimy się na czterech popularnych wzorcach projektowych, szczególnie przydatnych w projektowaniu i programowaniu gier. Wzorce te zostały wybrane do omówienia ze względu na to, że można je z powodzeniem zastosować w praktycznie każdym podsystemie gry. I najpewniej stosowaliśmy je już z dobrym skutkiem, choć bez świadomości, że nasze techniki zostały skatalogowane i opisane jako wzorce projektowe. Jeśli tak, to wypada tylko się cieszyć, bo to znaczy, że kod już jest przynajmniej w jakichś częściach organizowany zgodnie z najlepszą wiedzą. Z drugiej strony nieświadomość korzystania z wzorców projektowych oznacza, że nie było dotąd okazji do dołączenia opisanych rozwiązań do własnego słownika projek- towego. To z kolei uniemożliwia przekazywanie wysoce abstrakcyjnych koncepcji projektowych innym programistom, co było podstawowym motywem do katalogowania wzorców projektowych. Ale nic straconego. Po lekturze tego rozdziału będziemy już dysponowali solidną dawką wiedzy o kilku ważnych i popularnych wzorcach: (cid:141) Singleton (cid:141) Façade („fasada”) (cid:141) Observer („obserwator”) (cid:141) Visitor („wizytator”) Każdy z tych wzorców projektowych zostanie omówiony w osobnym podrozdziale. Omówienie będzie z konieczności i celowo uproszczone, dla łatwiejszego przetrawie- nia przedstawianych pojęć. Zachęcam jednak Czytelników do uzupełnienia wiedzy Rozdział 8. ♦ Wzorce projektowe w C++ 183 lekturą książek wymienianych pod koniec rozdziału; w nich można znaleźć znacznie więcej informacji o wszystkich omawianych wzorcach, istotnych dla podjęcia decyzji o ich ewentualnym wdrożeniu do własnych projektów. Wzorzec Singleton Na pierwszy ogień pójdzie wzorzec Singleton. Jak sama nazwa wskazuje, Singleton to klasa, która ma tylko (najwyżej) jeden egzemplarz. Ogólne zadanie Singletona to udostępnianie centralnego miejsca dla zestawu funkcji, które mają być dostępne glo- balnie dla całej reszty programu. Singleton powinien udostępniać otoczeniu dobrze zdefiniowany interfejs i hermetyzować jego implementację, co doskonale się zgadza z filozofią C++. Kolejną cechą Singletona jest to, że jedyny egzemplarz jest chroniony również przed ingerencjami z zewnątrz. Jak się wkrótce okaże, jedynym sposobem na utworzenie egzemplarza jest publiczny interfejs jego klasy — nie można samowolnie utworzyć Singletona za pomocą któregoś z jego konstruktorów! Ale zanim przejdziemy do szczegółów tworzenia egzemplarza, przyjrzymy się przykładowemu systemowi sta- nowiącemu idealnego kandydata na implementację wedle wzorca Singleton. Przykład: menedżer plików Praktycznie każda gra posiada własny podsystem obsługi plików w zakresie otwiera- nia, zamykania i modyfikowania plików. Taki podsystem jest zwykle określany mia- nem menedżera plików i zazwyczaj dla całej gry jest jeden wspólny taki system, bo plików zwykle potrzeba mnóstwo, ale do zarządzania nimi wystarczy pojedynczy menedżer. Podejrzewamy już, że taki menedżer może zostać zaimplementowany w postaci klasy, na przykład takiej jak poniższa: class FileManager { public: FileManager(); ~FileManager(); bool FileExists(const char* strName) const; File* OpenFile(const char* strName, eFileOpenMode mode); bool CloseFile(File* pFile); // itd... protected: // Tu „wnętrzności” menedżera }; Aby skorzystać z menedżera plików, wystarczy utworzyć obiekt klasy: FileManager fileManager; A potem można już z niego korzystać: File* pFP = FileManager.OpenFile( ObjectData.xml , eReadOnly); 184 Część II ♦ Wydobywanie mocy C++ Kod będzie działał zupełnie dobrze, ale takie podejście kryje kilka problemów, zwłaszcza w większych, bardziej rozbudowanych projektach. Przede wszystkim nie ma żadnego zabezpieczenia przed tworzeniem wielu obiektów menedżera plików. A ponieważ taki menedżer nie jest zwykle nijak powiązany z konkretną hierarchią klas ani z konkretnym podsystemem programu, tworzenie kilku menedżerów jest do- prawdy marnotrawstwem. A co gorsza, operacje na plikach zwykle angażują jakąś pulę zasobów sprzętowych i w grze zaimplementowanej wielowątkowo obecność wielu menedżerów plików odczytujących i zapisujących te potencjalnie wspólne za- soby to proszenie się o kłopoty. To, czego nam trzeba, to prosty menedżer, który będzie wyłącznym odpowiedzialnym za dostęp do owych zasobów, tak aby zawsze można było z niego bezpiecznie korzy- stać również w kodzie wielowątkowym; dobrze by było także, gdyby był elegancko hermetyzowany. Można by pomyśleć, że rozwiązanie jest banalne: wystarczy prze- cież, że ograniczymy się do utworzenia pojedynczego egzemplarza klasy menedżera plików! Cóż, może i wystarczy, ale takie założenie nie daje żadnej gwarancji, że kto inny nie utworzy innego egzemplarza. A nawet jeśli uda się wymusić na użytkowni- kach obecność tylko jednego egzemplarza, to gdzie niby powinien zostać utworzony? I jak użytkownicy mają się do niego odwoływać? Na pewno nasuwa się odpowiedź: wystarczy, żeby obiekt menedżera plików znajdował się w zasięgu globalnym. Może nasuwają się też inne rozwiązania, ale przejdźmy od razu do najlepszego: niech me- nedżer plików zostanie Singletonem. Implementacja Singletona Wiemy już, dlaczego należałoby wdrożyć do projektu właśnie ten wzorzec, więc pora go zaimplementować. Zaczniemy od podstawowej wersji klasy. class Singleton { public: static Singleton * GetInstance() { return s_pInstance; } static void Create(); static void Destroy(); protected: static Singleton * s_pInstance; Singleton(); // ukryty konstruktor! }; A oto jej implementacja: // Inicjalizacja wskaźnika jedynego egzemplarza wartością NULL Singleton* Singleron::s_pInstance = NULL; // Funkcja Create() static void Singleton::Create() { if (!s_pInstance) Rozdział 8. ♦ Wzorce projektowe w C++ 185 { s_pInstance = new Singleton; } } // Funkcja Destroy() static void Singleton::Destroy() { delete s_pInstance; s_pInstance = NULL; } Zwróćmy uwagę na jawne zastosowanie metod Create() i Destroy(). Można by co prawda ukryć proces tworzenia jedynego egzemplarza klasy za wywołaniem GetIn- stance(), zdając się na „opóźnione” (ang. lazy) utworzenie tego egzemplarza, odło- żone do momentu pojawienia się pierwszego odwołania do egzemplarza. Ale ponie- waż w Singletonach elegancja zaleca posiadanie zewnętrznego obiektu tworzonego i usuwanego jawnie, zastosujemy się do tego zalecenia. Kontynuując przykład z me- nedżerem plików, możemy teraz zrealizować go jako Singleton i utworzyć na początku egzemplarz menedżera: FileManager::Create(); i od tego momentu korzystać z niego jak poprzednio: FileManager::GetInstance()- OpenFile( ObjectData.xml , eReadOnly); Po zakończeniu korzystania z egzemplarza menedżera plików przy kończeniu programu należy usunąć egzemplarz: FileManager::Destroy(); Wzorzec Façade Następny wzorzec na naszej rozkładówce to wzorzec Façade (fasada). Z nazwy wy- nikałoby, że to jakby „sztuczny fronton”— przykrywka czegoś, co jest ukryte przed okiem widza. W programowaniu fasada jawi się zwykle jako interfejs do zestawu systemów albo klas, niekoniecznie ściśle ze sobą powiązanych. Zasadniczo fasada stanowi więc otoczkę ujednolicającą różnorakie interfejsy wszystkich tych systemów składowych do postaci interfejsu na przykład prostszego albo bardziej dostępnego. Dla przykładu weźmiemy klasę ilustrowaną rysunkiem 8.1. Zauważmy, że klasa w systemie A jest powiązana z kilkoma klasami w systemie B. W takim układzie w systemie B nie można mówić praktycznie o hermetyzacji systemu, bo o klasach B1, B2 i B3 musi wiedzieć klasa A1. Jeśli system B ulegnie w przyszłości zmianie w jakim- kolwiek niepowierzchownym zakresie, jest wielce prawdopodobne, że klasa A1 rów- nież będzie musiała zostać zmieniona. 186 Rysunek 8.1. Klasa w systemie A jest powiązana z potencjalnie wieloma klasami w systemie B Część II ♦ Wydobywanie mocy C++ Wyobraźmy sobie, że system B to podsystem generowania grafiki, a system A to klient tego systemu, na przykład interfejs użytkownika (menu gry). Klasami w pod- systemie graficznym mogą być na przykład: klasa zarządzająca teksturami, klasa od- rysowująca, klasa zarządzająca czcionkami czy klasa zarządzająca nakładkami (ang. overlay) 2D. Jeśli interfejs użytkownika będzie miał na ekranie wyświetlić komunikat tekstowy w jakimś przyjemnym dla oka formacie, będzie zapewne musiał odwoływać się do usług wszystkich tych czterech klas: Texture* pTexture = GfxTextureMgr::GetTexture( CoolTexture.tif ); Font* pFont = FontMgr::GetFont( UberRoman.fnt ); Overlay2d* pMessageBox = OverlayManager::CreateOverlay(pTexture, pFont, Hello World! ); GfxRenderer::AddScreenElement(pMEssageBox); Jeśli do systemu B wprowadzimy fasadę, będziemy mogli uzyskać wreszcie tak pożądany efekt hermetyzacji systemu i udostępnić jednolity, wspólny interfejs, lepiej i bezpo- średnio dostosowany do potrzeb systemu A. Pomysł ten ilustrowany jest rysunkiem 8.2. Zauważmy, że wprowadzenie do systemu B wzorca Façade powoduje, że w systemie A możemy korzystać z usług B za pośrednictwem pojedynczej klasy. Kontynuujmy przykład z podsystemem graficznym. Interfejs użytkownika może teraz zrealizować zadanie wyświetlenia okienka dialogowego za pośrednictwem poje- dynczego wywołania: // GraphicsInterface to nasza nowa fasada przesłaniająca // usługi podsystemu graficznego GraphicsInterface::DisplayMsgBox( CoolTexture.tif , UberRoman.fnt , Hello World! ); Rozdział 8. ♦ Wzorce projektowe w C++ 187 Rysunek 8.2. Fasada systemu B efektywnie hermetyzuje system i udostępnia pojedynczy punkt wejścia dla klientów systemu Tymczasem klasa GraphicsInterface może za kulisami, to jest wewnętrznie, inicjować kolejno te same wywołania, którymi jawnie posługiwał się interfejs użytkownika w po- przedniej wersji. Ale niekoniecznie; zresztą implementacja wywołania DisplayMsgBox może się od tego momentu dowolnie niemal zmieniać, ale dzięki dodatkowemu po- średnictwu fasady nie wymusi to żadnych zmian w kodzie interfejsu użytkownika, przed którym te zmiany zostaną skutecznie ukryte. Interfejs użytkownika jako klient systemu A powinien przetrwać nawet radykalne zmiany struktury wewnętrznej sys- temu B. Jak na rysunku 8.3, gdzie rozbudowa systemu nie musi być wcale widoczna dla któregokolwiek z zewnętrznych użytkowników systemu B. Przykład z podsystemem graficznym był dość oczywisty i pewnie każdy, kto imple- mentowałby taki system samodzielnie, wpadłby na pomysł udostępnienia dla całego podsystemu wspólnego interfejsu, za którym można by skutecznie chować szczegóły implementacji; i mało kto wiedziałby w ogóle, że stawia fasadę, tak jak rzadko uzmy- sławiamy sobie, że mówimy prozą. Ale można też podać mniej oczywiste przykłady zastosowania wzorca Façade — o nich za chwilę. 188 Część II ♦ Wydobywanie mocy C++ Rysunek 8.3. Ponieważ system B wdrożył wzorzec Façade, może się niemal dowolnie zmieniać — nawet dość radykalnie — bez wymuszania ingerencji w kodzie po stronie użytkowników systemu Fasada dla systemu „w budowie” Ogólnie mówiąc, jeśli stoimy dopiero na starcie do projektu i musimy zarysować techniczny projekt systemu, najlepiej zacząć od rozważenia wszystkich wymagań z punktu widzenia użytkowników tego systemu. W ten sposób może powstać spis wszystkich klas, które należałoby zaimplementować, spis interfejsów tych klas, opis ogólnej struktura systemu i tak dalej. Najlepiej byłoby, gdyby harmonogram projektu dawał wystarczająco dużo czasu, aby wcześniej można było przygotować znakomity projekt systemu. Ale w praktyce bywa raczej tak, że czasu jest za mało i projektowanie trzeba zarzucić na rzecz implemen- towania. Niedobory czasowe są zresztą typowe dla wszystkich etapów projektu i nie- kiedy nie można sobie pozwolić na jakiekolwiek wydłużanie poszczególnych faz i trzeba oddawać kolejne elementy tak szybko, jak się da. Jeśli do tego na udostępnie- nie systemu czekają nasi współpracownicy, ich oddech na naszym karku staje się pa- lący, a każde opóźnienie powoduje groźne napięcia w zespole — bo koledzy nie będą mogli z kolei zrealizować na czas swoich zadań. Rozdział 8. ♦ Wzorce projektowe w C++ 189 W takiej sytuacji pojawia się presja na to, aby zgodnie z regułami sztuki skupić się na dokończeniu projektowania przed przystąpieniem do implementacji systemu. Ale w większości przypadków byłoby to marnotrawstwem sporej ilości czasu. Prawda, że użytkownicy systemu polegają w swoim kodzie na stabilnym interfejsie, na którym mogliby opierać swoje implementacje i który nie wymuszałby zmian w ich kodzie w razie zmiany naszego systemu. Z drugiej strony klienci systemu nie zawsze mogą oprogramować swoje własne systemy, bo ich projekty mogą być uzależnione od róż- norakich czynników, takich jak choćby wydajność naszego systemu i jego zakres funkcji. Problemem jest współzależność i należałoby ją zerwać w przynajmniej jednym kie- runku. Wstrzymywanie tworzenia klientów naszego systemu powoduje silne szere- gowanie zadań w projekcie, które przecież po to się rozdziela, żeby je możliwie silnie zrównoleglić. Innymi słowy, trzeba się skupić na szybkim zaimplementowaniu i udo- stępnieniu podstawowych funkcji systemu, aby inni mogli na tej podstawie rozwijać własne podsystemy. Rozwiązaniem jest utworzenie interfejsu tymczasowego — fasady — która pozwala na korzystanie z elementów naszego systemu w czasie, kiedy ten jest dopiero w bu- dowie. Nawet jeśli interfejs systemu ostatecznie będzie zupełnie inny, to przynajmniej współpracownicy będą mogli dzięki niemu podjąć równoległe prace nad swoimi czę- ściami projektu. Co prawda będą musieli może nawet kilkakrotnie reagować na zmia- ny w interfejsie naszego systemu, ale korzyść ze zrównoleglenia prac, a zwłaszcza postępy współpracowników w rozwijaniu implementacji wewnętrznych elementów ich podsystemów, niemających związku z naszym kawałkiem projektu, mogą zre- kompensować koszty zmian interfejsu. Dodatkowe zalety tego podejścia ujawnią się w następnym przykładzie. Fasada dla przeróbek Pora na kolejne zastosowanie wzorca Façade, pozornie dość podobne do fasady dla systemów w budowie. Otóż kiedy znajdziemy się w potrzebie „refaktoryzacji” całego podsystemu, w naszym projekcie powinniśmy rozważyć zastosowanie fasady refak- toryzacji jako środka oddzielenia „dobrego” kodu, który ma pozostać niezmieniony, od „złego” kodu, który ma być przerobiony. Typowo w czasie przerabiania systemu dochodzi do wprowadzania licznych zmian w interfejsach różnych klas, co potencjalnie wymusza znaczące zmiany w interfejsie systemu jako całości. Wyobraźmy sobie, że dostaliśmy w spadku po poprzednikach dość przestarzały sys- tem efektów cząsteczkowych i stoimy przed zadaniem przerobienia go i uruchomienia tak, aby współdziałał z licznymi, znacznie nowszymi systemami gry. Zastany system może nie posiadać elementów i funkcji oczekiwanych w nowym środowisku albo po prostu nie spełniać wszystkich wymagań narzucanych przez projekt gry. Trzeba więc przepisać („refaktoryzować”, kiedy rozmawia się z szefem) liczne klasy w zastanym systemie, dodać do niego nowe klasy i pozbyć się niektórych niepotrzebnych. Oczy- wiście całe otoczenie podsystemu natychmiast przestanie działać, przez co kole
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

C++ dla programistów gier. Wydanie II
Autor:

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: