DirectX (9.)


V tΘto lekci se seznßmφme se t°φdou CInput, kterß zapouzd°uje prßci s objekty DirectInput. Postupn∞ tuto t°φdu rozvineme do rozsßhlΘ struktury a vyzkouÜφme si prßci s knihovnou DLL, proto₧e cel² modul DirectInput odd∞lφme do samostatnΘ knihovny. N∞co mßlo o knihovnßch jsem zmφnil minule a dnes to rozÜφ°φme. V²sledkem tak bude samostatnß knihovna DLL zapouzd°ujφcφ funkΦnost DirectInput.

9.1. Vytvo°enφ novΘho projektu

Asi nejlepÜφ bude, kdy₧ nov² projekt knihovny vlo₧φte do ji₧ rozd∞lanΘho projektu DirectDraw. To proto, abychom mohli vÜechno hned testovat. Jak se to ud∞lß jsem zmφnil v minulΘ lekci o VC++, ale pro ty, kte°φ Φlßnek neΦetli nebo se k nim nedostal Chip, to zopakuji.

Za prvΘ vytvo°φme prßzdn² projekt Blank Workspace t°eba se jmΘnem DirectX. Z menu File vyberte polo₧ku New. Po zobrazen² dialog vypl≥te n∞jak takto:

V mφst∞, kterΘ jste zadali v polφΦku Location se vytvo°φ adresß°. Do tohoto adresß°e kompletn∞ zkopφrujte projekt z p°edchozφch lekcφ o DirectDraw. D∙le₧itΘ je, aby DirectDraw z∙stalo v samostatnΘm podadresß°i pod DirectX. Po tΘ z v²vojovΘho prost°edφ p°idßme tento projekt jako ji₧ existujφcφ. Z menu Project z volte poslednφ polo₧ku Insert Project into Workspace a v dialogu vyberte soubor DirectDraw.dsp v podadresß°i DirectDraw. VÜimn∞te si zm∞ny v ClassView.

Novou knihovnu Input nemusφte nutn∞ spojovat s projektem DirectDraw, ale m∙₧ete si vytvo°it zcela jinou aplikaci. Knihovna Input.dll bude nezßvislß (alespo≥ prozatφm). Nynφ tedy vlo₧me knihovnu:

Z menu File vyberte polo₧ku New. Zobrazen² dialog vypl≥te n∞jak takto:

Zde je d∙le₧itΘ abyste projekt p°idali do ji₧ otev°enΘ Workspace. Projekt knihovny se op∞t vytvo°il v samostatnΘm adresß°i Input. Dßle nastavte rozÜφ°enou knihovnu DLL, aby se nßm lΘpe pracovalo s exporty a p°φpadn∞ se zdroji:

Nynφ si vÜimn∞te, ₧e do okna ClassView p°ibyla polo₧ka Input. Dßle trochu upravφme vlastnosti obou projekt∙, aby se nap°φklad kompilovaly ve sprßvnΘm po°adφ a do stejnΘho adresß°e Release a Debug celΘ Workspace.

Z menu Project vyberte polo₧ku Settings. Nastavte na kart∞ Link spoleΦn² adresß° pro v²stupnφ soubory (EXE a DLL). Toto nastavenφ musφte ud∞lat zvlßÜ¥ pro oba projekty a navφc pro verze Release a Debug (Φili 4x).

Dßle nastavφme zßvislosti. Musφme zaruΦit ₧e projekt Input se bude kompilovat p°ed DirectDraw a ₧e Input bude p°ilinkovßn k DirectDraw. I to jsme si ukazovali minule. Z menu Project vyberte polo₧ku Dependencies a dialog nastavte takto:

Vidφte zßvislost projektu DirectDraw na projektu Input.

To je k nastavenφ projekt∙ zatφm vÜe. Za chvilku jeÜt∞ p°idßme n∞kterΘ dynamickΘ knihovny pot°ebnΘ pro samotnΘ DirectInput jako tomu bylo u DirectDraw. P°ed kompilacφ se ujist∞te, ₧e mßte DirectDraw projekt nastaven² jako v²chozφ (nebo aktivnφ). Zkuste si projekt zkompilovat a uvidφte, ₧e nejprve se sestavφ projekt Input a teprve pak DirectDraw.

