wtorek, 1 września 2009

Mazanie po murach i inne zabawy

Ostatnio sporo czasu spędzam na oglądaniu produkcji niezależnych twórców, w poszukiwaniu inspiracji / pomysłu / weny - niepotrzebne skreślić. W moje ręce wpadła gierka, która wygrała w kategorii "Best Student Game" na Independent Games Festival 2009, pod złowieszczym tytułem "Tag: The Power of Paint". Po oglądnięciu screenów stwierdziłem, że to jakaś lipa - kiepsko (wręcz obciachowo) wyglądający cel shade'owy FPS, w dodatku nie widać przeciwników. No ale przecież zajął 1 miejsce w jakiejś kategorii, więc nie może być tak źle - ściągnąłem i... zakochałem się, genialna gra. Jest to bardzo nietypowe połączenie FPSa i gry logicznej - gatunków biegunowych. Do dyspozycji mamy 3 bronie - z zieloną, czerwoną i niebieską farbą oraz czwarte psikadełko - do zamazywania, coś jak gumka do ścierania. Każda farba ma pewne właściwości, gdy wejdziemy na pomalowaną nią podłogę... ścianę lub sufit. Zielona - powoduje, że nasza postać skacze, czerwona - że biegnie, niebieska - że przykleja się do ścian (dlatego można łazić po sufitach). Ogólnie chodzi o to, żeby tak bazgrać po planszy, żeby dostać się do punktu docelowego. Momentami trzeba sporo się pogłowić, popróbować różnych rozwiązań. Dodatkowo element zręcznościowy powoduje, że choć wiemy jak przeskoczyć z dachu na dach, to musimy do tego podchodzić kilka razy. No i jeszcze to łażenie po ścianach - kto grał w AVP już to przechodził, ale tutaj jest jakoś bardziej zwariowanie, momentami naprawdę mocno gubi się orientację przestrzenną. Warto zagrać. A jeszcze co ciekawe, w assetach znajdują się nieskompilowane shadery i oskryptowanie całej gry w XMLu, więc można ją sobie troszkę pozmieniać bez grzebania w binarkach.

Przy okazji wspomnę jeszcze o 2 gierkach z tego samego festiwalu. Pierwsza to rewelacyjny pomysł na gameplay - Snapshot Możemy robić screeny różnych przedmiotów (np. słonia, drzwi), potem umieszczać te screeny w jakimś miejscu, przenosząc w ten sposób owe przedmioty - np. stawiamy screena ze słoniem przy jakimś pudle, dzięki czemu słoń je przepchnie, albo drzwi na jakiejś ścianie, dzięki czemu będziemy mogli przez nią przejść. Dodatkowo klimatem przypomina troszkę czasy ośmiobitowców. Druga - FEIST spodobała mi się po prostu pod względem artystycznym, niesamowity klimat, zazdroszczę umiejętności grafikowi i designerowi (takie przeciwieństwo wspomnianego wyżej Tag :D).

A, jeszcze może wypada wspomnieć - samo IGF jest eventem odbywającym się w trakcie GDC. Wspomniana edycja odbyła się już dość dawno, w marcu. Mając fajny projekt na pewno warto się zgłosić, bo nagrody pieniężne są całkiem pokaźne, no i ten prestiż :P

środa, 12 sierpnia 2009

printf - based - listener

Potrzebowałem zrobić listener dowolnej ilości zmiennych oparty na funkcji o prototypie podobnym do

int printf ( const char * format, ... );

wyzwalany jakąś funkcją (w szczególnym przypadku callbackiem). Na przykład wyświetlający informacje typu FPS w grze, albo jako reakcja na event czy przychodzący z sieci pakiet. Dla uproszczenia tej notki załóżmy, że ma obsługiwać jedynie inty i floaty. Można oczywiście napisać wielką funkcję, w której w zależności od jakiejś zmiennej będziemy wywoływać w switchu printf(napis, int1), printf(napis, int1. int2) etc. aż do 1000 zmiennych różnych typów, czego na pewno nikt nie przekroczy. Ale można też to zrobić inaczej. Moje rozwiązanie jest proste - podajmy parametry bezpośrednio z poziomu asma. W dokumentacjach można wyczytać jak VS (w moim przypadku, bo go używam) wywołuje funkcje (calling convention), jednak warto to sprawdzić w praktyce.

