článek

Vytvoř si svou bludišťovku – 5

Vytvoř si svou bludišťovku – 5

Po delší odmlce, způsobené hlavně vánočními svátky, přichází na scénu další díl oblíbeného seriálu. Tentokrát se pustíme do celkem obsáhlého tématu. Je jím uživatelský vstup z klávesnice a myši, neboť interakce s hráčem je ve hrách velmi důležitá věc. Nově nabyté znalosti následně využijeme k tvorbě FPS kamery.

Ke zjišťování vstupu z klávesnice a myši využijeme část DirectX s názvem DirectInput. Jeho možnosti jsou opravdu obsáhlé. Například zvládne pracovat se všemi druhy gamepadů a joysticků, na které si jen vzpomenete. My však využijeme pouze základní funkce.



V první řadě musíme vložit potřebné hlavičkové soubory a knihovny




#include

#pragma comment (lib, "dinput.lib")
#pragma comment (lib, "dinput8.lib")
#pragma comment (lib, "dxguid.lib")




Někdo by možná mohl vznést námitku proč používat DirectInput8. Pravda je taková, že microsoft s vydáním DirectX9 DirectInput na verzi 9 neupgradoval.



Ze všeho nejdříve je potřeba vytvořit si potřebné objekty. Jedná se v první řadě o DirectInput interface a objekty pro práci s myší a klávesnicí.




LPDIRECTINPUT8 din;
LPDIRECTINPUTDEVICE8 dinkeyboard;
LPDIRECTINPUTDEVICE8 dinmouse;




Veškerou inicializaci provodeme ve funkci initDI. Jejími parametry bude HINSTANCE, což je identifikátor aplikace, a již známý handle okna.




void initDI(HINSTANCE hInstance, HWND hWnd)




Jako první musíme vytvořit interface.





DirectInput8Create(hInstance, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&din, NULL);




Poté musíme vytvořit devicy pro myš a klávesnici. Jedná se o prostředníka mezi vaší aplikací a klávesnicí, případně myší, jako je tomu u Direct3D a grafické karty.




din->CreateDevice(GUID_SysKeyboard, &dinkeyboard, NULL);
din->CreateDevice(GUID_SysMouse, &dinmouse, NULL);




Další krok je nastavit formát dat. K tomu nám poslouží funkce SetDataFormat. Nebudeme toto téma podrobněji rozebírat a použijeme defaultní hodnoty pomocí předpřipravených struktur, které DirectInput obsahuje.




dinkeyboard->SetDataFormat(&c_dfDIKeyboard);
dinmouse->SetDataFormat(&c_dfDIMouse);




Nyní nastavíme takzvaný cooperative level a tomu již budeme věnovat pozornosti více. Cooperative level určuje jak velkou kontrolu bude mít naše aplikace nad klávesnicí/myší.


Možné hodnoty jsou:



  • DISCL_BACKGROUND – Ke klávesnici/myši můžeme přistupovat i když není okno aplikace aktivní.

  • DISCL_FOREGROUND – Ke klávesnici/myši můžeme přistupovat pouze, pokud je okno aplikace aktivní.

  • DISCL_EXCLUSIVE – Ke klávesnici/myši může přistupovat pouze naše aplikace.

  • DISCL_NONEXCLUSIVE – Ke klávesnici/myši mohou přistupovat všechny aplikace včetně naší.

  • DISCL_NOWINKEY – Zakáže tlačítko s logem Windows, abychom si například jeho nechtěným stisknutím ve fullscreenu nepřerušili hru.






My zvolíme následující kombinaci:




dinkeyboard->SetCooperativeLevel(hWnd, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);

dinmouse->SetCooperativeLevel(hWnd, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND);




A tím jsme s funkcí initDI skončili. Veškerá zbylá práce s DirectInput se bude odehrávat v naší nové funkci handleCamera().



Zamysleme se nyní nad tím, jak by taková FPS kamera měla vlastně fungovat. Máme k dispozici View matrix, které zadáme pozici kamery a bod, kam se má dívat. Vypočítat pozici kamery by neměl být takový problém, ale spočítat místo kam se má dívat už může být horší. Vše si ukážeme.



Zamysleme se také nad dalším problémem. Je jím rychlost, kterou se bude naše kamera neboli hráč pohybovat. Vezměme si jednoduchý příklad. Když budeme držet šipku dopředu, bude se kamera pohybovat po ose z tím způsobem, že k její pozici na ose z přičte pokaždé 1. Tím ale docílíme jedné věci a sice, že bude rychlost kamery závislá na počtu snímků za sekundu a na různě výkonných sestavách bude její rychlost různá. Východiskem je vytvořit si takzvaný časovač, který bude počítat uplynulý čas mezi jednotlivými snímky. Využijeme k tomu funkci GetTickCount(), která vrací počet milisekund, uplynulých od startu systému. Kód pro časovač neboli timer bude potom vypadat následovně:




static int firsttime = GetTickCount();
static int currenttime = 0;
int lastcurrenttime = currenttime - firsttime;
currenttime = GetTickCount();
time = currenttime - lastcurrenttime – firsttime;




Výsledkem je globální proměnná timer, která obsahuje počet milisekund uplynulých od posledního snímku.


Nastal čas vydolovat nějaká data z klávesnice. V první řadě si vytvoříme pole proměnných typu BYTE, ve kterých budou uloženy informace o tom, které klávesy byly stlačeny. Poté funkcí Acquire() získáme přístup k zařízení a nakonec funkcí GetDeviceState naplníme naše pole chtěnými informacemi.