9.2. T°φda CInput

KoneΦn∞ se dostßvßme k programovßnφ. Jak u₧ bylo °eΦeno, jßdrem knihovny bude t°φda CInput. Tuto t°φdu lze exportovat dv∞ma zp∙soby. Za prvΘ m∙₧ete exportovat celou t°φdu. Klient, kter² knihovnu chce pou₧φt pak musφ vytvo°it objekt typu CInput a teprve pak volat jednotlivΘ metody. Druh² zp∙sob je podle m∞ z u₧ivatelskΘho hlediska snazÜφ, ale z vaÜφ strany vy₧aduje pßr °ßdk∙ navφc. V samotnΘ knihovn∞ vytvo°φme globßlnφ objekt CInput a potΘ budeme exportovat pouze globßlnφ funkce, kterΘ uvnit° pouze volajφ metody globßlnφho objektu. Tento zp∙sob takΘ zaruΦφ, ₧e u₧ivatel nevytvo°φ vφce objekt∙ CInput, co₧ je ne₧ßdoucφ.

P°idat t°φdu do projektu ji₧ umφme. Z menu Insert zvolte polo₧ku New class.

TIP: Abyste p°idali t°φdu do projektu Input musφ b²t tento projekt aktivnφ. Abyste nemuseli p°epφnat mezi projekty, staΦφ vyu₧φt kontextovΘ menu v ClassView. Kliknete-li prav²m tlaΦφtkem na projekt Input, m∙₧ete p°φmo vybrat volbu New class.

Nßsledujφcφ dialog vypl≥te p°ibli₧n∞ takto:

Prßv∞ v tuto chvφli budeme pot°ebovat knihovny DirectInput. Jsou dv∞: dinput8.dll a dxguid.dll. V menu Project volte polo₧ku Settings. Op∞t na kart∞ Link p°idejte do polφΦka Objects/library modules jmΘna knihoven import∙ t∞chto dvou knihoven: dinput8.lib a dxguid.lib. Nastavenφ prove∩te i pro Release. Dßle je pot°eba vlo₧it hlaviΦkov² soubor dinput.h nejlΘpe do souboru stdafx.h projektu Input (nejlΘpe se k n∞mu dostanete p°e FileView)  Nynφ mßme zajiÜt∞no, ₧e pokud pou₧ijeme n∞kterΘ objekty a funkce DirectInput, bude je kompilßtor i linker znßt.

Nßsledujφcφ tabulky popisujφ t°φdu CInput:
 

╚lenskΘ prom∞nnΘ

Typ prom∞nnΘ

Nßzev prom∞nnΘ

Popis

BOOL

m_bInit

TRUE pokud je systΘm inicializovan² jinak FALSE. To jest ochrana proti vφcenßsobnΘm volßnφ inicializaΦnφ metody a proti neoprßvn∞nΘmu volßnφ ostatnφch metod.

LPDIRECTINPUT8

m_lpDI

Ukazatel na objekt DirectInput. Tento objekt vytvo°φme v inicializaΦnφ metod∞.

LPDIRECTINPUTDEVICE8

m_lpDIDKeyboard

Ukazatel na objekt klßvesnice. Pou₧φvß se ke sta₧enφ informacφ o aktußlnφm stavu klßvesnice (naplnφ pole jednotliv²ch klßves, ze kterΘho je pak mo₧no urΦit, kterß klßvesa je stisknuta a kterß nikoliv).

LPDIRECTINPUTDEVICE8

m_lpDIDMouse

Ukazatel na objekt myÜi. Pou₧φvß se ke sta₧enφ informacφ o aktußlnφm stavu myÜi (poloze a tlaΦφtkßch).

BYTE

m_arKeyboard[256]

V tomto poli se prßv∞ uklßdß stav jednotliv²ch klßves.

DIMOUSESTATE

m_MouseState

Struktura obsahujφcφ aktußlnφ stav myÜi (informace o tlaΦφtkßch, pohybu apod.)

BOOL

m_IsHandled[256]

Do tohoto pole se uklßdß informace o tom, zda-li je klßvesa stisknuta poprvΘ, Φi jestli je dr₧ena.

BOOL

m_bIsMouseHandled[2]

ObdobnΘ pole jako p°edchozφ, pouze pro dv∞ tlaΦφtka myÜi.

