Gorące testy

2007-07-18 18:38

W takich dniach jak dzisiejszy, gdy słupek rtęci w termometrze oscyluje w zdecydowanie zbyt wyżynnych okolicach, można żałować, że procesor w stojącym na biurku pececie pracuje dokładnie tak, jak mu fabryka nakazała i ani megaherca więcej. Dlaczego? Bo może wtedy nie obyło by się bez chłodzenia wodnego, co odjęłoby robiący w tych dniach krecią robotę radiator :)

W takich dniach kodowanie też jakoś za bardzo nie idzie, ale oczywiście jest to marnym usprawiedliwieniem nierobienia niczego pożytecznego. Skoro więc produkowanie nowego kodu nie przebiega tak płynnie jak zwykle, może warto zająć się kodem już istniejącym?… Ano warto – zwłaszcza jeśli pilnie wymaga on np. tak elementarnej czynności jak testowanie.
Przy tej okazji oczywiście nie raz i nie dwa pada najważniejsze pytanie, zadawane jak świat długi i szeroki przez wszystkich programistów, jacy kiedykolwiek chodzili po tej ziemi. Tak, chodzi naturalnie o gromkie:

Dlaczego to nie działa?!

To jest jednak tylko pytanie podstawowe, które można modyfikować w zależności od swojej programistycznej specjalności. I tak koderzy zajmujący się grafiką w ogóle, a programowanie w DirectX w szczególności mogą zmodyfikować je do równie dramatycznego:

Dlaczego nic nie widać?

Ten problem jest już na szczęście bardziej konkretny i stąd jego rozwiązanie powinno być łatwiejsze. Istotnie, na warsztatowym Wiki leży sobie nawet odpowiedni artykuł w formie listy kontrolnej, która wylicza sporo możliwości, mogących stać za problemami. Pocieszać można się jeszcze tym, że w 99% przypadków możemy przynajmniej zobaczyć coś innego niż nieprzeniknioną czerń – ja np. gustuję w czyszczeniu bufora tylnego na jaskrawozielony, opierając się wszechobecnej dyktaturze koloru niebieskiego ;P

Skoro tyle piszę o testowaniu, debugowaniu czy jak to ktoś “ładnie” spolszczył – odpluskiwaniu – to powinienem jeszcze wspomnieć, jakiż to kod poddaję tym mało wyszukanym operacjom. Jest to oczywiście pewien kawałek modułu grafiki 2D, który niedawno opisywałem.
Tak więc zgodnie ze starą prawdą, pisanie programu to rozkosz; uruchamianie programu to koszmar :) I chociaż może (oby!) nie zawsze jest tak w rzeczywistości, to jednak programiści są istotami wybitnie omylnymi i przez tę drugą, mniej przyjemną fazę tworzenia też trzeba przejść. I prawdę mówiąc, raczej nie ma co się nad nią dłużej rozwodzić :P

Tags: ,
Author: Xion, posted under Programming » Comments Off on Gorące testy

Jedno D mniej

2007-07-16 16:20

Zaprogramowanie w miarę porządnego, elastycznego i wydajnego silnika grafiki 3D jest oczywiście dość trudne. W końcu trzeba zadbać o właściwą organizację sceny, wczytywanie modeli, system materiałów, oświetlenie, cienie i pewnie jeszcze mnóstwo innych rzeczy. Trzeci wymiar nie ułatwia nam wcale życia.

Wydawałoby się więc, że gdy go usuniemy, sprawy nabiorą znacznie przyjemniejszego (i prostszego) kształtu. No cóż, kiedyś życie było łatwiejsze – ale głównie dlatego, że mieliśmy mniejsze wymagania. Mieliśmy na przykład całkiem znośną bibliotekę 2D zaimplementowaną jako część DirectXa – czyli DirectDraw. Chociaż obcowanie z nią na początku mogło być dość odstręczające (zwłaszcza jeśli wcześniej korzystało np. tylko z windowsowego GDI czy arcyprostego SDL), to jednak i tak stworzenie powierzchni do rysowania z podwójnych buforowaniem jest w DD znacznie prostsze niż chociażby poprawne zainicjowanie urządzenia Direct3D…
Fakty są jednak takie, że 3D ma się dobrze i coraz lepiej, a grafika dwuwymiarowa jest najczęściej tylko dodatkiem (prawda, że niezbędnym) do renderowanej sceny. Dlatego też najlepiej używać do niej tego samego DirectXa, z którego korzystamy w trakcie wyświetlania scen.

