Posts from 2010

Tower Offence

2010-04-16 19:18

Zamieszczam grę stworzoną przez moją drużynę (Rzeźnicy Inc. :]) w ramach konkursu Compo, który odbył się w zeszłą niedzielę. Jak co roku trwa on około 7 godzin, podczas których należy stworzyć w zasadzie kompletną grę (z dokładnością do ewentualnych, wcześniej przygotowanych frameworków) na zadany temat. Nasza praca zajęła drugie miejsce, przegrywając o włos – w dodatkowym głosowaniu publiczności – ze zwycięską.

[singlepic id=81 w=320 h=240 float=center]

Gra jest wariantem ‘Tower Defence’, czyli obrony przed nadciągającymi hordami wrogów za pomocą strategicznie poustawianych wież. Najważniejszą różnicą jest to, że mamy tutaj gracza, który sam może się po planszy poruszać i strzelać do stworków (i jednocześnie musi ich unikać); stąd zresztą nazwa Tower Offence :) Ponadto budowanie wieżyczek jest możliwe tylko w polach sąsiadujących z aktualną pozycją gracza. Ogólnie chcieliśmy, żeby rozgrywka była bardziej dynamiczna niż w zwykłym TD, a jednocześnie eliminowała jeden bardzo frustrujący element tego rodzaju gier: sytuację, gdy jeden stworek przebija się przez naszą linię obrony, a my nie możemy nic zrobić, jak tylko patrzeć jak powoli idzie w kierunku naszej bazy… Tutaj możemy wtedy wziąć sprawy we własne ręce i po prostu go ustrzelić :)

File: [2010-04-11] Tower Offence  [2010-04-11] Tower Offence (5.7 MiB, 1,128 downloads)

Zapraszam więc do rzucenia okiem na tę produkcję. Niech dodatkową rekomendacją będzie to, że mi osobiście gra się w nią bardzo przyjemnie – a z gatunkiem TD bynajmniej nie przepadam ;P
Do działania gra wymaga aktualnych runtime‘ów DirectX-a.

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

Na Lodowym Tronie

2010-04-13 22:59

Nieczęsto pozwalam sobie na aż taki off-topic, ale w tym przypadku uważam, że spokojnie mogę być usprawiedliwiony. Jeśli bowiem ktoś grał swego czasu w Warcrafta 3, to pewnie pamięta, że kampania w tej grze (a właściwie dodatku do niej) kończyła się powstaniem jednego z najpotężniejszych antagonistów, jakich świat gier tej doskonałej serii kiedykolwiek widział – w polskiej wersji znanego jako Król Lisz, a w oryginale jako Lich King. Jego długi cień rzucał się potem na pierwszą edycję kolejnej gry z serii, czyli World of Warcraft. Aż w końcu teraz możliwe jest stanięcie mu naprzeciw i rozprawienie się z nim… miejmy nadzieję, że raz na zawsze.

[singlepic id=80 w=320 h=240 float=center]

I to właśnie udało mi się zrobić dzisiaj, co widać na obrazku powyżej. Ci, którzy nie mają specjalnego pojęcia o serii Warcraft mogą zareagować co najwyżej wzruszeniem ramion. Co bardziej hardcore‘owi gracze WoW-a mogą z kolei wskazywać, że to przecież tylko wersja 10-osobowa, na normalnym poziomie trudności i w dodatku o 10% łatwiejsza niż oryginalnie.

Ale ja się tym zupełnie nie przejmuję :) Jak na moje ograniczone możliwości czasowe oraz niewielką zdolność do wciskania klawiszy WSAD oraz przycisków myszki naraz, uważam to za satysfakcjonujące osiągnięcie. A że ma też ono dla mnie pewne znaczenie symboliczne, pozwalam sobie podzielić się z nim ze światem.

Tags:
Author: Xion, posted under Events, Games » 3 comments

IGK nr 7

2010-04-11 23:32

W tym nieprzewidywalnym, szybko zmieniającym się i zaskakującym niespodziewanymi wydarzeniami świecie są jednak pewne rzeczy, które się nie zmieniają – i mam nadzieję, że zmieniać się nie będą. Jedną z nich jest to, że co roku w okolicach początku wiosennego kwietnia koderzy z Warsztatu i okolic zjeżdżają do Siedlec, by wziąć udział w konferencji poświęconej tworzeniu gier komputerowych – czyli IGK. To już sześć lat minęło od pierwszej takiej imprezy i z początku chyba tylko nieliczni wyrażali nieśmiałe nadzieje na to, że stanie się ona stałym punktem w ich kalendarzu…