CPoint

m_ptCursor

Aktußlnφ sou°adnice kurzoru.

CSize

m_szRes

Hodnoty x a y popisujφ aktußlnφ rozliÜenφ. Tuto hodnotu budeme inicializovat v inicializaΦnφ metod∞ a slou₧φ nßm k tomu, abychom kurzor udr₧eli ve sprßvn²ch mezφch.

HICON

m_hCursor

Handle na aktußlnφ kurzor.

int

m_iMouseSensitivity

Sensitivita myÜky Φili citlivost.

BOOL

m_bShowCursor

Informace o tom, zda-li mß b²t kurzor vid∞t (TRUE) Φi nikoliv (FALSE).

 

 

 

 

 

 

 

 

 

 

 

 

 

╚lenskΘ funkce - metody

Ve°ejnΘ funkce - funkce kterΘ budou exportovßny z knihovny

Nßvratovß hodnota

Nßzev funkce a parametr∙

Popis metody i parametr∙

HRESULT

CreateDirectInputSystem(HINSTANCE hInst, HWND hWnd, CSize csResolution)

Inicializuje objekt DirectInput, objekt klßvesnice a objekt myÜi. Nastavuje chovßnφ aplikace v∙Φi svΘmu okolφ a uklßdß si rozliÜenφ.

HRESULT

RestoreDevices()

Pokud si pamatujete na obnovovßnφ ztracen²ch povrch∙, pak tato metoda d∞lß n∞co podobnΘho, ale pro myÜ a klßvesnici.

BOOL

IsRButtonDown(BOOL _Handling = TRUE)

Vracφ TRUE pokud je stisknutΘ pravΘ tlaΦφtko myÜi.

BOOL

IsLButtonDown(BOOL _Handling = TRUE)

Vracφ TRUE pokud je stisknutΘ levΘ tlaΦφtko myÜi.

BOOL

IsRButtonUp()

Vracφ TRUE pokud nenφ stisknutΘ pravΘ tlaΦφtko myÜi.

BOOL

IsLButtonUp()

Vracφ TRUE pokud nenφ stisknutΘ levΘ tlaΦφtko myÜi.

BOOL

IsKeyDown(int Key, BOOL bHandle)

Vracφ TRUE pokud je stisknutß po₧adovanß klßvesa (Key).

void

ProcessInput()

Stahuje data z klßvesnice a myÜi. Posouvß kurzor myÜky.

void

UpdateCursor(HDC hdc)

Vykresluje kurzor na obrazovku. Po₧aduje HDC (handle kontext za°φzenφ) okna nebo povrchu DirectDraw.

CPoint

GetCursor()

Vracφ aktußlnφ pozici kurzoru.

void

ShowCursor(BOOL bShow)

Pokud je parametr bShow, kurzor se zviditelnφ jinak z∙stane skryt².

HRESULT

LoadCursor(HICON hCursor)

Nastavuje aktußlnφ zobrazovan² kurzor.

SoukromΘ funkce

Nßvratovß hodnota

Nßzev funkce a parametr∙

Popis metody i parametr∙

void

Clean()

Provßdφ Φistφcφ prßce p°i ukonΦenφ aplikace nebo p°i nßsilnΘm ukonΦenφ v d∙sledku chyby.

HRESULT

MoveCursor(int dx, int dy)

Pouze p°iΦφtß p°φr∙stky x-ovΘ a y-ovΘ sou°adnice kurzoru k aktußlnφ pozici kurzoru. P°φr∙stky jsou vyta₧eny ze stavovΘ struktury myÜi.

Ve t°φd∞ CDisplay byla funkce CreateFullscreenDisplay(). My si podobnou funkci vytvo°φme i naÜφ t°φd∞. T°φda bude mφt nßsledujφcφ deklaraci:

HRESULT CreateDirectInputSystem(HINSTANCE hInst, HWND hWnd, CSize csResolution);

P°edßvßme ji t°i parametry, kterΘ pozd∞ji vyu₧ijeme p°i vytvß°enφ objektu DirectInput:
 

HINSTANCE hInst

handle instance aplikace

HWND hWnd

handle okna aplikace

CSize csResolution

struktura napln∞nß informacφ o rozliÜenφ

 

 