I chociaż jest to generalnie trudniejsze, daje o wiele większe możliwości i lepszą wydajność. Można oczywiście korzystać z należącego do D3DX interfejsu ID3DXSprite, lecz jego możliwości są raczej ograniczone i do wygodnego stosowania należałoby zapakować go w bardziej gustowne klasy.
Stąd też lepiej, w moim odczuciu samemu zatroszczyć się o odpowiednie funkcje i klasy do obsługi rysowania 2D. Muszę przy tym przyznać, że jestem trochę może za bardzo przyzwyczajony do eleganckich interfejsów bibliotek w rodzaju GDI+, VCL czy Graphics32. Chodzi tu przede wszystkim o dosyć intuicyjną koncepcję “płótna” (canvas), czyli obiektu kontrolującego całe rysowanie (w zwykłym GDI nazywa się to kontekstem urządzenia). Płótno, jak sama nazwa wskazuje, jest to taki obiekt, na którym rysujemy inne obiekty – jak na przykład obrazy czy tekst. Jest to nieco inne rozwiązanie projektowe niż chociażby te stosowane w D3DX, w którym to same obiekty (sprite’y, czcionki, itp.) wiedzą, jak się narysować.

Jak zapewne nietrudno się domyślić, piszę o tym dlatego, iż obecnie zajmuję się właśnie implementacją modułu grafiki 2D opartego z grubsza o podane wyżej założenia. Naturalnie, zakodowanie wszystkich funkcji dostępnych w GDI(+) itp. byłoby strasznie pracochłonne i w większości przypadków zupełnie niepotrzebne. Dlatego ograniczam się przede wszystkim do wyświetlania prostokątnych obrazków, a później także do wypisywania tekstu.
Zadanie to może wydawać się proste, ale w gruncie rzeczy wcale takie nie jest. Weźmy chociażby pod uwagę to, że dla efektywności należy podczas rysowania obrazków (będących oczywiście oteksturowanymi prostokątami) unikać częstego przełączania tekstur. A z tego wynika, że pojedynczy “obrazek” to tak naprawdę określony fragment pewnej tekstury, dla którego trzeba chociażby wygenerować określone współrzędne. A to już wymaga odpowiedniego opisu takich obrazków (konsekwentnie unikam słowa sprite, które uważam za sztuczne ;P) – na przykład w zewnętrznym pliku tekstowym. Dla wygody czy chociażby dla celów animacji obrazki wypadałoby ponadto układać w kolekcje. W sumie wychodzi więc całkiem sporo pracy, nieprawdaż? :)
Zresztą samo rysowanie też nie należy do najłatwiejszych. Jedną z pojawiających się tu kwestii jest na przykład przycinanie (clipping), czyli ograniczenie rysowania do wybranego obszaru – w najprostszej wersji prostokąta. Direct3D od wersji 9 posiada wprawdzie narzędzie nazywane scissor test (“test nożyczkowy”), które to właśnie robi, jednak jego użycie praktycznie wykluczałoby możliwość buforowania rysowanych kształtów. Znaczy to, że każda zmiana obszaru przycinania wymuszałaby renderowanie zebranych trójkątów – co w dość skuteczny sposób obniżyłoby ogólną wydajność. Tak więc przycinanie trzeba robić – tadam! – ręcznie :P Na szczęście nie jest to bardzo trudne, jeżeli dotyczy wyłącznie prostokątów wyrównanych do osi układu współrzędnych (czyli wtedy, gdy wykluczamy obroty).

Tak mniej więcej przedstawia się początek tematu pt. grafika dwuwymiarowa. Czy ktoś nadal uważa, że jest to rzecz znacznie prostsza niż 3D? :)

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

Wykopaliska

2007-07-14 11:12