Na początek robimy sobie małą aplikację testową:

#include
int main() {
printf("printf call %d %f\n", 1, 2.0f);
return 0;
}

Kompilujemy, odpalamy, i (o dziwo) na ekranie wyświetla się napis „printf call 1 2.000000”. Podejrzyjmy wygenerowaną binarkę w desassemblerze, np. w IDA (program jest dostępny w wersji Freeware – troszkę ograniczonej, ale dla przeciętnego człowieka oferuje i tak o wiele za dużo). Interesuje nas poniższy fragment (jeśli nie umiesz znaleźć go w swoim disasmie, warto się pouczyć podstaw RE, choćby z [1] czy [2], czasem się przydają nawet jeśli nie chcemy być RE).


.text:004113BE mov esi, esp
.text:004113C0 sub esp, 8
.text:004113C3 fld ds:dbl_415758
.text:004113C9 fstp [esp+0D4h+var_D4]
.text:004113CC push 1
.text:004113CE push offset aPrintfCallDF
.text:004113D3 call ds:printf
.text:004113D9 add esp, 10h

Jak widać parametry funkcji wrzucane są na stos od końca. Czyli zgodnie z opisem w dokumentacji. Jeśli chodzi o inty sprawa jest banalna - znany wszystkim push wartości. Więc np. zamiast

int i = 1;
int j = 2;
printf("printf call %d %d\n", i, j);

możemy zrobić równoważne:

int i = 1;
int j = 2;

__asm {
push j
push i
}

printf("printf call %d %d\n");

int add_ = 8;
__asm add esp, add_

Na początku wrzucamy zmienne na stos zgodnie z konwencją kompilatora, potem robimy call i na końcu zwiększamy wskaźnik stosu o tyle o ile zmniejszyliśmy go niejawnie przy pushowaniu. Jaki z tego jest zysk? Ano taki:

int tablica[5] = {1, 2, 3, 4, 5};

for(int i = 4; i >= 0; --i) {
int k = tablica[i];
__asm push k
}

printf("printf call %d %d %d %d %d\n");

int add_ = 4 * 5;
__asm add esp, add_

A co z floatami? FLD wrzuca float’a na stos FPU, natomiast FSTP kopiuje pierwszą wartość z tego stosu pod wskazany adres, po czym wykonuje POPa (btw. jest też wersja bez POPa - FST). Trzeba też pamiętać, że potrzebujemy aż 64 bitów.

float f = 3.14f;
int floatSize = 8;
__asm {
fld f
sub esp, floatSize
fstp qword ptr [esp]
}
printf("float = %f\n");
__asm add esp, floatSize

Samego listenera można teraz zaimplementować na tyle sposobów ilu jest koderów. W moim przypadku cały snippet wygląda tak:

#include
#include
#include

class PrintfListener {
enum Type {
T_INT,
T_FLOAT
};

struct Value {
Type type;
union V_ {
int* i;
float* f;
} val;

Value(int* i) { type = T_INT; val.i = i; }
Value(float* f) { type = T_FLOAT; val.f = f; }
};

std::vector value;
std::string str;
public:
void addParam(int* i) { value.push_back(Value(i)); }
void addParam(float* f) { value.push_back(Value(f)); }
void setString(const std::string& s) { str.assign(s); }

void use() {
std::vector::iterator it = value.end();
int totalSize = 0;
while(true) {
--it;

switch(it->type) {
case T_INT: {
int v = *(it->val.i);
__asm push v
totalSize += 4;
} break;
case T_FLOAT: {
float v = *(it->val.f);
__asm {
fld v
sub esp, 8
fstp qword ptr [esp]
}
totalSize += 8;
} break;
}

if(it == value.begin()) break;
}

printf(str.c_str());
__asm add esp, totalSize
}
};

