Monthly archive for November, 2007

Wyrafinowane sposoby marnowania czasu

2007-11-29 12:33

Grać każdy może – trochę lepiej lub trochę gorzej :) Wśród gier komputerowych każdy gatunek ma pozycje wybitne, które pierwsze przychodzą na myśl, kiedy tylko o nim wspomnimy. Przy każdej z nich można przesiedzieć długie godziny i dni, a inne gry często bywają nazywane “podobnymi do…” – Starcrafta, Quake’a, SimCity, Baldur’s Gate, i tak dalej.

Screen z gry KalOnlineAle jest też pewien szczególny gatunek gier, mających już na starcie uprzywilejowaną pozycję. Należące do niego tytuły nie zawsze muszą odznaczać się wyjątkową oprawą graficzną, nowatorskim gameplayem, wieloma możliwymi sposobami prowadzenia rozgrywki – a mimo to często zdobywają rzesze graczy, którzy są im wierni przez całe miesiące, a nawet lata. Pewnie nietrudno zgadnąć, jaki gatunek gier mam na myśli. Chodzi mi bowiem o te określane akronimem MMORPG – Massive Multiplayer Online Roleplaying Game.
Screen z gry Cabal OnlineZastanawiałem się niedawno, jaka jest tego przyczyna. W końcu mam pewne doświadczenia z kilkoma grami tego typu (a zwłaszcza z jedną :)), więc miałem nadzieję dojść do jakichś sensownych wniosków. W końcu wymyśliłem trzy powody, które wydają mi się najważniejsze.
Są to:

  • Magia numerków, gdzie tymi numerkami są głównie przeróżne statystyki postaci gracza na czele z tym najważniejszym – aktualnym poziomem (level). Podnoszenie tych wartości – ukryte pod eufemistycznym określeniem ‘rozwoju postaci’ – jest głównym celem MMORPGów. Różnią się one tutaj znacząco od klasycznych gier RPG, gdzie mimo wszystko najbardziej istotnym składnikiem jest linia fabularna – choćby po drodze wymagała wyrżnięcia tysięcy potworów.
  • Społeczność, czyli kontakty z innymi graczami: począwszy od przygodnych rozmów na chatach, poprzez granie w jednej drużynie, aż po najbardziej zaawansowaną formę, czyli gildie i klany. Każda z nich jest zresztą aktywnie wspierana przez mechanizmy samych gier. Nie da się ukryć, że często to inni ludzie są tym czynnikiem, który potrafi zatrzymać graczy – nawet jeśli pozostałe stracą już swoją przyciągającą moc.
  • Screen z gry World of WarcraftZłożoność. Gry typu MMORPG są skomplikowane, i to na wielu płaszczyznach. Dotyczyć to może rozległego świata, liczby dostępnych klas postaci i możliwych strategii grania każdą z nich, samej mechaniki gry, dodatkowych profesji pozwalających wytwarzać nowe przedmioty (crafting), specjalnych lokacji dla małych i wielkich grup graczy wraz z zamieszkującymi je bossami, systemu walki pomiędzy graczami (PvP) i pewnie jeszcze kilku innych aspektów rozgrywki. Dogłębne poznanie ich wszystkich jest zapewne niemożliwe, chociaż każdemu wydaje się, że może to zrobić :) Faktem jest jednak, że w dobrym MMORPGu, niezależnie od aktualnego stopnia rozwoju postaci, gracz zawsze ma co robić.

Nie zdziwiłbym się naturalnie, gdyby powyższa lista okazała się o wiele za krótka. Przeciwnie, byłoby to dość zaskakujące, jeśli fenomen gier MMORPG dało się zanalizować w tak trywialny sposób. Nadal też nie rozwiązałem dylematu, czy nad grami tego typu pożyteczniej jest się zastanawiać, czy może w nie… grać. Jak na razie obie te czynności wydają mi się marnowaniem cennego czasu ;D

Tags:
Author: Xion, posted under Games, Thoughts » 5 comments

Typy rozróżnialne

2007-11-28 10:11

