Programowanie z DirectX dla chŕtnych, Programowanie z DirectX dla chętnych


Artur Poznański

PROGRAMOWANIE

GIER

Z DIRECTX

DLA CHĘTNYCH

Kraków 2001

1. WPROWADZENIE

Jak wynika z tytułu zajmę się programowaniem gier z użyciem biblioteki Microsoftu o nazwie DirectX. Temat jest dość poważny, będzie więc o stopień trudności wyżej niż w poprzednim tekście. Tradycyjnie zacznę od wymagań stawianych czytelnikom:

Pokrótce omówię te punkty.

  1. Chcąc programować poważne gry, trzeba znać ten język. C++ stał się praktycznie standardem przemysłowym.

  2. Większość gier chodzi pod Windows (95/98/Me), więc trzeba orientować się jak działają aplikacje pod tym systemem. Polecam zapoznanie się z moim tekstem pt: „Programowanie w Windows 95 dla chętnych”.

  3. Obsługa kompilatora to kolejny wymóg. Podstawowe wskazówki można znaleźć w tekście podanym powyżej.

  4. Najnowszą wersję biblioteki DirectX można ściągnąć ze strony WWW Microsoftu. Wersja 8.0 znajduje się na styczniowym kompakcie miesięcznika PC Chip (1/2001).

  5. Większość komentarzy, nazwy zmiennych oraz funkcji w plikach są po angielsku

(własne - po polsku)

Jeszcze na koniec moja konfiguracja:

2. INSTALACJA DIRECTX

Po zainstalowaniu DirectX SDK w każdym projekcie VC++ trzeba dodać:

  1. ścieżki do katalogów (Tools ->Options -> Directories i np. C:\MSSDK\LIB oraz C:\MSSDK\INCLUDE)

  2. odpowiednie biblioteki (Project ->Settings -> Link, w okno Object/Library modules wpisać ddraw.lib, dxguid.lib).

3. WYPISANIE TEKSTU NA EKRANIE

Nasz pierwszy przykład wypisze tradycyjne „Witaj mistrzu!” na środku ekranu. Wpierw jednak ustawimy rozdzielczość 640x480 i głębię kolorów na 16. Co to znaczy 640x480 ? Rozdzielczość ekranu jest to ilość pikseli w poziomie ekranu na ilość pikseli w pionie. Inne typowe rozdzielczości to: 320 x 200, 800 x 600, 1280 x 1024 itp.

Głębia kolorów to ilość bitów użytych na jeden kolor piksela. Może być:

Rozpakuj plik przyk1.zip dołączony do tekstu. Znajdziesz tam 4 pliki

Po utworzeniu projektu, dodaniu tych plików, kompilacji i uruchomieniu pojawi się nasz znany napis. Koniec następuje po naciśnięciu klawisza ESCAPE.

4. KRÓTKI WSTĘP DO DIRECTX

Jak już wspomniałem, DirectX stanowi zbiór bibliotek firmy Microsoft. Pozwala na szybszy dostęp do urządzeń takich jak karta graficzna, dźwiękowa, a także niezależność od sprzętu. W skład wchodzą takie elementy jak (wersja 7.0):

5. DIRECTDRAW I POWIERZCHNIE

Omówię jedynie główną ideę tworzenia animacji przy użyciu DirectDraw. Tworzymy dwa obszary pamięci na obraz ekranu. Są to tzw. powierzchnie. Ich nazwy to:

Cała sztuka płynnej animacji polega na szybkiej zamianie tych dwóch powierzchni za pomocą funkcji Flip. Zajrzyj do funkcji Game_Main, a zobaczysz coś takiego:

(...)

hRet = G.lpDDSBack->GetDC(&hDC); // pobieramy kontekst urządzenia

if (FAILED(hRet)) return; // dla tylnego bufora

SetBkColor(hDC, RGB(0,0,0)); // ustawiamy kolor tła dla tekstu

SetTextColor(hDC, RGB(255, 255, 255)); // ustawiamy kolor tekstu

TextOut(hDC, 270, 200, "Witaj mistrzu!", 14); // wyświetlamy tekst

G.lpDDSBack->ReleaseDC(hDC); // zwalniamy kontekst urządzenia

// dla tylnego bufora

while (TRUE)