Na swoich stronach domowych zamieszczałem zwykle różnego rodzaju produkcje programistyczne własnego autorstwa. Przez sześć lat nazbierało się tego całkiem sporo, z czego oczywiście nie wszystkie nadają się ponownego pokazania :)
Z czeluści twardego dysku wybrałem jednak kilka, które, jak sądzę, mogą być jeszcze interesujące. Linki prowadzące do ich ściągnięcia można zobaczyć po prawej stronie, ale wypadałoby powiedzieć pokrótce, co (nie)ciekawego można tam znaleźć:

  • Megatutorial “Od zera do gier kodera” to rzecz jasna znany już kurs programowania w C++ i Windows API z kilkoma mniej lub bardziej związanymi z tym dodatkami. Ku mojemu zaskoczeniu okazał się on niezwykle popularny, co ma swoje dobre i złe strony :)
    Link prowadzi do strony Warsztatu, gdzie kurs jest dostępny w całości.
  • Taphoo to moja najnowsza produkcja, chociaż ma już kilka miesięcy. Jest to dość oryginalna gra logiczna, w której celem jest przejście wyznaczonej ścieżki na planszy i zniszczenie po drodze wszystkiego, co da się zniszczyć :)

    Taphoo (screen)

  • PenSoccer to z kolei nic innego jak popularne piłkarzyki – zwykle rozgrywane na kartce papieru w kratkę. Mimo że planowałem opracowanie jakiegoś AI dla tej gry, to póki co można grac jedynie lokalnie dwoma graczami. Ale za to grafika jest ładna ;P
  • CoordCAD to taki niecodzienny programik, którego przeznaczeniem jest rysowanie różnych figur w prostokątnym układzie współrzędnych. Można więc kreślić wykresy funkcji, linie proste, krzywe Beziera, itp.
  • ColorShop to bodaj najprostszy i jednocześnie najbardziej przydatny program z nich wszystkich :) Mieści się on w małym okienku, przy pomocy którego można pobrać kolor dowolnego piksela na ekranie. Mimo że generalnie trzymam się od grafiki jak najdalej, muszę stwierdzić, że przydaje się to w wielu najmniej oczekiwanych sytuacjach ;)
  • AlteString to z kolei wynik zabawy nowopoznanym językiem programowania C#. Teoretycznie program potrafi dokonywać różnego rodzaju ‘transformacji’ na tekście, a praktyce jest chyba jedną z najbardziej bezużytecznych aplikacji, jakie widział świat ;] Wymaga .NET Framework 2.0.
  • Zegarek jest, jak sama nazwa wskazuje, zegarkiem :) A jak przystało na porządny zegarek, potrafi on pokazywać aktualny czas oraz działać jak stoper. Posiada też funkcję synchronizacji czasu ze zdalnym serwerem, która niestety od czasu pojawienia się Windows XP nie jest już tak przydatna.

Jak widać nie ma tu żadnych zapierających dech w piersiach dzieł, ale istnieje przecież niezerowe prawdopodobieństwo, że coś może tu komuś wpaść w oko. Przy okazji muszę zaznaczyć, że kolejność ułożenia pozycji jest nieprzypadkowa i te z górnej części listy są generalnie ciekawsze niż te z dolnej :)


Author: Xion, posted under Games, Website » 2 comments

O profilowaniu, cyklach i kodzie z odzysku

2007-07-12 15:54

Dla odmiany dzisiaj napisałem niewielką, ale bardzo wiele mówiącą o każdym kodzie rzecz – czyli profiler. Już wyjaśniam, że jest to moduł służący do kontrolowania wydajności: dzięki odpowiednio umieszczonym wywołaniom można przy jego pomocy określić, ile czasu zajmuje wykonywanie poszczególnych części programu.

Zasadniczą częścią jest tu oczywiście jakiś sposób pomiaru czasu. Dawniej, pisząc moduł profilujący, przygotowałem go tak, by oprócz czasu faktycznego w (mili/nano)sekundach podawał też ilość cykli procesora przypadających na daną operację. Na procesorach x86 można to zrobić przy pomocy rozkazu RDTSC. Obecnie porzuciłem tę koncepcję z kilku powodów, przy czym najbardziej prozaiczny jest ten, że… nigdy mi się taka funkcjonalność nie przydała :) Poza tym obecnie wartość zwracana przez wspomnianą instrukcję (a jest to liczba cykli procesora od momentu ‘resetu’) jest mało wiarygodna, jako że zdarzenia w rodzaju wstrzymywania czy hibernacji systemu bardzo lubią ją resetować. I wreszcie: ciężko jest przecież optymalizować kod pod kątem pojedynczych cykli procesora, jeśli pisze się głównie w języku wysokiego poziomu.
Wspomniałem, że to nie jest mój pierwszy profiler. W istocie, tak naprawdę dokonałem teraz zwyczajnego przepisania go (czy raczej drobnego przerobienia jego kodu) tak, by pasował do nowej wersji mojej biblioteki kodów wszelakich. (Bo cały czas bronię się, żeby nazywać ją silnikiem :)). W sumie więc nie jest to jakieś wielkie osiągnięcie, ale przynajmniej dowodzi tego, że kod napisany parę lat wcześniej nie musi od razu iść do kosza. Aczkolwiek jeśli chodzi o tę poprzednią wersję biblioteki, to raczej nic ciekawego już w niej nie znajdę :]