P°idejte tuto funkci do t°φdy. Mßme dva zp∙soby, jak p°idat novou metodu. Bu∩ op∞t vyu₧ijeme kontextovΘho menu, ale nynφ p°φmo na t°φd∞, nebo to ud∞lßme ruΦn∞. Minule jsem vßm taky poradil, abyste si vytvß°eli funkce Clean() u slo₧it∞jÜφch t°φd. Tuto funkci s v²hodou vyu₧ijeme i v naÜφ novΘ t°φd∞.

Nynφ doplnφme k≤d funkce CreateDirectInputSystem():

HRESULT CInput::CreateDirectInputSystem(HINSTANCE hInst, HWND hWnd, CSize csResolution)
{
   HRESULT dwRet = 1;
   //
   // You cannot call this function twice
   if(!m_bInit) {
      //
      // Creation of direct input object
     dwRet = DirectInput8Create(hInst, DIRECTINPUT_VERSION, IID_IDirectInput8, (VOID**)&m_lpDI, NULL);
     if(dwRet != DI_OK) {
        TRACE("Cannot create direct input object %d\n", dwRet);
        Clean();
        return dwRet;
     }
     //
     // Create keyboard object
     dwRet = m_lpDI->CreateDevice(GUID_SysKeyboard, &m_lpDIDKeyboard, NULL);
     if(dwRet != DI_OK) {
         TRACE("Cannot create keyboard object %d\n", dwRet);
         Clean();
         return dwRet;
     }
     //
     // Create mouse object
     dwRet = m_lpDI->CreateDevice(GUID_SysMouse, &m_lpDIDMouse, NULL);
     if(dwRet != DI_OK) {
         TRACE("Cannot create mouse object %d\n", dwRet);
         Clean();
         return dwRet;
     }
     //
     // Set data format for both devices
     dwRet = m_lpDIDMouse->SetDataFormat(&c_dfDIMouse);
     if(dwRet != DI_OK) {
        TRACE("Cannot set data format for mouse %d\n", dwRet);
        Clean();
        return dwRet;
     }
     dwRet = m_lpDIDKeyboard->SetDataFormat(&c_dfDIKeyboard);
     if(dwRet != DI_OK) {
         TRACE("Cannot set data format for keyboard %d\n", dwRet);
         Clean();
         return dwRet;
     }
     //
     // Set cooperative level for both devices
     dwRet = m_lpDIDKeyboard->SetCooperativeLevel(hWnd, DISCL_FOREGROUND|DISCL_NONEXCLUSIVE);
     if(dwRet != DI_OK) {
         TRACE("Cannot set cooperative level for keyboard %d\n", dwRet);
         Clean();
         return dwRet;
     }
     dwRet = m_lpDIDMouse->SetCooperativeLevel(hWnd, DISCL_NONEXCLUSIVE|DISCL_FOREGROUND);
     if(dwRet != DI_OK) {
         DXTRACE6("Cannot set cooperative level for mouse %d\n", dwRet);
         Clean();
         return dwRet;
     }
     //
     // Acquire mouse and keyboard
     dwRet = m_lpDIDKeyboard->Acquire();
     if(dwRet != DI_OK) {
         DXTRACE6("Cannot acquire keyboard %d\n", dwRet);
         Clean();
         return dwRet;
     }
     dwRet = m_lpDIDMouse->Acquire();
     if(dwRet != DI_OK) {
         TRACE("Cannot acquire mouse %d\n", dwRet);
         Clean();
         return dwRet;
     }
     // Inicializace nekterych vnitrnich atributu
     m_csResolution = csResolution;
     m_bInit = TRUE;
   }
   return dwRet;
}

P°edchozφ k≤d mo₧nß vypadß pon∞kud slo₧it∞, ale ve skuteΦnosti je to vÜechno lehkΘ. Prvnφ podmφnka je pouze z d∙vodu ochrany p°ed vφcenßsobn²m volßnφ metody. Zkrßtka pokud se u₧ivatel pokusφ vφckrßt volat tuto i dalÜφ metody, nic se nestane a je vrßcena hodnota 1. VÜechno to zajiÜ¥uje prom∞nnß typu BOOL m_bInit. V prvnφm kroku vytvß°φme objekt DirectInput. K tomu slou₧φ funkce DirectInput8Create() s p∞ti parametry:
 