{

hRet = G.lpDDSPrimary->Flip(NULL, 0); // tu zamieniamy tylny

if (hRet == DD_OK) break; // bufor z powierzchnią

if (hRet == DDERR_SURFACELOST) // podstawową czyli go

{ // wyświetlamy na ekranie

(...)

ĆWICZENIA-1

Warto teraz przejść do własnych eksperymentów z kodem. Oto dwa zadania jakie wymyśliłem. Dla wprawy postaraj się je wykonać:

  1. Zmień wyświetlany tekst (np. „ESC - wyjście”), oraz kolor (np. na żółty), na koniec ustaw napis w lewym górnym rogu ekranu

  2. Przestaw rozdzielczość na 800x600 i 8-bitowy kolor.

6. ODMIERZANIE CZASU

Ważną rzeczą jest to, aby gra chodziła z tą samą prędkością na szybkich jak i na wolniejszych komputerach. Jak można odmierzać czas, pokażę na następnym przykładzie. Zmodyfikujmy nieco kod, aby nasz słynny napis co sekundę zmieniał swój kolor. Nowy kod jest w przyk2.zip. Poniżej znajduje się interesujący nas fragment pliku GameMain.cpp.

(...)

int r,g,b; // zmienne na losowe składowe koloru

bool bStart = false; // kiedy rusza timer

DWORD dwTimer; // tu jest nasza zmienna

void Game_Main()

{

HRESULT hRet;

HDC hDC;

if (!bStart)

{

dwTimer = GetTickCount();// Pierwsze pobranie czasu

bStart = true;

r = g = b = 255; // Początkowy kolor (biały)

}

if (G.bQuitting) return;

EraseBackground();

if (GetTickCount()>=dwTimer+1000) // 1000 ms = 1 sekunda

{

r = rand() % 255; // losowanie składowej czerwonej

g = rand() % 255; // zielonej

b = rand() % 255; // i niebieskiej

dwTimer = GetTickCount(); // nowe pobranie czasu

}

(...)

Funkcja GetTickCount podaje nam w milisekundach czas od momentu uruchomienia Windows. Wpierw pobieramy dzięki niej czas, a potem sprawdzamy aktualny z poprzednim powiększonym o 1000 milisekund, czyli jedną sekundę. Następnie funkcją rand losujemy sobie liczby z zakresu 0 do 255 będące składowymi koloru. Na koniec ponownie pobieramy aktualny czas po czym następuje rysowanie napisu.

ĆWICZENIA-2

Poniżej przedstawiam przykładowe zadania do wykonania.

  1. Spraw by napis pojawiał się co pół sekundy, w różnych miejscach ekranu.

  2. Wypisz na ekranie licznik i zmniejszaj jego wartość co sek. z 10 do 0, po czym niech

program zakończy działanie.(*) (* - trudniejsze)

7. OBLICZANIE FPS

Czeka na nas kolejne zadanie. Spróbujmy sprawdzić jak szybko odświeżany jest obraz, czyli ile klatek na sekundę jest aktualnie wyświetlanych. Dodaj trzy zmienne typu int: sekundy , klatki i fps. Następnie przerób nieco poprzedni kod na poniższy (przyk3.zip):

(...)

if (!bStart)

{

dwTimer = GetTickCount();

bStart = true;

sekundy = klatki = 0; // zerujemy początkowe wartości

}

if (G.bQuitting) return;

EraseBackground();

if (GetTickCount()>=dwTimer+1000) // 1000 ms. = 1 sekunda

{

if (sekundy < 10) // liczymy do 10

sekundy++;

else

{

sekundy = 0;

klatki = 0;

}

if (sekundy!=0 && klatki!=0)

fps = klatki/sekundy; // fps = ang. frames per second

dwTimer = GetTickCount();

}

klatki++;

(...)

Należy również zmienić sposób wyświetlania. szText jest 80-elemetnową tablicą znaków.

(...)

wsprintf(szText, "FPS: %d", fps);

TextOut(hDC, 10, 10, szText, strlen(szText));

(...)

Im więcej klatek na sekundę (FPS) tym lepiej, chociaż płynna animacja jest już przy ok. 30 FPS. Zmień rozdzielczość na wyższą, a przekonasz się, że FPS nieco zmaleje. Włącz w tle jakiś program (np. WinAmp) i zobacz jak zmieni się ta wartość.

ĆWICZENIA-3

  1. Dodaj wyświetlanie sekund oraz ilości klatek.

  2. Zwiększ dokładność wartości FPS do dwóch miejsc po przecinku.

8. PROSTA ANIMACJA

Tym razem zrobimy prostą animację polegającą na tym, że po ekranie będzie poruszał się biały kwadrat (16x16 pikseli). Będzie on odbijał się od ścian ekranu jak piłka. (przyk4.zip):

(...)

if (!bStart) // początkowe wartości

{

pilka.x = SCREEN_WIDTH/2-SZEROKOSC_PILKI/2;

pilka.y = SCREEN_HEIGHT/2-WYSOKOSC_PILKI/2;

lot_pilki = DOL_PRAWO;

bStart = true; // ruszyliśmy

}

if (G.bQuitting) return;

EraseBackground();

porusz_pilka();

hRet = G.lpDDSBack->GetDC(&hDC); // pobieramy kontekst urządzenia

if (FAILED(hRet)) return; // dla tylnego bufora

Rectangle(hDC, // rysujemy prostokąt

pilka.x, // lewy górny róg

pilka.y,

pilka.x+SZEROKOSC_PILKI, // prawy dolny róg

pilka.y+WYSOKOSC_PILKI);

G.lpDDSBack->ReleaseDC(hDC); // zwalniamy kontekst urządzenia

// dla tylnego bufora

(...)

Stworzyliśmy funkcję porusz_pilka, która wywołuje jedną z czterech funkcji w zależności od wartości zmiennej lot_pilki. W pliku globals.h dodaliśmy trzy nowe stałe:

(...)

#define SZEROKOSC_PILKI 16

#define WYSOKOSC_PILKI 16

#define SPEED 2

(...)

Stała SPEED określa nam o ile pikseli ma się przesunąć kwadrat co klatkę.

ĆWICZENIA-4

  1. Zmień wielkość piłki oraz zwiększ jej prędkość.

  2. Dodaj jeszcze dwie piłki (*)

9. WYŚWIETLANIE BITMAP

Biały kwadrat zastąpimy niebieską kulką. Jej wygląd będzie znajdował się w pliku Resource.bmp. Rozpakuj plik przyk5.zip. Są tam trzy nowe pliki:

W dwóch poprzednich zaszły zmiany

do niej pliku Resource.bmp

Główna zmiana w funkcji Game_Main wygląda tak:

(...)

EraseBackground();

if (G.bQuitting) return;

porusz_pilka();

// rysujemy piłkę

G.lpDDSBack->BltFast(pilka.x,pilka.y,G.lpDDSRes,NULL,

DDBLTFAST_NOCOLORKEY | DDBLTFAST_WAIT);

(...)

Pierwsze dwa parametry funkcji BltFast określają, gdzie funkcja ma rysować (podajemy współrzędne piłki). Kolejne dwa określają, co funkcja ma rysować. Nasza bitmapa jest załadowana na trzeciej powierzchni i jej nazwę podaliśmy jako trzeci parametr. Czwartym jest rozmiar ładowanej powierzchni, ale my chcemy załadować całość więc daliśmy NULL. Podsumowując można stwierdzić, że nasz obrazek jest ładowany w trzech etapach.

  1. z pliku do powierzchni zasobów (tej trzeciej)

  2. z powierzchni zasobów do tylnego bufora

  3. z tylnego bufora do powierzchni podstawowej.

ĆWICZENIA-5

  1. Zmień wygląd piłki

  2. Stwórz drugą, własną bitmapę i pozamieniaj w kodzie nazwy pliku na utworzony.

10. OBSŁUGA KLAWISZY

Dodajmy dwie paletki (jako białe prostokąty) oraz sterowanie ich położeniem za pomocą funkcji DirectInput (przyk6.zip). Lewą paletką można sterować klawiszami [A] oraz [Z], zaś prawą [UP] (Góra) i [DOWN] (Dół). Projekt wymaga dołączenia m.in. biblioteki dinput.lib. W pliku InitTerm.cpp znajdują się nowe funkcje tworzące i usuwające obiekty DirectInput. W WinBase.cpp dodano jedną linijkę :

(...)

G.hInstance = hInstance;

(...)

Obsługą klawiatury zajmuje się w naszym programie nowa funkcja odczyt_znaku

(...)

/////////////////////////////////////////////////////////////////

// odczyt_znaku

//

// pobiera znak i zmienia położenie paletek dla klawiszy A,Z,UP,DOWN

void odczyt_znaku()

{

HRESULT hRet;

// pobranie znaku z klawiatury

while (hRet = G.lpDIKeyboard->GetDeviceState(256, G.KeyState)

== DIERR_INPUTLOST)

{

if (FAILED(hRet = G.lpDIKeyboard->Acquire())) break;

}

if (KEYDOWN(DIK_A))

{

if (lewa.y>0)

lewa.y-=SPEED; // lewa paletka jedzie do góry

}

if (KEYDOWN(DIK_Z))

{

if (lewa.y<SCREEN_HEIGHT-WYSOKOSC_PALETKI)

lewa.y+=SPEED; // lewa paletka jedzie na dół

}

if (KEYDOWN(DIK_UP))

{

if (prawa.y>0)

prawa.y-=SPEED; // prawa paletka jedzie do góry

}

if (KEYDOWN(DIK_DOWN))

{

if (prawa.y<SCREEN_HEIGHT-WYSOKOSC_PALETKI)

prawa.y+=SPEED; // prawa paletka jedzie do na dół

}

}

(...)

Nazwy klawiszy takich jak DIK_UP, DIK_DOWN i inne, można znaleźć w pliku dinput.h

ĆWICZENIA-6

  1. Zmień przyciski A, Z na S i X

  2. Dodaj przesuwanie paletek w poziomie.

11. WYŚWIETLANIE DUSZKÓW

Duszek (ang. sprite) to mały obiekt, będący fragmentem bitmapy. Do naszej bitmapy dodamy wygląd paletek i potraktujemy ją jako zbiór trzech duszków (przyk7.zip). Wpierw zwiększamy rozmiary obrazka w pliku Resource.bmp i dorysowujemy dwie paletki.

(...)

// prawa paletka

rectSource.left = 8;

rectSource.top = 16;

rectSource.right = rectSource.left+SZEROKOSC_PALETKI;

rectSource.bottom = rectSource.top+WYSOKOSC_PALETKI;

G.lpDDSBack->BltFast(prawa.x,prawa.y,G.lpDDSRes, &rectSource,

DDBLTFAST_NOCOLORKEY | DDBLTFAST_WAIT);

// rysujemy piłkę

rectSource.left = 0;

rectSource.top = 0;

rectSource.right= rectSource.left+SZEROKOSC_PILKI;

rectSource.bottom=rectSource.top+WYSOKOSC_PILKI;

G.lpDDSBack->BltFast(pilka.x,pilka.y,G.lpDDSRes, &rectSource,

DDBLTFAST_NOCOLORKEY | DDBLTFAST_WAIT);

(...)

W funkcji Game_Main tworzymy zmienną rectSource, która jest obiektem struktury RECT o czterech polach. Następnie wypełniamy te pola w zależności od położenia naszego duszka w bitmapie. Na koniec jako czwarty parametr w funkcji BltFast umieszczamy adres obiektu rectSource.

ĆWICZENIA-7

  1. Zmień wygląd paletek (kształt, kolory itp.)

  2. Zamień w pliku Resource.bmp położenie paletek z położeniem piłki

12. DETEKCJA KOLIZJI

Sprawimy, by piłka odbijała się tylko od paletek. Ponadto dodamy liczenie punktów i losowy wybór początkowego kierunku lotu piłki (przyk8.zip). Nowe funkcje to

Nowymi zmiennymi przechowującymi stan punktowy są punkty_lewego, punkty_prawego. Funkcje rysujące paletki i piłkę umieściliśmy w funkcji rysowanie. Poniżej znajduje się przykładowa funkcja do obsługi kierunku góra-lewo.

(...)

void gora_lewo()

{

// zakres lotu

if ((pilka.y>0) && (pilka.x>0))

{

pilka.x-=SPEED;

pilka.y-=SPEED;

}

// odbicie od sciany

if (pilka.y<=0)

lot_pilki = DOL_LEWO;

// od paletki

if (pilka.x<=SZEROKOSC_PALETKI

&& pilka.y>=lewa.y-WYSOKOSC_PILKI/2

&& pilka.y<=lewa.y+WYSOKOSC_PALETKI-WYSOKOSC_PILKI/2)

lot_pilki = GORA_PRAWO;

}

(...)

Określanie strefy odbić odbywa się tak jak na rysunku poniżej

0x08 graphic
Rys.1 Zakres odbić piłki od lewej paletki

ĆWICZENIA-8

1) Zmień wygląd punktacji

2) Dodaj różne kąty odbić piłki w zależności od miejsca uderzenia w paletkę (*)

13. TŁO I KOLOR KLUCZOWY

Następny przykład pokaże, jak dodać do naszego tenisa własne tło. Zmienimy też sposób wyświetlania duszków. (przyk9.zip) Wpierw tworzymy jeszcze jedną powierzchnię i ładujemy do niej przygotowany wcześniej obrazek, przedstawiający boisko o wymiarach 648x480 pikseli. Zauważ, że nie musimy już używać funkcji EraseBackground do mazania tła. Aby nie było widać czarnych rogów w naszych duszkach, musimy je przesyłać z kolorem kluczowym. Kolor kluczowy jest to wartość, która nie ma być przesyłana przy wyświetlaniu duszka. W pliku InitTerm.cpp ustawiliśmy kolor kluczowy jako czarny. Dzięki temu czarne obszary duszków będą wyglądały jak gdyby były przeźroczyste.

(...)

DDCOLORKEY ckey;

ckey.dwColorSpaceHighValue = ckey.dwColorSpaceLowValue = 0;

G.lpDDSRes->SetColorKey(DDCKEY_SRCBLT, &ckey);

(...)

Samo wyświetlanie różni się tylko jednym słowem. W funkcji BltFast podajemy zamiast DDBLTFAST_NOCOLORKEY parametr DDBLTFAST_SRCCOLORKEY

(...)

G.lpDDSBack->BltFast(pilka.x,pilka.y,G.lpDDSRes, &rectSource,

DDBLTFAST_SRCCOLORKEY | DDBLTFAST_WAIT);

(...)

Ostatnią poprawką była zmiana koloru tła dla wyświetlania stanu punkowego w funkcji rysuj_wynik.

ĆWICZENIA-9

  1. Zmień wygląd boiska

  2. Zmień kolor kluczowy z czarnego na biały

14. KĄTY ODBIĆ I PAUZA

Aby nieco urozmaicić grę, dodamy różne kąty odbić w zależności od części paletki, którą odbijamy piłkę. Drugą dodaną rzeczą będzie pauza wywoływana klawiszem [P] (przyk10.zip). Pierwszą czynnością jest zastąpienie czterech funkcji gora_lewo, gora_prawo, dol_lewo, dol_prawo jedną o nazwie kierunek. Pola x i y obiektu lot przechowują kierunek lotu. Piłka może dzięki temu lecieć w 12 różnych kierunkach jak na rysunku poniżej.

0x08 graphic

Rys.2 Wartości zmiennych lot.x i lot.y w zależności od kierunku lotu piłki

A oto jak przedstawiają się strefy odbić na paletce.

0x08 graphic
Rys.3 Strefy odbić na lewej paletce.

Zatrzymanie gry zrobiliśmy dodając obsługę klawisza [P].

(...)

if (KEYDOWN(DIK_P))

{

if (bPauseToggle == false)

{

bPauseToggle = true;

if (G.bPauza)

G.bPauza = false;

else

G.bPauza = true; // włączono pauzę

pauza(); // napis (PAUZA)

}

}

else bPauseToggle = false;

(...)

Zmienna bPauseToggle przechowuje stan klawisza [P] (wciśnięty = true, nie = false).

Funkcja pauza wypisuje na środku ekranu żółty napis (--PAUZA--).

ĆWICZENIA-10

  1. Zmień wyświetlanie punktów na wyświetlanie kierunków lotu.

  2. Zmień prędkość poruszania się paletek.

15. STRONA TYTUŁOWA

Jak łatwo zgadnąć gra jest klonem Ponga (1972 r.), więc nazwiemy ją po prostu Ping-Pong. Podzielmy naszą grę na cztery części:

Gotowy kod znajduje się w pliku przyk11.zip. Tworzymy zmienną SG (Stan Gry) i wypełniamy ją jedną z czterech możliwych wartości o nazwach takich jak nasze części.

(...)

if (G.SG == MENU)

strona_tytulowa();

if (G.SG == KONIEC_GRY)

strona_koncowa();

if (G.SG == STARTUJEMY) // początkowe wartości

{

pilka.x = SCREEN_WIDTH/2-SZEROKOSC_PILKI/2;

pilka.y = SCREEN_HEIGHT/2-WYSOKOSC_PILKI/2;

lewa.x = 0;

lewa.y = SCREEN_HEIGHT/2-WYSOKOSC_PALETKI/2;

prawa.x = SCREEN_WIDTH-SZEROKOSC_PALETKI;

prawa.y = SCREEN_HEIGHT/2-WYSOKOSC_PALETKI/2;

punkty_lewego = 0;

punkty_prawego = 0;

losuj_kierunek();

G.bPauza = false;

G.SG = BIEGNIEMY; // ruszyliśmy

} // STARTUJEMY

if (G.SG == BIEGNIEMY)

{

if ((punkty_lewego==15) || (punkty_prawego==15))

{

G.SG = KONIEC_GRY;

}

(...)

Nowe funkcje strona_tytulowa i strona_koncowa wypisują odpowiedni tekst i czekają na klawisz spacji.

ĆWICZENIA-11

  1. Zmień stronę tytułową i końcową.

  2. Znajdź i popraw zauważone błędy.

16. DŹWIĘK ODBICIA

Na koniec dodamy jeszcze dźwięk jaki wydawać powinna odbijana piłka (przyk12.zip). Do projektu jako bibliotekę należy dołączyć m. in. pliki dsound.lib i winmm.lib. Mamy tutaj trzy nowe pliki:

Ponadto jednym pliku zaszły zmiany

Plik dźwiękowy został wzięty z płyty Windows 98. Samo odtworzenie dźwięku następuje po wywołaniu funkcji Play.

(...)

// odbicie od górnej lub dolnej ściany

if ((pilka.y<=0) || (pilka.y>=SCREEN_HEIGHT-WYSOKOSC_PILKI))

{

lot.y = -lot.y;

if (G.bDzwiek)

G.lpDSB_Line->Play(0, 0, 0); // dźwięk

return;

}

(...)

Do gry dodano także możliwość włączania i wyłączania dźwięku za pomocą klawisza [D].

ĆWICZENIA-12

  1. Zmień odtwarzany plik na inny.

  2. Zrób inną grę

17. ZAKOŃCZENIE

Mój tekst już dobiegł końca. Mam nadzieję, że zawarte w nim przykłady bawiły, ale i czegoś nauczyły. Muszę się przyznać, że nie wszystko jest mojego autorstwa. Chcę podziękować w tym miejscu Timowi Bostonowi, który jest autorem części kodu. Bez niego ten tekst nigdy by nie powstał. Myślę, że najtrudniejszy jest pierwszy krok, zaś moją największą satysfakcją byłby fakt, że pomogłem komuś ten krok zrobić. Miłej zabawy oraz wspaniałych gier życzy autor.

KONIEC

1

3

0x01 graphic

0x01 graphic

0x01 graphic



Wyszukiwarka

Podobne podstrony:
Programowanie w jezyku C dla chetnych A Poznanski
PROGRAMOWANIE w c dla chetnych
A Poznański Programowanie w języku C dla chętnych
Programowanie w jezyku C dla chetnych A Poznanski
PROGRAMOWANIE w c dla chetnych
A Poznański Programowanie w języku C dla chętnych
Poznański Artur Programowanie W Jezyku C Dla Chetnych
GW CW15 Dla chetnych
Zadanie dodatkowe dla chętnych
Maturka dla chętnych 2
Zarządzanie Jakością - wykłady - 1 zadanie dla chętnych + odpowiedź, Zarządzanie UG, Sem. III, Zarzą
Ankieta Kapliczki, WoK, Prace dla chętnych
Zarządzanie Jakością - wykłady - 3 zadanie dla chętnych + odpowiedź, Zarządzanie UG, Sem. III, Zarzą
kazusy dla chetnych - mandat poselski, Dokumenty(2)
Zarządzanie Jakością wykłady 2 zadanie dla chętnych odpowiedź
GW CW15 Dla chetnych

więcej podobnych podstron