Jak się robi screeny

2010-04-20 16:19

W robieniu zrzutów ekranowych (screenshotów) nie ma, wydawałoby się, nic nadzwyczajnego. Wciskamy po prostu klawisz Print Screen i obraz ekranu ląduje nam w systemowym Schowku, po czym możemy go użyć w dowolny sposób. Przynajmniej tego właśnie się spodziewamy zazwyczaj.
W rzeczywistości robienie screenów to dość śliski temat, jeśli mamy do czynienia z czymś więcej niż zwykłymi aplikacjami okienkowymi. Dotyczy to chociażby pełnoekranowych gier, korzystających z bibliotek graficznych typu DirectX i OpenGL. Wykorzystują one mechanizm znany jako hardware overlay, pozwalający - w skrócie - na ominięcie narzutu systemu operacyjnego związanego z zarządzeniem oknami wielu aplikacji na współdzielonym ekranie monitora. Skutkiem ubocznym jest między innymi to, że wbudowany w system mechanizm tworzenia zrzutów ekranu staje się w tej sytuacji bezużyteczny, jeśli chcemy wykonać screenshota tego rodzaju aplikacji.

Skąd w takim razie biorą się screeny, a nawet całe filmy ze współczesnych gier?... Cóż, istnieją oczywiście metody pozwalające na przechwytywanie obrazów generowanych przez aplikację - można bowiem skłonić ją do wywoływania naszej własnej wersji metody IDirect3DDeviceN::Present zamiast tej wbudowanej :) Brzmi to pewnie tajemniczo i nieco hakersko, ale tak mniej więcej działają programy do przechwytywania wideo typu Fraps.

Do zwykłych screenów zazwyczaj jednak przewiduje się odpowiednie rozwiązania w samych grach. Jednym ze sposobów jest, w przypadku wykrycia wciśnięcia klawisza odpowiadającego za screenshoty (czyli zwykle Print Screen), wyrenderowanie sceny najpierw do tekstury, a ją potem na ekran. Rzeczoną teksturę można później zapisać do pliku.


Aliasing (i nie tylko)
w pełnej krasie

Czy to dobre rozwiązanie? Otóż nie, i to aż z trzech powodów. Stosunkowo najmniej ważnym jest to, iż implementacja może być kłopotliwa, bo wymaga dobrania się do początku i do końca potoku renderingu w aplikacji z kolejnym render targetem. Bardziej istotny jest brak multisamplingu (a więc wygładzania krawędzi wielokątów znanego jako anti-aliasing) w powstałym obrazku. Nie będzie on więc wyglądać zbyt ładnie, już nie wspominając o tym, że nie będzie on odpowiadał temu, co widzimy na ekranie.

Jak więc należy robić screeny? Dokładnie tak, jak nam podpowiada intuicja: należy wziąć to, co widać na ekranie - czyli zawartość bufora przedniego (front buffer) i zapisać to do pliku. W DirectX mamy do tego metodę urządzenia o nazwie GetFrontBufferData, która pobiera nam to jako powierzchnię:

IDirect3DSurface9* screen;
dev->CreateOffscreenPlaneSurface(Width, Height, D3DFMT_A8R8G8B8,
    D3DPOOL_SCRATCH, &screen, NULL);
// screenshot i zapis go do pliku
dev->GetFrontBufferData(0, &screen);
D3DXSaveSurfaceToFile(ScreenFile, D3DXIFF_JPG, screen, NULL, NULL);

Ważne jest, by pamiętać o właściwych rozmiarach powierzchni - takich, jakie ma bufor przedni. W trybie pełnoekranowym odpowiada to buforowi tylnemu, ale dla aplikacji okienkowej wcale nie musi, jeśli rozmiar jej okna możemy zmieniać.

W OpenGL podobną rolę spełnia do GetFrontBufferData spełnia funkcja glReadPixels. Z zapisaniem pobranego obrazu może być tylko "nieco" trudniej ;-)

  • RSS
  • Facebook
  • Twitter
  • Wykop
  • Reddit
  • del.icio.us
  • Google Bookmarks

Cokolwiek, czyli Boost.Any

2010-04-17 11:52