W większości języków możemy zdefiniować nową nazwę dla istniejącego typu danych; nazywa się ją zwykle aliasem. I tak na przykład w C/C++ jest to możliwe za pomocą słowa kluczowego typedef. Analogicznie w Delphi mamy od tego słowo kluczowe type:
[delphi]type TMyInt = Integer;[/delphi]
Tak powstały typ TMyInt jest faktycznie tylko aliasem. Zmienne należące do tego typu są bowiem całkowicie kompatybilne ze zmiennymi zwykłego typu całkowitego Integer. W razie potrzeby konwersja między nimi może bez problemu zachodzić w obie strony.

Jeżeli jednak użylibyśmy deklaracji w formie:
[delphi]type TMyInt = type Integer;[/delphi]
wówczas TMyInt byłby już zupełnie innym typem niż Integer. Mimo że oba mogłyby przechowywać wartości tego samego rodzaju (liczby całkowite) i z tego samego zakresu, konwersje między nimi wymagałyby rzutowania.
Można by sądzić, że tworzenie takich typów rozróżnialnych “na siłę” jest bezcelowe. Zauważmy jednak, że typy wyliczeniowe (deklarowane przez enum) są przecież także w gruncie rzeczy liczbami z określonego zbioru. Najczęściej jednak nie chcemy, aby możliwa była niejawna konwersja między nimi a normalnymi typami liczbami. Wszystko dlatego, że w enumach liczby nie pełnią funkcji liczb, tylko identyfikatorów pewnych stanów.

Podobnie tutaj w przypadku TMyInt nie chodzi nam zapewne o liczby w sensie ich wartości, tylko o coś w rodzaju uchwytów – identyfikatorów obiektow. Kopalnią typów przeznaczonych do takiego właśnie celu jest oczywiście Windows API, zawierające tak znane i lubiane typy jak HWND, HINSTANCE, HDC, itd. Wszystkie one są w gruncie rzeczy liczbami 32-bitowymi, a mimo to nie są ze sobą kompatybilne. Gdyby API to było obiektowe, obiekty reprezentowane obecnie przez te uchwyty należałyby do różnych klas.
Efekt niezgodności uchwytów osiągnięto, deklarując ich typy nie jako aliasy na DWORD:

  1. typedef DWORD HWND;  // wszystkie tak określone typy będą ze sobą zgodne

lecz jako niezwiązane ze sobą typy wskaźnikowe:

  1. struct __HWND;
  2. typedef __HWND* HWND;

Można to uznać za dość pokrętną sztuczkę, ale na pewno jest ona lepsza niż tworzenie typu wyliczeniowego zawierającego nieco ponad 4 miliardy (232) nazwanych stałych ;]

Tags: , , ,
Author: Xion, posted under Programming » 2 comments

Wiedza czysto użytkowa

2007-11-26 22:11

Spotykam się ostatnio z osobliwym podejściem do przeróżnych wiadomości tyczących się programowania. Mogę je określić dość zaskakująco jako nadmiar “chęci rozumienia” lub ewentualnie “nieuzasadnioną dociekliwość”. Dotyczy to bardzo wielu narzędzi używanych w zasadzie każdej dziedzinie programowania – jak języki, środowiska czy biblioteki – oraz większości jego aspektów.

Czym to się objawia? Otóż symptomem jest pragnienie dogłębnego zrozumienia jakiegoś mechanizmu w sposób jak najszerszy i jak najgłębszy jednocześnie – zanim jeszcze spróbuje się go zastosować. Ten pęd to wiedzy dla samej wiedzy skutkuje najczęściej pozyskaniem dużego zasobu informacji, z których sensownym połączeniem i – przede wszystkim – stosowaniem są później pewne, a często spore, kłopoty.
Czy twierdzę więc, że nadmiar wiedzy szkodzi? Bynajmniej. Chodzi mi raczej o chęć zadawania raczej pytań w rodzaju “jak to działa?” niż “jak tego użyć?”. Sądzę bowiem, że zdecydowana większość programistycznej wiedzy ma charakter czysto użytkowy i jako taka jest na tyle przydatna, na ile daje się użyć w praktyce. Koder powinien być raczej przygotowany na poszukiwanie potrzebnych informacji w trakcie rozwiązywania danego problemu, a nie na kompletne poznanie tematu przez przystąpieniem do pracy.

Jeśli aczkolwiek życzymy sobie poznać głębiej jakieś zagadnienie z racji tego, że jest ono interesujące, wszystko jest w porządku. Jeśli jednak chodzi nam tylko o osiągnięcie zamierzonego celu, nie musimy poznawać rozgałęzień każdej drogi, która do niego prowadzi. Jak zawsze, najważniejsze jest zachowanie odpowiedniej równowagi.