static BYTE key[256];
dinkeyboard->Acquire();
dinkeyboard->GetDeviceState(256, (LPVOID)key);




Vytvoříme si pár pomocných proměnných. Jedná se o rychlost kamery, o kolik se má posunout dopředu a o kolik se má posunout do strany. Když bude například forvard obsahovat zápornou hodnotu, kamera bude logicky couvat.




static float speed = 0.002f;
float forward = 0.0f;
float side = 0.0f;




Nyní zjistíme, které klávesy byly stisknuty a zařídíme se podle toho. Zde přichází k užitku náš časovač a máme zajištěno, že kamera se bude pohybovat nezávisle na počtu snímků za sekundu.




if(key[DIK_W] & 0x80) forward += speed * time;
if(key[DIK_S] & 0x80) forward -= speed * time;
if(key[DIK_A] & 0x80) side -= speed * time;
if(key[DIK_D] & 0x80) side += speed * time;




Nastal čas získat informace také o myši. Postup je stejný jako u klávesnice s výjimkou, že informace načteme do struktury DIMOUSESTATE.




static DIMOUSESTATE mouse;
dinmouse->Acquire();
dinmouse->GetDeviceState(sizeof(DIMOUSESTATE), (LPVOID)&mouse);




Struktura DIMOUSESTATE vypadá následovně:




typedef struct _DIMOUSESTATE {
LONG lX;
LONG lY;
LONG lZ;
BYTE rgbButtons[4];
} DIMOUSESTATE, *LPDIMOUSESTATE;




Jsou v ní uchovány informace o změně pozice (nikoliv o pozici) ve třech osách (osa z je kolečko myši) a čtyřech tlačítkách.


Nově nabyté informace využijeme ke spočítání úhlů pro kameru. Navíc omezíme interval pro rotaci y, což je nahoru a dolů.




static float rotx = 0;
static float roty = 0;
rotx += mouse.lX * 0.0001f;
roty += mouse.lY * 0.0001f;
static float maxy = 0.01;
if(roty > maxy) roty = maxy;
else if(roty < -maxy) roty = -maxy;




Vytvoříme si vector s názvem plulos, který přičteme k pozici kamery (campos je globální proměnná). Stačí nám pouze použít funkce sinus/cosinus. Násobení rotx číslem 57.29577951 ho převede z úhlu v radianech na stupně.




D3DXVECTOR3 pluspos;
pluspos.x = ((float)sin(rotx * 57.29577951) * forward) + ((float)cos(rotx * 57.29577951) * side);
pluspos.y = 0.0f;
pluspos.z = ((float)cos(rotx * 57.29577951) * forward) - ((float)sin(rotx * 57.29577951) * side));
campos += pluspos;




Zbývá nám vypočítat pozici vektoru camlookpos (také globální proměnná), který určuje bod, kam se kamera dívá. A posledním krokem je nastavení View matrix.




camlookpos = campos;
camlookpos.z += (float)cos(rotx * 57.29577951) * 0.1f;
camlookpos.x += (float)sin(rotx * 57.29577951) * 0.1f;
camlookpos.y -= (float)sin(roty * 57.29577951) * 0.1f;
D3DXMatrixLookAtLH(&matView, &campos,&camlookpos, &D3DXVECTOR3(0.0f, 1.0f, 0.0f));




A tím prohlašuji naší FPS kameru za hotovou. Pomocí proměnné campos ji umístíme na vytoužené místo ve scéně.


Možností, jak FPS kameru vytvořit je spousta, já se snažil vymyslet způsob co nejjednodušší.


Co nám zbývá, je zařídit volání funkce handleCamera pokaždé hned před funkcí render. Musíme také ve funkci render pokaždé přenastavovat View matrix a nastavením matlale na 1.5f zvětšit bludiště na potřebnou velikost.




d3ddev->SetTransform(D3DTS_VIEW, &matView);




A na závěr musíme také objekty DirectInput uvolnit. Použije k tomu funkci releaseDI().




void releaseDI()
{
dinkeyboard->Unacquire();
dinmouse->Unacquire();
din->Release();
}




Po dnešním snažení jsme dosáhli následujícího výsledku. Po bludišti se můžete volně pohybovat klávesami wsad a myší se klasicky rozhlížíte.








Na závěr malé představení obsahu příštího dílu. Budeme se zabývat materiály a osvětlením scény, což podstatně zvýší visuální kvalitu projektu.



Kompletní zdrojové kódy (všechny díly seriálu): bludistovka_zdroje.zip



Související odkazy:







pavlík_petr

autor
/ pavlík_petr

Publikováno: 30.12.2007


další články této kategorie





diskuze

odeslat

Nejsi automat? Napiš výsledek 4 + 5 =

Freegame | 30.10.08 v 09:48

Máme pro vás velmi dobrou zprávu, a to, že o víkendu vyjde další díl a pak další a další! Je skvělé, že máte lo seriál zájem!

Pavel | 30.10.08 v 00:35

Mohu se zeptat, jestli bude ještě slibované pokračování, nebo dokončení tutoriálu???Je škoda něco začít zkoušet a pak se seknout při kameře...:o(

mich98 | 04.05.08 v 19:09

mohli by ste prosim aktivovat ty zdrojovy kody