Wykład na temat ciał miękkich

A jednak to się kręci. Niniejszą notkę piszę właściwie na gorąco, jako że tegoroczna edycja nr 7 zakończyła się zaledwie przed kilkoma godzinami. Oficjalnie. Bo tak naprawdę IGK trwa tak długo, jak długo jej uczestnicy – pasjonaci kodowania gier – chcą się spotykać i spędzać ze sobą czas, niekoniecznie tylko na sali wykładowej, słuchając kolejnych referatów z interesujących ich dziedziny. Zdecydowanie nie tylko tam :)
W tym roku referaty dopisały ilościowo i jakościowo. Dwie odbywające się równolegle sesje plenarne oferowały ciekawe wykłady z różnych obszarów szeroko pojętej ‘inżynierii gier komputerowych’, z których zapewne każdy mógł wybrać dla siebie coś pasującego – i to w kilku sztukach. Częściowo pewnie przyczynił się do tego zapał organizatorów (koła naukowego studentów informatyki Akademii Podlaskiej), którzy sami przygotowali kilka prezentacji. Nie obyło się też bez przedstawicieli firm informatycznych; aczkolwiek obiecane wystąpienie Techlandu ostatecznie się nie odbyło ;(


Niektórych prac nie powstydziłby się
sam TurboDymoMen ;-)

Ale referaty to dla wielu tylko poboczny fragment części oficjalnej IGK. Nie może tu zabraknąć Compo, czyli konkursu czteroosobowych grup piszących gry na wybrany temat w ciągu kilku (ok. 7-8) godzin trzeciego dnia konferencji. Udział w nim jest niezwykle pouczającym, a jednocześnie dość nietypowym doświadczeniem (o ile ktoś nie jeździ na imprezy demoscenowe, rzecz jasna :]). Jest też bardzo satysfakcjonujący wtedy, gdy w wyniku włożonych podczas niego wysiłków powstaje gotowa, grywalna gra. Coś takiego właśnie stało się moim udziałem w tym roku. A że przy okazji udało się nią zgarnąć nagrody za drugie miejsce – to niewątpliwie miły, ale tylko dodatek :)

Nieodzowną częścią IGK jest natomiast, khm… “wieczorek integracyjny” (czytaj: LAN-party) po zakończeniu programu oficjalnego. To też świetna okazja, by niemal w czasie rzeczywistym podzielić się wrażeniami – co też właśnie czynię. Ale ponieważ czas tu jest cenny, a naprędce sklecona topologia sieci nie do końca stabilna (co oczywiście tylko dodaje jej klimatu), to na tej krótkiej impresji pozwolę sobie zakończyć. W końcu Quake 3 i Starcraft same się w siebie nie zagrają :>

(Zdjęcia dzięki uprzejmości Rega)

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

Sklejanie wierszy kodu w C++

2010-04-06 22:24

Języki programowania o składni podobnej do C nie narzucają specjalnych ograniczeń na to, jak pisany w nich kod formatujemy wizualnie. Możemy więc robić dowolne wcięcia, a także w dowolny sposób dzielić poszczególne instrukcje między wiersze w pliku źródłowym. Jest tak dlatego, że to inne znaki – jak średniki czy nawiasy klamrowe – nadają listingowi strukturę odczytywaną przez kompilator.

W C/C++ są jednak przynajmniej dwie sytuacje, w których “fizyczne” wiersze w pliku z kodem mają znaczenie. Przekonał się o tym każdy, kto próbował zdefiniować (dyrektywą #define) makro rozciągające się na kilka linijek. Ściśle rzecz ujmując, w ogóle nie da się tego zrobić – dyrektywy preprocesora z założenia zajmują zawsze dokładnie jeden wiersz. Można jednak sprawić, by ów wiersz… rozciągał się na kilka linijek:

  1. #define FUNCTOR(name,params,code) class name { \
  2.     public: \
  3.         void operator()(params) \
  4.             code \
  5. };
  6. // disclaimer: nie należy zakładać, że powyższe makro
  7. // ma jakikolwiek praktyczny sens ;)

Znak \ (backslash) robi właśnie ten rodzaj magii. “Skleja” on mianowicie dwie linijki kodu tak, że dla kompilatora stają się one jedną. Używając go, możemy więc rozbić makro preprocesora na wiele wierszy.