Wypadałoby teraz powiedzieć co nieco o tym, w jaki sposób tego starego-nowego profilera się używa. Otóż jest to całkiem proste. Bazując na jednym z rozdziałów pierwszego tomu Perełek programowania gier zorganizowałem pomiar czasu hierarchicznie. Można zatem korzystać z tego, że mniejsze operacje są częścią większych i tak je profilować – na przykład:

  1. Profiler.Begin ("Frame");
  2. Profiler.Begin ("Update");
  3. /* (tutaj kod uaktualniający elementy sceny) */
  4. Profiler.End();
  5.  
  6. Profiler.Begin ("Render");
  7. /* (a tutaj renderowanie) */
  8. Profiler.End();
  9. Profiler.End();

Dzięki temu można sprawdzać nie tylko maksymalny, minimalny i średni czas generowania całej ramki gry, ale też te same czasy osobno np. dla samego renderowania. Dalej można by jeszcze dodać statystyki dotyczącego tego, ile czasu (minimalnie, maksymalnie i średnio) zajmują procentowo czynności “mniejsze” względem “większych”.

Ale to już wymaga dopisania od podstaw, więc w przeciwieństwie do recyklingu starego kodu nie byłoby takie proste :)

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

Partikle!

2007-07-11 20:55

Żeby zwiększyć szansę na to, by moje devlogowanie nie było tylko pobożnym życzeniem, od razu spróbuję przejść do rzeczy. Zaprezentuję mianowicie to, co udało mi się napisać w ostatni weekend.

Jako sposób na przypomnienie sobie DirectX’a, postanowiłem zrealizować prostą, ale efektowną “sztuczkę” znaną jako system cząsteczkowy. Dla mniej zorientowanych mogę wyjaśnić, że jest to sposób na generowanie ciekawych efektów graficznych stosunkowo małym wysiłkiem przy pomocy dużej ilości tzw. cząstek – albo też “partikli”, bo całość to przecież particle system. Takie cząstki mają zwykle dość prozaiczne właściwości, jak pozycja i prędkość (odpowiednio aktualizowane), a w praktyce są renderowane jako kwadraty zwrócone do kamery i pokryte określoną teksturą.

Swój system oparłem w dużym stopniu na zawartości 18. i (zwłaszcza) 19. rozdziału książki Programowanie gier w DirectX – doskonałej zresztą. Jak przystało na porządny system jest więc opisywanie właściwości cząstek za pomocą zewnętrznych skryptów. Za najbardziej pomysłowy uważam jednak opisany w rzeczonej książce mechanizm zdarzeń, czyli sposób na opisanie życia każdej cząstki – od poczęcia do naturalnej śmierci ;)
Weźmy na przykład taki dym. Po utworzeniu cząsteczka dymu na kolor jasnoszary, ale z czasem robi się ciemniejsza i coraz bardziej przezroczysta. Ponadto dym rzadko unosi się pionowo do góry, zatem początkowy wektor prędkości powinien co jakiś czas mieć losowo zmienianą składową poziomą.

Wszystko to można opisać w skrypcie, ustanawiając zdarzenia – czyli mówiąc, co spotka cząstkę np. w 3 sekundzie życia. Może to być na przykład coś takiego:

  1. event
  2. {
  3. at = 3
  4. velocity_x = { set, "random(-1,1)" }
  5. }

co – jak nietrudno się domyślić – jest ustawieniem składowej X prędkości na wartość z zakresu [-1;1]. Ciekawsza jest interpolacja liniowa parametrów:

  1. event
  2. {
  3. at = end
  4. color_a = { lerp, 0 }
  5. color_r = { lerp, 0 }
  6. color_g = { lerp, 0 }
  7. color_b = { lerp, 0 }
  8. }

W ten sposób cząsteczka będzie się stawać coraz ciemniejsza i coraz bardziej przezroczysta, bo i alfie, i trzem składowym koloru każemy liniowo zmniejszać się do zera. Całe zdarzenie nie ma przypisanego stałego czasu, a jedynie oznaczenie, iż ma się wykonać w chwili śmierci cząsteczki. W tym przypadku aczkolwiek znaczy to tylko tyle, że w tym momencie cząsteczka będzie już całkiem czarna i zupełnie niewidoczna, lecz interpolacja jej koloru będzie przeprowadzana przez cały czas (od ew. poprzedniego zdarzenia).
Zaprogramowanie tego mechanizmu było swoją drogą nieco kłopotliwe. W książkowym oryginale zdarzenia mogły zmieniać tylko jeden parametr cząstki, więc implementacja płynnego przejścia nie była trudna. W mojej wersji można naraz zmieniać dowolną ilość właściwości oraz mieszać ze sobą natychmiastowe (set) i płynne (lerp) zmiany. Ostatecznie skończyło się na trochę pokręconym sprawdzaniu różnicy między czasem, w którym zdarzenie ma wystąpić, a aktualnym zegarem cząsteczki.