HINSTANCE hinst

Handle na instanci aplikace. Prßv∞ u tohoto parametru vyu₧ijeme vstupnφ parametr funkce CreateDirectInputSystem()

DWORD dwVersion

Po₧adovanß verze vytvß°enΘho objektu. U tohoto parametru vyu₧ijeme symbolickΘ konstanty DIRECTINPUT_VERSION. Pokud bychom cht∞li jinou verzi ne₧ 8.0, museli bychom tuto konstantu sami definovat jeÜt∞ p°ed vlo₧enφm hlaviΦkovΘho souboru input.h. Napsali bychom t°eba #define DIRECTINPUT_VERSION 0x0700 pro verzi 7.0 apod. Pokud chceme pou₧φt verzi 8.0 (co₧ chceme), je dobrΘ explicitn∞ konstantu definovat takΘ, proto₧e jinak ses nßm bude ve v²stupnφm okn∞ objevovat varovnß hlßÜka.

 REFIID riidltf

Unikßtnφ identifikßtor rozhranφ, kterΘ po funkci chceme. Chceme rozhranφ objektu DirectInput, tak₧e posφlßme hodnotu IID_IDirectInput8.

LPVOID* ppvOut

Prom∞nnß, do kterΘ se ulo₧φ adresa po₧adovanΘho rozhranφ. Je to stejnΘ jako u DirectDraw, posφlßme ukazatel na ukazatel.

LPUNKNOWN punkOuter

Tento parametr se vyu₧φvß, pouze pokud chceme pou₧φt agregaci COM. My poÜleme hodnotu NULL.

 

 

 

 

 

 

 

 

Funkce vracφ hodnotu DI_OK (0) pokud je vÜe v po°ßdku. VÜimn∞te si logiky odhalovßnφ chyb. Pokud by se n∞co pokazilo, ihned je volßna metoda Clean(), kterß uvolnφ rozhranφ a funkce je ukonΦena s tφm, ₧e nßm zapφÜe k≤d chyby do v²stupnφho okna. V DirectX existuje pßr funkcφ a maker, kterΘ jsou schopny odhalit o jakou chybu se jednß a vracφ °et∞zec Φiteln² pro Φlov∞ka.

V dalÜφch krocφch provßdφme tytΘ₧ kroky pro myÜ a klßvesnici. Pomocφ funkce CreateDevice() vytvo°φme objekt myÜi a objekt klßvesnice. My si pouze uklßdßme ukazatel na rozhranφ t∞chto objekt∙ a pokud je toto rozhranφ uvoln∞no funkcφ Release(), je i objekt myÜi nebo klßvesnice uvoln∞n. VÜimn∞te si takΘ, ₧e metoda CreateDevice() je Φlenskß funkce rozhranφ objektu DirectInput.
CreateDevice() mß t°i parametry:
 

REFGUID rguid

Unikßtnφ identifikßtor za°φzenφ, pro kterΘ chceme vytvo°it objekt. Pokud chceme pouze myÜ nebo klßvesnici m∙₧eme pou₧φt p°eddefinovanΘ hodnoty GUID_SysMouse nebo GUID_SysKeyboard. Pokud ovÜem chcete jinß za°φzenφ, musφte pou₧φt funkci EnumDevices(), abyste zjistili, co je prßv∞ p°ipojeno k PC.

LPDIRECTINPUTDEVICE *lplpDirectInputDevice

Prom∞nnß, do kterΘ se ulo₧φ ukazatel naÜeho rozhranφ. Op∞t je to stejnΘ jako vÜude jinde: p°edßvßme ukazatel na ukazatel.

LPUNKNOWN pUnkOuter

Poslednφ parametr je stejn² jako poslednφ parametr p°edchozφ funkce. PoÜleme NULL.

 

 

 

 

 

Nynφ mßme zinicializovanΘ vÜechny pot°ebnß rozhranφ a zb²vß pßr funkcφ nutn²ch pro sprßvn² chod. P°edevÜφm je to funkce SetDataFormat(), SetCooperativeLevel() a Acquire(). VÜechny tyto metody volßme pro ka₧dΘ za°φzenφ zvlßÜ¥.