Druga sytuacja, w której tak stosowany backslash jest przydatny, to długie ciągi znaków pisane w kodzie:
std::cout << "To jest bardzo długi komunikat, który w konsoli pojawi się \ w pojedynczym wierszu mimo tego, że tutaj w pliku źródłowym jest on podzielony \ na kilka następujących po sobie linijek." << std::endl;[/cpp] Potrafi on bowiem także "oszukać cudzysłów". Jednak identyczny efekt można też osiągnąć, jeśli wiemy, że kompilator sam skleja następujące po sobie napisy. “a” “b” daje na przykład to samo "ab". Dlatego też backslash w wielolinijkowych napisach jest stosunkowo rzadko używany.

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

Facebook, gry webowe i problem inwestycji

2010-04-01 17:12

No i stało się – kierowany zapewne diabelskimi podszeptami, zarejestrowałem się na Facebooku. Zło (a nawet Zuo) w najczystszej postaci żem uczynił i oprócz formułowania próśb o przebaczenie mogę jedynie usprawiedliwiać się, co mnie do tego zgubnego w skutkach czynu popchnęło. A owym skutkiem (a “zupełnie przypadkiem” również przyczyną) było to, że przyjrzałem się największemu bogactwu tego serwisu, czyli… grom, rzecz jasna :)
Gry na Facebooku dzielą się w sumie na dwie kategorie. Pierwsza z nich to wyewoluowana forma małych, szybkich gier flashowych, które służą do zabijania tych krótkich odcinków czasu, gdy akurat nie chce się nam robić czegoś pożytecznego. Ewolucja polegała tutaj na wykształceniu możliwości porównywania wyników ze znajomymi, co oczywiście natychmiast podnosi grywalność przynajmniej o 120% i jednocześnie uspokaja nas, że nie jesteśmy jedynymi osobami, które się obijają :)
Drugi typ to zabawa w systematyczne klikanie: należy mniej więcej raz na kilkanaście godzin zalogować się i coś zrobić, by posunąć rozgrywkę do przodu. Cokolwiek to jest, nie możemy tego zrobić częściej, bo nie pozwala na to mechanika gry pod postacią regenerującej się w czasie energii/many/itp. (jak choćby w Mafia Wars) czy też nierealistycznie krótkiego okresu wegetacyjnego truskawek (tak, mam tu na myśli oczywiście Farmville).

Jak widać w obu przypadkach nie jest to specjalnie absorbujące zajęcie. Z gier pierwszego typu do gustu przypadła mi Word Challenge, dzięki której wydatnie (czyli o jakieś 5%) poszerzył się mój zasób angielskich słówek (niestety jedynie takich, które mają co najwyżej sześć liter). Wybraną przeze mnie pozycją z kategorii drugiej jest z kolei Castle Age – coś w rodzaju MMORPG-a przez przeglądarkę, całkiem zresztą udanego. I właśnie z tym związany jest pewien ciekawy problem…

W rzeczonej grze oprócz niewątpliwie ekscytującego odklikiwania kolejnych questów mamy też – że powiem nieco na wyrost – wątek ekonomiczny. Należy tam bowiem kupować nieruchomości, które później co godzinę generują nam przypływ świeżej gotówki. Budynki różnią się ceną i uzyskiwanym z nich przychodem, a ponadto możemy kupować je nie tylko pojedynczego, ale i w pakietach (po 5 lub 10).
Jak pewnie nietrudno się domyślić, nasuwającym się tu od razu pytaniem jest to o optymalną strategię nabywania kolejnych budynków, skutkującą największym zyskiem w dłuższej perspektywie czasowej. Tak naszkicowany problem inwestycji (nazwa brzmi cokolwiek poważnie!) miałby więc następujące założenia:

  • Istnieje n \in \mathbb{N} rodzajów budynków z przypisanymi cenami początkowymi c_1, \ldots, c_n i zyskiem generowanym na jednostkę czasu: z_1, \ldots, z_n. Początkowo posiadamy k_1 = \ldots = k_n = 0 budynków i p pieniędzy, ale w każdej jednostce czasu możemy dokonać dowolnej liczby zakupów, o ile posiadane środki na to wystarczają.
  • Każdorazowo po dokonaniu zakupu budynku danego rodzaju, jego cena wzrasta o ustaloną wartość d_i dla i \in {1, \ldots, n}. Zakupu możemy dokonywać pojedynczo lub też w ilościach określonych przez zbiór L \subset \mathbb{N} (w Castle Age mamy L = \{ 5, 10 \}), jednak zawsze tylko jednego rodzaju budynków naraz.
  • Istnieje ograniczenie k \in \mathbb{N} na maksymalną posiadaną liczbę budynków jednego rodzaju – zatem zawsze zachodzi: \forall i \le n, i \in \mathbb{N} \quad k_i \le k. Dla ułatwienia możemy przyjąć, że ograniczenie to jest stałe (w Castle Age jest ono związane z poziomem doświadczenia).