Ano właśnie – nie czasem życia, tylko zegarem. Bo cząstkę można też zapętlić, dzięki czemu będzie wiele razy przechodziła przez te same zdarzenia. Wystarczy “oszukać” jej zegar:

  1. event
  2. {
  3. at = 0.5
  4. velocity_x = { set, "random(-1,1)" }
  5. }
  1. event
  2. {
  3. at = 1
  4. timer = { set, 0.01 }
  5. }

i już mamy taki oto śnieg:

Śnieg jako efekt cząsteczkowy

Przy okazji muszę stwierdzić, że mechanizm publikowania obrazków na Bloggerze to czysta poezja. Nie dość, że można uploadować własne pliki, to jeszcze miniaturki tworzone są automatycznie. Tak tak – nie krępuj się i kliknij :)

Może nie wygląda on zbyt realistycznie, ale jego tekstura była robiona bardzo na szybko ;P Zresztą, w efektach cząsteczkowych partikle (coraz bardziej lubię to słowo :D) są zwykle bardzo małe, a końcowe wrażenie jest oparte na wyglądzie całej tej “masy”, a nie pojedynczych cząstek. Tak czy owak widać jednak, że systemy cząsteczkowe to całkiem przydatny mechanizm tworzenia ładnego dymu, śniegu, kurzu, efektów magicznych, że nie wspomnę o błyskach towarzyszących zbieraniu różnych power-upów :) Mała acz zgrabna rzecz.
Przy czym ‘mała’ to pojęcie dość względne, jako że ostatecznie system zamknął się w ok. 1500 linijkach kodu – i to nie licząc parsera skryptów (który oparłem na opracowanym jakiś czas temu formacie XVL) i używanego też parsera wyrażeń matematycznych. Całkiem niezły wynik jak na dwa dni kodowania :)

Tags: , ,
Author: Xion, posted under Programming » 1 comment

Start?…

2007-07-11 20:15

Dzień dobry, albo i dobry wieczór. Który to już raz witam się na nowej lub z lekka odnowionej stronie domowej? Taka okazja zdarzała się zdecydowanie zbyt dużo razy ;P Dlatego miejmy nadzieję, że ta jest ostatnia.
W kwestii stron domowych przerabiałem już chyba wszystkie opcje, włączając bloga. Ale teraz to ma być devlog – czyli takie miejsce, gdzie mam głównie prezentować postępy w mojej pracy programistycznej. Założenie to ambitne i wiele chwalebne, ale z praktyką może być pewnie gorzej ;)

Jedną z dużych przeszkód było zwykle to, że o ile o programowaniu gier czy też użytków mam jak sądzę jako takie pojęcie, o tyle webmastering zawsze był i nadal jest moim słabym punktem. Co więcej, mam chyba jakąś nieuleczalną alergię na te tematy :) Po prostu widok obleśnego HTMLa czy też kodu PHP – lub co gorsza, połączenia jednego i drugiego – nie napawa mnie jakimiś przyjaznymi uczuciami. W takiej sytuacji prowadzenie strony internetowej jest… hmm… trudne ;P
Dlatego bardzo miło znajduję Bloggera. Wprawdzie korzystanie z niego ostatnio nabrało chyba rozmiarów jakiejś mody napędzanej instynktem lemingów, ale nie da się ukryć, że serwisu używa się bardzo przyjemnie. I to musi być najprawdziwsza prawda, skoro nawet taki webowy analfabeta jak ja się do tego przyznaje :P Naturalnie wynikowym produktem nie jest normalna strona domowa, a zwykły (b)log, lecz i tak efekt jest wyjątkowo dobry w stosunku do włożonego wysiłku. Ot, Web Dwa Zero w pełnej krasie :)

Zatem póki co możemy być optymistami – a przynajmniej ja pozwolę sobie na ten rzadki luksus :) Jeśli (znów) nie zdarzy się jakiś bliżej nieokreślony dziejowy kataklizm, może nawet coś z tego będzie…


Author: Xion, posted under Website » 1 comment
 


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