Читать книгу Programowanie gier - Robert Nystrom - Страница 21

Wydajność i szybkość

Оглавление

Istnieje jeszcze inny rodzaj krytyki architektury oprogramowania i abstrakcji, o którym niekiedy się słyszy, zwłaszcza w świecie programistów gier. Głosi on, że szkodzą one ich wydajności. Wiele wzorców, które uelastyczniają nasz kod, zależy od wirtualnych dyspozytorów, interfejsów, wskaźników, wiadomości i innych mechanizmów. Wszystkie one generują jakiś koszt w postaci czasu wykonania.

Interesujący kontrprzykład stanowią szablony w C++. Metaprogramowanie szablonów może niekiedy dać nam abstrakcję od interfejsów bez negatywnych konsekwencji po stronie czasu wykonania. Istnieje tu całe spektrum elastyczności. Gdy piszemy kod wywołujący konkretną metodę w jakiejś klasie, określamy tę klasę w momencie pisania kodu – na sztywno zakodowaliśmy, którą klasę wywołujemy. Gdy korzystamy z metody wirtualnej lub interfejsu, klasa, która zostaje wywołana, jest znana dopiero w momencie wykonania. Jest to rozwiązanie znacznie elastyczniejsze, ale zakłada, że istnieją pewne rezerwy po stronie czasu wykonania. Metaprogramowanie szablonów znajduje się gdzieś pośrodku. W tym przypadku decyzję o tym, którą klasę wywołać, podejmujemy w momencie kompilacji, gdy tworzona jest instancja szablonu.

Istnieje po temu powód. W architekturze oprogramowania w dużej mierze chodzi o to, aby nasze programy były bardziej elastyczne, a ich modyfikacja wymagała mniejszego wysiłku. Znaczy to, że w programie zakodowane zostanie mniej założeń. Korzystamy z interfejsów, by nasz kod działał z dowolną klasą, która go implementuje, a nie tylko z tą jedną, która pozwala na to dzisiaj. Korzystamy z obserwatorów (rozdz. „Obserwator”) i komunikatów (rozdz. „Kolejka zdarzeń”), by pozwolić dwóm elementom gry na rozmowę ze sobą po to, aby potem z łatwością mogły to robić trzy lub cztery.

Wydajność to natomiast w całości kwestia założeń. W praktyce optymalizacja rozkwita dzięki konkretnym ograniczeniom. Czy możemy bezpiecznie założyć, że nigdy nie będziemy mieli więcej niż 256 oponentów? Świetnie, możemy wsadzić ID do pojedynczego bajta. Czy będziemy tu wywoływać jakąś metodę na jednym konkretnym typie? Świetnie, możemy to statycznie wyekspediować lub skorzystać z konstrukcji inline. Czy wszystkie jednostki będą tej samej klasy? Świetnie, możemy z nich utworzyć sympatyczną, ciągłą tablicę (rozdz. „Lokalizacja danych”).

Nie znaczy to, że elastyczność jest czymś złym! Pozwala nam szybko wprowadzić zmiany do naszej gry, a szybkość rozwoju oprogramowania jest absolutnie kluczowa, jeśli chcemy stworzyć produkt pozwalający na dobrą zabawę. Nikt, nawet Will Wright, nie może opracować zrównoważonego projektu gry na papierze. Wymaga to wielu iteracji i eksperymentowania. Im szybciej jesteśmy w stanie wypróbowywać pomysły i sprawdzać, jakie dają odczucia, tym więcej rzeczy możemy wypróbować i tym większe są szanse na to, że trafimy na coś fantastycznego. Nawet gdy znajdziemy już prawidłową mechanikę, mnóstwo czasu zajmie nam dostrajanie. Drobne zaburzenie równowagi może zniweczyć frajdę z gry.

Nie ma tu łatwej odpowiedzi. Aby sprawić, żeby nasz program stał się bardziej elastyczny, dzięki czemu łatwiej będzie prototypować, będziemy musieli ponieść pewien koszt po stronie wydajności. Podobnie optymalizacja kodu sprawi, że będzie on mniej elastyczny.

Z mojego doświadczenia wynika jednak, że łatwiej jest zwiększyć szybkość działania gry sprawiającej frajdę, niż sprawić, aby szybka gra zaczęła ją sprawiać. Możliwy kompromis polega na utrzymywaniu elastyczności kodu do czasu aż projekt się ustabilizuje i usunięciu kilku abstrakcji później w celu zwiększenia wydajności.

Programowanie gier

Подняться наверх