Pytamy tutaj o to, kiedy, ile i jakie budynki powinniśmy kupować, aby zmaksymalizować swój zysk w długim (najlepiej dowolnie długim) przedziale czasu. W szczególności zastanawiamy się, czy działa tu prosta strategia zachłanna – podobna do rozwiązania ciągłego problemu plecakowego – polegająca na zbieraniu zawsze takiej ilości gotówki, aby kupować maksymalną ilość najbardziej “efektywnych” budynków, tj. tych o największym ilorazie zysku do bieżącej ceny. Intuicja podpowiadałaby, że nie dla wszystkich zestawów danych musi tak być…

Zostawiam więc to zadanie jako materiał do przemyśleń na wolne dni. Nagroda za jego rozwiązanie będzie wielka: udowodni się w ten sposób, że gry z Facebooka mogą się do czegoś przydać!

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

Może być tylko jeden… shader

2010-03-26 20:42

Nie tak znowu dawno temu napisałem notkę na temat kilku typowych nieporozumień, jakie czasami pojawiają w temacie shaderów oraz związanych z nimi (przynajmniej w DirectX) plików .fx. Nie wspomniałem w niej jednak o pewnej kwestii, która jest kluczowa, dla wielu oczywista, a jednocześnie bywa nielichym i do tego niezbyt przyjemnym zaskoczeniem dla kogoś, kto dopiero zaczyna bliższe spotkanie z tematem programowania grafiki 3D.

Scenariusz wyglądać tu może mniej więcej tak. Na początku pracowicie zgłębiamy tajniki posługiwania się graficznym API (dla ustalenia uwagi możemy założyć, że będzie to DirectX :]), w idealnym przypadku zaznajamiając się też dogłębnie ze związaną z tym matematyką. Umiemy obiekty wyświetlać, teksturować, oświetlać, kontrolować ich widoczność, a może nawet i wczytywać skomplikowane modele z plików. Wydaje się, że to wszystko nie jest takie trudne… aż do momentu, gdy doznamy Szoku Typu Pierwszego i dowiemy się, że większość tych wszystkich technik opartych na fixed pipeline jest nam zupełnie niepotrzebna. Żeby bowiem osiągnąć jakiekolwiek sensowne i godne pokazania efekty, w tym chociażby te tak oczywiste jak dynamiczne światła czy cienie, trzeba używać shaderów…
No cóż, mówi się trudno i kodzi się dalej :) Pracowicie eksperymentujemy więc z różnymi efektami graficznymi, pisząc dla nich odpowiednie vertex i pixel shadery, ucząc się przekazywania danych od jednych do drugich, renderowania różnego rodzaju materiałów, korzystania z poszczególnych typów oświetlenia czy efektów postprocessingu i całej masy różnych innych, interesujących rzeczy. Aż w końcu przychodzi taki moment (i to raczej wcześniej niż później), że proste, pojedyncze efekty przestają nam wystarczać – i tutaj właśnie doznajemy Szoku Typu Drugiego.

A wszystko przez pewien prosty fakt. Staje się zresztą on tym bardziej oczywisty, im większą wiedzą na temat działania potoku graficznego dysponujemy. Ale nawet gdy zostaniemy już ekspertami od grafiki 3D, jest on – jak przypuszczam, rzecz jasna :) – wciąż irytujący i trudny do pogodzenia się. Co powoduje tego rodzaju rozterki egzystencjalne?…
To, że naraz można używać co najwyżej jednego shadera danego rodzaju (vertex lub pixel shadera). Tak, shader może być tylko jeden. Jeden, one, ein, un, uno, один, li pa (to ostatnie jest w lojbanie, rzecz jasna ;>). Dlatego właśnie nie istnieje jeden łatwy i szybki, a przede wszystkim ogólny sposób na to, by połączyć ze sobą dwie techniki, z których każda wymaga wykonania kawałka kodu na karcie graficznej dla wierzchołka i/lub piksela.

Nie znaczy to oczywiście, że sprawa jest beznadziejna – dowodem jest choćby to, że przecież gry 3D wciąż jakoś powstają ;) Różne rozwiązania są tu możliwe, jak choćby te opisane kiedyś przez Rega. Wahają się one na skali między złożonością i elastycznością, ale żadne z nich nie jest idealne.

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

Wiązania zmiennych w anonimowych delegatach

2010-03-24 17:10