Funkce SetDataFormat() pouze nastavuje, v jakΘm formßtu budeme stahovat data z klßvesnice nebo myÜky. Funkce mß jeden parametr, kter² p°edstavuje bu∩ p°eddefinovan² nebo u₧ivatelsk² formßt stßhnut²ch dat. P°eddefinovanΘ formßty jsou nßsledujφcφ (v pravΘm sloupci jsou p°φsluÜnΘ struktury, kterΘ budeme pou₧φvat):
 

c_dfDIMouse

DIMOUSESTATE

c_dfDIMouse2

DIMOUSESTATE2

c_dfDIKeyboard

array of 256 bytes

c_dfDIJoystick

DIJOYSTATE

c_dfDIJoystick2

DIJOYSTATE2

 

 

 

 

Nap°φklad rozdφl mezi DIMOUSESTATE a DIMOUSESTATE2 je takov², ₧e struktura DIMOUSESTATE2 obsahuje informace o osmi tlaΦφtkßch myÜi, zatφmco struktura DIMOUSESTATE jen o Φty°ech. VÜimn∞te si takΘ, ₧e pro klßvesnici mßme ji₧ zmi≥ovanΘ pole byt∙. Vφce se o t∞chto datov²ch strukturßch dovφte, a₧ je budeme p°φmo pou₧φvat.

Funkce SetCooperativeLeve() nastavuje chovßnφ naÜeho za°φzenφ (objektu za°φzenφ) v∙Φi ostatnφm objekt∙m tΘho₧ za°φzenφ a v∙Φi okolnφm aplikacφm. Je to podobnΘ jako u DirectDraw. Funkce mß dokonce velmi podobnΘ parametry:
 

HWND hWnd

Handle na okno aplikace, to jest druh² parametr naÜφ funkce  CreateDirectInputSystem().

DWORD dwFlags

Zde je to o trochu slo₧it∞jÜφ. Tento parametr se m∙₧e sklßdat z vφce hodnot. Je p∞t hodnot: DISCL_BACKGROUND pro zajistφ to, ₧e p°φstup k za°φzenφ m∙₧e zφskat i okno, kterΘ nenφ aktivnφ, Φili je v pozadφ. Pokud programujeme s DirectDraw, pravd∞podobn∞ budeme pou₧φvat fullscreen a tam mßme pouze jedno naÜe okno na pop°edφ tak₧e rad∞ji nastavφme druhou hodnotu DISCL_FOREGROUND. Hodnota DISCL_EXCLUSIVE zajiÜ¥uje exkluzivnφ p°φstup za°φzenφ. Pokud zφskßme exkluzivnφ p°φstup, ₧ßdnß jinß aplikace nem∙₧e zφskat rovn∞₧ exkluzivnφ p°φstup. Naproti tomu stojφ DISCL_NONEXCLUSIVE, kterß zajiÜ¥uje b∞₧n² p°φstup a kter² m∙₧e zφskat vφce aplikacφ najednou. Abychom se vyhnuli konflikt∙m nastavφme hodnotu neexkluzivnφ. Poslednφ hodnota DISCL_NOWINKEY pouze zabra≥uje pou₧itφ klßvesy s okΘnkem, pro vyskoΦenφ z aplikace.

 

 

 

 

 

Poslednφ metodou Acquire() zφskßme koneΦn∞ p°φstup k dat∙m za°φzenφ. Pokud vyskoΦφme mimo z naÜφ aplikace, je toto prßvo p°φstup zruÜeno a po zp∞tnΘm navrßcenφ se musφ op∞t zavolat funkce Acquire(), abychom op∞t zφskali p°φstup k za°φzenφ (tuto Φinnost bude provßd∞t funkce RestoreDevices()).

9.3. Zßv∞r

A to je pro tuto lekci vÜe. P°φÜt∞ budeme pokraΦovat v implementaci t°φdy CInput a doufejme, ₧e staΦφme cel² systΘm spustit. Do budoucna bych cht∞l uvΘst trochu komplexn∞jÜφ p°φklad grafickΘho menu, kterΘ pracuje s DirectDraw a s DirectInput. Budu rßd za vaÜe dotazy a p°ipomφnky.


T∞Üφm se p°φÜt∞ nashledanou.

Ji°φ Formßnek