Author: Xion, posted under Programming, Thoughts » 8 comments

HLSL i kolorowanie składni

2007-11-25 21:38

Niby kod można pisać w Notatniku, ale własnej równowagi psychicznej chyba lepiej zaopatrzyć się w edytor, który oferuje przynajmniej podświetlanie elementów składniowych języka. Wiadomo przecież, że mnogość kolorów poprawia samopoczucie :)
Co więc zrobić, gdy zamierzamy pisać efekty w języku HLSL (lub bardzo podobnym Cg)? Trzeba zdecydować się na jakieś narzędzie. Możliwych jest kilka wyjść:

Jak widać, nie jesteśmy więc skazani na surową, czarno-białą czcionkę. A to dobrze, bo po dodaniu tej całej skomplikowanej matematyki, dziwnej semantyki dla danych wierzchołków i niezliczonych dyrektyw kompilacji warunkowej, kod shaderów jest już wystarczająco skomplikowany :)

Kompilowanie efektów w locie

2007-11-24 22:26

Model subtraktywny programowalnego potoku graficznego (nie ma to jak kilka trudnych słów na początek ;P) charakteryzuje się tym, że kody shaderów są w nim dość rozdęte objętościowo. Wynikową postać shadera otrzymuje się bowiem poprzez wybranie części kodu odpowiadającej aktualnym potrzebom związanym np. z materiałem i oświetleniem. Rzeczone części są wydzielone przy pomocy dyrektyw podobnych do preprocesora z C: #if, #else, itd.

Najprościej jest wtedy, gdy korzystamy z plików efektów (.fx) z DirectX. Wtedy można użyć funkcji D3DXCreateEffectFromFile lub D3DXCreateEffectFromFile, którym można przekazać wartości makr potrzebnych w danym przebiegu renderowania. Działa to tak, jakbyśmy użyli dyrektywy #define bezpośrednio w kodzie efektu i podobnie do makr definiowanych w wierszu poleceń kompilacji w przypadku normalnych programów.
Otrzymany w ten sposób skompilowany shader należy oczywiście zachować, aby można było szybko przełączać między potrzebnymi wersjami w czasie renderowania. Wciąż jednak wymaga to ponownej kompilacji wszystkich używanych wersji shadera przy każdym uruchomieniu aplikacji – co jest marnotrawieniem czasu, jeżeli plik z kodem efektu się nie zmienia.

Można coś na to poradzić, stosując interfejs ID3DXEffectCompiler zamiast zwykłego ID3DXEffect. Ten pierwszy ma bowiem dodatkową, bardzo przydatną metodę CompileEffect:

  1. HRESULT ID3DXEffectCompiler::CompileEffect(
  2.   DWORD Flags,
  3.   LPD3DXBUFFER * ppEffect,
  4.   LPD3DXBUFFER * ppErrorMsgs
  5. );

W wyniku jej użycia możemy dostać bufor (czyli w gruncie rzeczy kawałek pamięci wypełniony danymi binarnymi) zawierający efekt w postaci skompilowanej. Najważniejsze jest to, że w tej postaci możemy zapisać go do pliku (zwykle z rozszerzeniem .fxo) i później tylko szybko odczytać – bez czasochłonnej rekompilacji. W ten sposób można stworzyć mechanizm cache‘owania skompilowanych shaderów, który przyspieszy uruchamianie aplikacji.

Tags: ,
Author: Xion, posted under Programming » Comments Off on Kompilowanie efektów w locie

Refleksje

2007-11-23 22:19

Dzisiaj zastanowimy się nad refleksjami. Nie, nie jest to wcale masło maślane. Mechanizm refleksji (reflection – odbicie) jest zwany też introspekcją i polega na tym, iż działający program “wie” o swojej wewnętrznej strukturze. Prostą odmianą jest tutaj dynamiczna informacja o typie (RTTI), czyli możliwość określenia faktycznego typu obiektu znanego poprzez wskaźnik lub referencję.
W bardziej zaawansowanej wersji, obecnej na przykład w języku Java i na platformie .NET, refleksje są jednak czymś więcej. Stanowią faktyczne ‘odbicie’ struktury programu w postaci informacji dostępnych w czasie wykonania. Przy ich pomocy można na przykład uzyskać dokładne informacje o polach, metodach i właściwościach każdej klasy oraz jej miejscu w hierarchii dziedziczenia, nie mówiąc już o możliwości tworzenia jej instancji. Dostępność takich informacji umożliwia łatwe stworzenie modułu serializacji lub bardziej zaawansowanego odśmiecania pamięci, bazującego na przechodzeniu grafu odwołań między obiektami.

