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):
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
© 2001
Vogel Publishing,
design by
ET NETERA
|