int main() {
int i = 0;
float f = 0.0f;

PrintfListener listener;
listener.addParam(&i);
listener.addParam(&f);
listener.setString("i = %d, f = %f\n");

listener.use();

i = 10;
f = 3.14f;
listener.use();

return 0;
}

Ok, a jak to ugryźć inaczej? Można np. zastosować STLowy string stream i do printfa przekazywać gotowy poskładany string - ale wtedy właściwie tracimy największą zaletę formatującego printfa, więc przy takim podejściu byśmy już pewnie użyli iostream na konsoli i napisali inną funkcję wypisującą w przypadku aplikacji graficznej. Jak ktoś ma inne genialne sposoby (może da się to zrobić dużo prościej?) to będę wdzięczny za wszelkie info :)

[1] ReverseCraft by Gynvael – dobre źródło dla początkujących z RE: http://re.coldwind.pl/
[2] Introduction to Reverse Engineering Software by Mike Perry - świetne źródło, troszkę wiekowe, dla początkujących z RE, bardzo dużo informacji od podszewki: http://www.acm.uiuc.edu/sigmil/RevEng/

poniedziałek, 1 czerwca 2009

Birthday demo

This piece of demoscene was created for my friend, Kasia Wasak, on her 22nd birthday. There were some typical demoscene effects included, like growing weeds and moving spheres with environment mapping using dynamic cube-mapping technique.





sobota, 25 kwietnia 2009

Metaballs 2D

Just a demoscene effect, kind of presentation. But nothing big. In this small project I created dynamic metaballs (isosurfaces) and synchronized it with Drum & Bass music in the background. There were also some fancy electrical breakdowns in this demo, that occured when one metaball was close to another, and the other was then attracted by the first one. One of the metaballs was controlled by input device (mouse). And there were also some equalizers in this demo ;) Everything was created in C++ and CG (all metaball stuff is calculated on the GPU, with some simple shader).


sobota, 28 marca 2009

Don't Kill The Babcia (Granny)

The game was created in 8 hours during 6th Conference of Games Engineering in Siedlce, Poland. Compo theme was "psychodelic game". Our team was: me (Sobol, most of the code), Słoń (code & gfx), Koshmaar (some code & gfx & sfx) and Artpoz (code & sfx & gfx).

Our entry was and MMO game (yeah, right - Massive Multiplayer Offline :P), because up to 24 people could could play on one keyboard. The goal of the game is NOT to kill grannies, that are talking some bullshit and swearing at you. Sometimes granny takes a white rabbit out of her pocket and throws it at you. You have to press your key then to block it, otherwise you loose frag. Sometimes granny tries to frighten you by taking the rabbit, but not throwing. If you press the key in such a moment, you loose frag, because granny dies. Once every couple of seconds, there is a keybord shuffle, and you have to find your new key.

We placed 8th out of 9 teams. That's not too good, I agree. But the main problem in my opinion is that we were not able to present our game to the public, because they didn't understand what we were doing. In my opinion this game was kind of fun :)



środa, 24 grudnia 2008

SA4011, Mirco Machines remake

This game was created as a term project for "Advanced C++" classes on WEiTI, Warsaw University of Science. The game was supposed to be a crazy variation over Codemasters Micro Machines. We created a game that several people could play over TCP/IP connection, and got the highest mark possible (5). You could choose between 6 unique vehicles (driven by me and my 5 friends). Unfortunately it's too bugy to release on the internet. 





Our team was:

  • Michał "Sobol" Sobiecki (me)
  • Maciek "Słoń" Stefańczyk
  • Kacper "Szkudi" Szkudlarek

niedziela, 18 maja 2008

Think, 4k intro

Yeah, what can I say? Pure demoscene. This 4k intro (size limited to 4kb) was my entry for gamedev.pl demoscene compo, on may 2008, where I took 2nd place. Video rip was placed on YouTube, and there was also some entry on Pouet.