Ale C++ posiada tylko bardzo proste RTTI, które do takich celów jest daleko niewystarczające. Czy można jednak naprawić tę przypadłość? Jest na to kilka sposobów:

  • Można spróbować odczytać informacje debugowania generowane przez nasz kompilator. To może dawać kompletne informacje o wszystkich typach użytych w programie, ale ma dwie zasadnicze wady. Po pierwsze, wymaga kompilacji programu przynajmniej z pewnymi ustawieniami trybu debugowania także w wersji wydaniowej. A po drugie, jest w oczywisty sposób nieprzenośnie pomiędzy różnymi kompilatorami. Poza tym nie wydaje mi się, żeby było to proste rozwiązanie.
  • Innym podejściem jest odczytanie wszystkich danych bezpośrednio, czyli samodzielne przeanalizowanie kodu źródłowego i wygenerowanie na tej podstawie koniecznych informacji. Najpewniej wynikiem byłyby dodatkowy kod poddawany normalnej kompilacji. Wadą jest tu stopień skomplikowania: parsowanie kodu C++ łącznie z rozwijaniem dyrektyw preprocesora zdecydowanie nie jest prostym zadaniem. Ponadto proces kompilacji projektu stałby się bardziej złożony.
  • Jeżeli ktoś byłby dostatecznie zdesperowany, mógłby teoretycznie… stworzyć własny kompilator C++, obsługujący refleksje natywnie. Nie da się ukryć, że potrzeba by sporej siły, by zamachnąć się motyką na takie słońce :)
  • Wreszcie możliwe jest, aby programista podawał niezbędne informacje – zwykle jako kombinacja dziedziczenia i makr preprocesora. To podejście jest (stosunkowo) najprostsze w implementacji, ale zapewne najbardziej nieporęczne w użytkowaniu. Może być też podatne na błędy. Jednocześnie jednak ma największe szanse być przenośne i… działające :)

Gdybym aczkolwiek kiedyś cierpiał na dużą nadwyżkę wolnego czasu, to pewnie rozważyłbym implementację rozwiązania drugiego (lub jakiejś kombinacji rozwiązania drugiego i pierwszego, na przykład zlecającej rozwijanie makr programowi kompilującemu). Do tego czasu jednak C++ już dawno będzie posiadał wbudowany system refleksji ;)

Tags: ,
Author: Xion, posted under Programming » 6 comments

Mario nieco inaczej

2007-11-22 21:20

Niektóre gry stają się legendarne… Dotyczy to w większości tych, które mają już swoje lata – jak choćby nieodżałowana pierwsza część Super Mario Bros.. Wielu przeszło ją nie raz i nie dwa razy (sam się do takich osób zaliczam :)), kiedy jeszcze królowała na ośmiobitowych konsolach.
Nie jest to oczywiście specjalne osiągnięcie, jako że jej zwykła wersja jest stosunkowo prosta. Jak przystało na grę nieśmiertelną, powstało jednak mnóstwo modów, hacków tudzież alternatywnych wersji.

Wśród nich jedna doczekała się całkiem sporej sławy jako najtrudniejszy wariant gry. A stało się tak z powodu tego oto filmu, na którym mierzy się z nią pewien gracz charakteryzujący się oryginalnym poczuciem humoru i – powiedzmy – dość mało powściągliwym sposobem wyrażania emocji ;) Zresztą tytuł mówi sam za siebie:

Super Mario Brothers – Frustration

Wszystkim fanom Mario i nie tylko, którzy jeszcze nie mieli okazji zobaczyć tej genialnej produkcji, niniejszym serdecznie ją polecam. Trzeba odkryć tajemnice technologii niewidzialnych bloczków ;]


Author: Xion, posted under Games » Comments Off on Mario nieco inaczej
 


© 2023 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.