Zasadniczo w C++ zmienne mają jednoznacznie określone typy, znane w czasie kompilacji. Istnieją oczywiście pewne mechanizmy, które zdają się tę zasadę lekko naciągać (dziedziczenie i polimorfizm, szablony), ale bez znaczącej nieścisłości można przyjąć, że jest ona prawdziwa. W języku kompilowanym nie jest to zresztą nic nadzwyczajnego.
Z kolei w językach skryptowych i interpretowanych dość powszechne jest używanie zmiennych bez określonego (tj. jawnie podanego w deklaracji) typu. To, co obecnie przechowuje liczbę, za chwilę może zawierać ciąg znaków czy odwołanie do obiektu i nie ma z tym specjalnego problemu. Typy w takich językach oczywiście istnieją, ale mają je wartości, nie zaś zmienne.

Czasami coś podobnego - czyli zmienna mogąca przechowywać wartości różnego rodzaju - przydaje się i w C++. Wtedy niektórzy przypominają sobie o void*, ale "ogólny wskaźnik na coś" rzadko jest w tym przypadku szczytem marzeń. Jego podstawową wadą jest to, że wymaga on przechowania gdzieś poza nim informacji o docelowym typie wartości, na którą pokazuje - jeśli chcemy ją później wykorzystać, rzecz jasna. Równie poważną jest fakt, że mamy tu do czynienia z wskaźnikiem; pamięć, na którą on pokazuje, musi być kiedyś zwolniona.
Dlatego też lepszym rozwiązaniem jest biblioteka Any z Boosta, umożliwiająca korzystanie ze zmiennych "typu dowolnego". Reprezentuje go klasa boost::any, mogąca przechowywać wartości prawie dowolnego rodzaju (wymaganiem jest głównie to, by ich typy posiadały zwykły konstruktor kopiujący):

#include <boost/any.hpp>
boost::any any1 = 5, any2 = std::string("Ala ma kota");
boost::any foo = Foo(); // o ile Foo da się kopiować

Ponieważ jednak jest to wciąż C++, a nie język skryptowy, do wartości zapisanych w obiekcie any bezpośrednio dostać się nie da. W szczególności nie można liczyć na niejawne konwersje powyższych zmiennych do typów int, string czy Foo. Zamiast tego korzysta się ze specjalnego operatora rzutowania any_cast, który pozwala na potraktowanie wartości zapisanej w any zgodnie z jej właściwym typem:

int x = 1 + boost::any_cast<int>(any1);

W przeciwieństwie do void*, próba jej reinterpretacji na inny typ danych skończy się wyjątkiem. Nie trzeba jednak polegać na jego łapaniu, jeśli docelowego typu nie jesteśmy pewni: boost::any pozwala też pobrać jego type_info (tj. to, co zwraca operator typeid) bez dokonywania rzutowania.

I to w sumie wszystko jeśli chodzi o tę klasę, a jednocześnie i całą bibliotekę. Mimo swej niewątpliwej prostoty jest ona bardzo przydatna tam, gdzie potrzebujemy zmiennych przechowujących różne rodzaje wartości. Warto więc się z nią zapoznać :)

  • RSS
  • Facebook
  • Twitter
  • Wykop
  • Reddit
  • del.icio.us
  • Google Bookmarks

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ą.

Screen z Tower Offence

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ć :)

Plik: [2010-04-11] Tower Offence  [2010-04-11] Tower Offence (5,7 MiB, 160 ściągnięć)

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.

  • RSS
  • Facebook
  • Twitter
  • Wykop
  • Reddit
  • del.icio.us
  • Google Bookmarks
Tagi:
Autor: Xion w Gry, Wydarzenia » Komentarze (5)

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.

Lich King - screenshot

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.

  • RSS
  • Facebook
  • Twitter
  • Wykop
  • Reddit
  • del.icio.us
  • Google Bookmarks

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)

  • RSS
  • Facebook
  • Twitter
  • Wykop
  • Reddit
  • del.icio.us
  • Google Bookmarks
Tagi: ,
Autor: Xion w Wydarzenia » Komentarze (2)

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:

#define FUNCTOR(name,params,code) class name { \
    public: \
        void operator()(params) \
            code \
};
// disclaimer: nie należy zakładać, że powyższe makro
// 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;

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.

  • RSS
  • Facebook
  • Twitter
  • Wykop
  • Reddit
  • del.icio.us
  • Google Bookmarks
Tagi:
Autor: Xion w Programowanie » Komentarze (3)

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ć!

  • RSS
  • Facebook
  • Twitter
  • Wykop
  • Reddit
  • del.icio.us
  • Google Bookmarks
 



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