Możliwość używania delegatów w C# to fajna rzecz. Przyjemne jest zwłaszcza definiowanie ich “w locie”, czyli bez konieczności tworzenia zupełnie nowej funkcji. Takiego delegata nazywamy wówczas anonimowym:

  1. btnOK.Click += delegate (object sender, EventArgs e) { MessageBox.Show("Bu!"); }

Przydaje się to zwłaszcza to podawana różnego rodzaju predykatów do funkcji sortujących lub wyszukujących. Takie nienazwane funkcje są zwykle krótkie i bardzo proste.

A co jeśli jest inaczej?… W szczególności interesująca sytuacja jest wtedy, gdy nasz delegat odwołuje się do zmiennej zewnętrznej – czyli takiej, która nie została w nim zadeklarowana, ale której zasięg zawiera definicję delegata. Oto przykład:

  1. public MainForm_Load(object sender, EventArgs e)
  2. {
  3.     int x; // to jest zmienna zewnętrzna dla poniższego delegata
  4.     // ...
  5.     btnFoo.Click += delegate (object sender, EventArgs e)
  6.         {
  7.             if ((int)(sender as Control).Tag < x)
  8.                 MessageBox.Show ("Bu!");
  9.         });
  10. }&#91;/csharp]
  11. Pomyślmy teraz, że kliknięcie na przycisk <code>btnFoo</code> na pewno nastąpi już po zakończeniu funkcji <code>MainForm_Load</code>. Zmienna <code>x</code> jest w niej zmienną lokalną... Czy to oznacza, że stanie się wtedy coś złego, bo delegat spróbuje odwołać się do wartości zmiennej, która już nie istnieje?
  12. Otóż nie; na szczęście nic złego się nie zdarzy. Kompilator potrafi wykryć taką sytuację zawczasu i przenieść zmienną zewnętrzną na zarządzaną stertę, gdzie czas jej życia może zostać wydłużony. Nawet jeśli jest to zmienna lokalna, będzie ona dostępna tak długo, jak długo chociaż jedno odwołanie do wykorzystującego ją delegata nie jest możliwe do uprzątnięcia przez <em>garbage collector</em>. Wszystko będzie więc dobrze i - co więcej - zupełnie przezroczyście: w powyższym przykładzie nie widać przecież żadnych oznak tego, że zmienna <code>x</code> jest w rzeczywistości znacznie mniej lokalna niż się na pierwszy rzut oka wydaje ;)
  13.  
  14. Widać zatem, że anonimowe delegaty w C# wiążą się ściśle ze swoimi zmiennymi zewnętrznymi, odwołując się do nich w razie potrzeby. Naturalnym efektem ubocznym jest fakt, że wobec tego wszystkie zmiany wartości zmiennych zewnętrznych są widoczne w kolejnych wywołaniach delegatów, które z nich korzystają:
  15. [csharp]delegate void Procedure();
  16. //...
  17. int i = 0;
  18. Procedure p = delegate() { MessageBox.Show(i.ToString()); };
  19. p();
  20.  
  21. i = 1;
  22. p();

Powyższy kod (skompilowany pod .NET co najmniej 3.0) pokaże 0 i 1, bowiem anonimowy delegat wiązany jest z samą zmienną i, nie zaś jej wartością w momencie definicji funkcji (czyli 0). Że zachowanie to nie jest znowu tak oczywiste, można uzasadnić podając przykład biblioteki Boost.Lambda dla C++, gdzie jest odwrotnie. Tam domyślnie wiązane są same wartości zmiennych zewnętrznych (w momencie tworzenia anonimowej funkcji), ale można to zmienić niewielkim wysiłkiem (używając modyfikatora var, jeśli kogoś to interesuje).
W C# podobnej możliwości nie ma, a do anonimowych delegatów wiązane są zawsze same zmienne, a nie ich wartości. Jeśli jednak potrzebowalibyśmy czegoś takiego, to prostym wyjściem jest wprowadzanie zmiennej pomocniczej, zainicjowanie jej wartością zmiennej pierwotnej i używanie jej wewnątrz delegatu:

  1. Procedure p;
  2. int i = 0;
  3. {
  4.     int j = i;
  5.     p = delegate() { MessageBox.Show(j.ToString()); };
  6. }
  7. p(); // 0
  8.  
  9. i = 1;
  10. p(); // również 0

Zarówno delegata, jak i deklarację owej zmiennej dobrze jest też zamknąć w osobnym bloku kodu – tak jak powyżej. Dzięki temu eliminujemy możliwość przypadkowej jej modyfikacji, która oczywiście zostałaby “zauważona” przez delegata.

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


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