DirectX (10.)


V dneÜnφ jubilejnφ lekci budeme pokraΦovat v implementaci novΘ t°φdy CInput, kterou jsme rozd∞lali v minulΘ lekci. V²sledkem dneÜnφ lekce, by m∞la b²t fungujφcφ myÜ i klßvesnice, ale bohu₧el zatφm nemßme co ovlßdat. V n∞kolika p°φÜtφch lekcφch vÜak hodlßm sestavit v∞tÜφ projekt, kter² bude vyu₧φvat znalostφ, kterΘ mßte. Bude se jednat o trochu slo₧it∞jÜφ systΘm grafickΘho menu, ale vφce se o tom dovφte a₧ p°φÜt∞.

10.1. Funkce ProcessInput() a UpdateCursor()

Ob∞ tyto funkce jsou spoleΦnΘ tφm, ₧e je budeme volat z funkce UpdateFrame() v naÜem hlavnφm programu. ProblΘm je ale v tom, ₧e funkce ProcessInput() musφte volat d°φve, ne₧ obnovφte grafiku. Kdybyste ale obnovili grafiku d°φve ne₧ kurzor, byl by kurzor skryt za pozadφm a to nechceme. Funkce ProcessInput() stahuje data z klßvesnice a myÜφ, p°φpadn∞ posune sou°adnice kurzoru. Na tyto novΘ sou°adnice se poslΘze nakreslφ kurzor z funkce UpdateCursor().

Abychom mohli tyto dv∞ hlavnφ funkce implementovat, musφme p°idat pßr nov²ch prom∞nn²ch a metod, kterΘ s t∞mito funkcemi spolupracujφ.

Za prvΘ to bude inline funkce MoveCursor(), kterß je vskutku primitivnφ:

void MoveCursor(int dx, int dy) {m_ptCursor += CPoint(dx, dy);}

Vidφte, ₧e musφme p°idat atribut m_ptCursor. To je objekt typu CPoint (pokud nemßte rßdi MFC, m∙₧ete po₧φt strukturu POINT) a obsahuje aktußlnφ absolutnφ sou°adnice kurzoru na monitoru. Hodnoty x-ovΘ sou°adnice se pohybujφ od 0 do hodnoty rozliÜenφ v x-ovΘm sm∞ru. Ve vertikßlnφm sm∞ru je to obdobnΘ. Nynφ u₧ vφte, proΦ jsme si uklßdali aktußlnφ rozliÜenφ.

Funkce ProcessInput() vypadß nßsledovn∞:

HRESULT dwRet = 1;
if(m_bInit) {
  
//
   // Get state of keyboard and save it to internal structure (array)

   m_lpDIDKeyboard->GetDeviceState(sizeof(m_arKeyboard), &m_arKeyboard);
  
//
   // Update cursor if is showed

   if(m_bShowCursor) {
     
//
      // Get relative position of cursor.
      // Save state of mouse.

      m_lpDIDMouse->GetDeviceState(sizeof(m_MouseState) , &m_MouseState);
    
 //
      // Add or substract position of cursor

      MoveCursor(m_MouseState.lX, m_MouseState.lY);
    
 //
      // Bounds cursor on the screen

      if(m_ptCursor.x < 0) m_ptCursor.x = 0;
      if(m_ptCursor.x > m_csResolution.cx) m_ptCursor.x = m_csResolution.cx;
      if(m_ptCursor.y < 0) m_ptCursor.y = 0;
      if(m_ptCursor.y > m_csResolution.cy) m_ptCursor.y = m_csResolution.cy;
   }
}
return dwRet;

P°ibyly jeÜt∞ dalÜφ prom∞nnΘ. Prom∞nnß m_bShowCursor nßm °φkß, zda-li je kurzor vid∞t Φi nikoliv. Pokud ne, je zbyteΦnΘ stahovat data z myÜi a v∙bec pracovat s kurzorem.

Prvnφ co v tΘto funkci ud∞lßme je, ₧e stßhneme data z klßvesnice. Funkce GetDeviceState() naplnφ pole o 256 byte prvcφch. Pole m_arKeyboard je tedy dalÜφ atribut. V tomto poli je ulo₧ena informace o stavu vÜech klßves.

To samΘ provedeme s myÜφ (pokud je kurzor zobrazen). Za prvΘ stßhneme data z myÜi tentokrßt do struktury DIMOUSESTATE, kterß obsahuje vÜe co pot°ebuje: p°φr∙stky pozice kurzoru a stavy tlaΦφtek. V dalÜφm kroku p°iΦteme relativnφ p°φr∙stky kurzoru k absolutnφ pozici - to provßdφ v²Üe definovanß funkce MoveCursor(). VÜimn∞te si, ₧e pou₧φvßme atributy lX a lY struktury DIMOUSESTATE.

Struktura DIMOUSESTATE obsahuje nßsledujφcφ atributy:

LONG lX;
LONG
lY;
LONG
lZ;
BYTE
rgbButtons[4];

lX a lY jsou p°φr∙stky ve sm∞ru osy x a y. lZ je p°φr∙stek otoΦenφ koleΦka myÜi (pokud myÜ nemß koleΦko, je tato hodnota rovna 0). Poslednφ pole rgbButtons[] obsahuje stavy Φty° tlaΦφtek. V poli je na prvnφm mφst∞ levΘ tlaΦφtko, pak pravΘ a na dalÜφch dvou indexech jsou dalÜφ tlaΦφtka, pokud myÜ n∞jakΘ mß. Pokud je po₧adovanΘ tlaΦφtko stisknuto, je v poli nenulovß hodnota jinak 0.

V poslednφm kroku funkce, provedeme tzv. clipping kurzoru. Musφme zabrßnit tomu, aby kurzor mohl vyjet mimo obrazovku. KoneΦn∞ vyu₧ijeme rozliÜenφ, kterΘ jsme si ulo₧ili. A to je vÜe.

Dßle si rozeberme funkci UpdateCursor(). I k tΘto funkci budeme pot°ebovat jednu funkce navφc. Bude to SetCursor(), kterß nastavφ handle kursoru, kter² se bude vykreslovat na pozici urΦenΘ atributem m_ptCursor. Op∞t se jednß o jednoduchou inline funkci:

HRESULT SetCursor(HICON hCursor) {m_hCursor = hCursor; return 0;}

Pouze p°i°adφme nov² handle.

Samotnß funkce UpdateCursor() bude takΘ celkem primitivnφ:

HRESULT CInput::UpdateCursor(HDC hDC)
{
    HRESULT dwRet = 1;
    if(m_bInit && m_bShowCursor && m_hCursor) {

        dwRet = DrawIcon(hDC, m_ptCursor.x, m_ptCursor.y, m_hCursor);
        if(dwRet == 0) {
            dwRet = GetLastError();
            TRACE("Nastala chyba pri vykresleni kurzoru: %d\n", dwRet);
        }
    }
    return dwRet;
}

Nejd°φve otestujeme sprßvnost handlu m_hCursor a zobrazφme kurzor jen kdy₧ mß b²t skuteΦn∞ zobrazen (m_bShowCursor). Jako druh² a poslednφ krok zavolßme funkci DrawIcon(), kterß vykreslφ po₧adovan² kurzor na sprßvn²ch sou°adnicφch. Mo₧nß si te∩ prßv∞ myslφte, proΦ nevyu₧it k vykreslenφ kurzoru DirectDraw. Bylo by sprßvnΘ to tak ud∞lat, ale v souΦasnΘ dob∞ mßme k≤d DirectDraw v modulu .exe souboru a ten vyu₧φvß knihovny Input.dll. Tudφ₧ Input.dll nem∙₧e b²t zßvislß na .exe souboru, kde je pot°ebnΘ DirectDraw. Pozd∞ji projekt upravφme tak, ₧e i DirectDraw bude v samostatnΘ knihovn∞ a potΘ budeme kurzor vykreslovat pomocφ DirectDraw.

10.2. Exportovßnφ funkcφ

Nynφ jsme dosp∞li do stavu, kdy nßÜ systΘm DirectInput mß n∞co d∞lat a pot°ebovali bychom to vyzkouÜet. To znamenß, ₧e pot°ebuje volat funkce jako je ProcessInput() a UpdateCursor(). Toto je dß provΘst n∞kolika zp∙soby (o t∞chto zp∙sobech jsem psal v minul²ch lekcφch). Vytvo°me tedy globßlnφ objekt CInput:

CInput  g_theInput;

Tento °ßdek napiÜte na zaΦßtek souboru input1.cpp. Nynφ do hlaviΦkovΘho souboru input1.h doplnφme seznam exportovan²ch globßlnφch funkcφ:

INPUT_API HRESULT inpCreateDirectInputSystem(HINSTANCE hInst, HWND hWnd, CSize csResolution);
INPUT_API HRESULT inpProcessInput();
INPUT_API HRESULT inpUpdateCursor(HDC hDC);
INPUT_API HRESULT inpSetCursor(HICON hCursor);

Konstanta INPUT_API je definovßna na dvou mφstech r∙zn∞. V implementaΦnφ souboru input1.cpp definujte konstantu takto:

#define   INPUT_API __declspec(dllexport)

Tuto definici musφte provΘst p°ed vlo₧enφm hlaviΦkovΘho souboru input1.h. Za druhΘ p°ipiÜte nßsledujφ °ßdky na zaΦßtek souboru input1.h:

#ifndef INPUT_API
     #define INPUT_API __declspec( dllimport )
#endif // INPUT_API

Tyto °ßdky za°φdφ to, ₧e pokud se pokusφme vlo₧it tento hlaviΦkov² soubor mimo knihovnu input.dll, nadefinuje se makro COMMON_API pro import v²Üe uveden²ch funkcφ. Naopak v modulu knihovny je konstanta  definovßna pro export funkcφ.

Od te∩, pokud chcete pou₧φt funkce s prefixem inp, musφte pouze  vlo₧it hlaviΦkov² soubor input1.h.

Zb²vß jen nadefinovat exportovanΘ funkce. Definice bude velmi jednoduchß. StaΦφ vyu₧φt globßlnφho objektu k volanφ jednotliv²ch metod:

//
// Exportovane funkce
HRESULT inpCreateDirectInputSystem(HINSTANCE hInst, HWND hWnd, CSize csResolution)
{
    return g_theInput.CreateDirectInputSystem(hInst, hWnd, csResolution);
}

HRESULT inpProcessInput()
{
    return g_theInput.ProcessInput();
}

HRESULT inpUpdateCursor(HDC hDC)
{
    return g_theInput.UpdateCursor(hDC);
}

HRESULT inpSetCursor(HICON hCursor)
{
    return g_theInput.SetCursor(hCursor);
}

 

Dßle upravφme druh² projekt DirectDraw tak, abychom koneΦn∞ mohli zavolat novΘ funkce. Do souboru control.cpp vlo₧te hlaviΦkov² soubor input1.h tφmto zp∙sobem:

#include "..\Input\Input1.h"

Uv∞domte si, ₧e soubor input1.h le₧φ ·pln∞ v jinΘm adresß°i. Od tΘto chvφle m∙₧ete vyu₧φt globßlnφ funkce, kterΘ jsme p°ed chvilkou nadefinovali. NßÜ systΘm DirectDraw nenφ moc vhodn² pro vklßdßnφ DirectInput, ale pro zaΦßtek nßm to bude staΦit.

Do funkce InitDD() p°idejte nßsledujφcφ °ßdky:

dwResult = inpCreateDirectInputSystem(AfxGetInstanceHandle(), hWnd, CSize(RES_X, RES_Y));
if(dwResult != ERROR_SUCCESS) {
    TRACE("Nemohu inicializovat DirectInput protoze %d.\n", dwResult);
    return dwResult;
}

inpSetCursor(LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_DD)));

ZaprvΘ je nutno zavolat funkci, kterß zinicializuje DirectInput. Funkce AfxGetInstanceHalndle() vracφ handle instance, co₧ je p°esn∞ to, co pot°ebujeme. Dßle posφlßme handle okna a nakonec strukturu CSize s rozliÜenφm obrazovky. V druhΘm kroku nastavφme kurzor tzn., ₧e nahrajeme po₧adovan² kurzor do pam∞ti a nastavφme handle funkcφ inpSetCursor(). Kurzor p°idejte do zdroj∙ projektu DirectDraw do slo₧ky Icons.

Dßle funkci UpdateFrame() upravte takto:

void CControl::UpdateFrame()
{
   DWORD dwRet;
   if(m_bReady) {

      inpProcessInput();
      //...
      CRect rcBackground;
      rcBackground.SetRect(0,0,RES_X, RES_Y);

      dwRet = m_theDisplay.Blt(0, 0, m_surBackground, rcBackground);
      if(dwRet != DD_OK) {
          if(dwRet == DDERR_SURFACELOST) {
               m_surBackground->GetDDrawSurface()->Restore();
               m_surBackground->DrawBitmap((TCHAR*)(LPCSTR)m_csBackground, 0, 0);
          }
      }
      //...
      //
      // Bound moving sprite in the screen
      CPoint ptPos = m_sprShow.GetPosition();
      int iVelX = m_sprShow.GetVelX();
      if(ptPos.x >= RES_X-150 || ptPos.x <= 0) {
         m_sprShow.SetVelX(-iVelX);
      }
      //
      // Update first sprite
      m_sprShow.MoveSprite();
      m_sprShow.DrawSprite();
      //...
      // Update second sprite
      m_sprAnimShow.ChangeAnimationPhase();
      m_sprAnimShow.DrawSprite();
 
      HDC hdc;
      m_theDisplay.GetBackBuffer()->GetDC(&hdc);
        inpUpdateCursor(hdc);
      m_theDisplay.GetBackBuffer()->ReleaseDC(hdc);

      //...
      m_theDisplay.Present();
   }
}

Na zaΦßtku zavolßme funkci inpProcessInput() a na konci p°ed prohozenφm buffer∙ inpUpdateCursor(). Po zkompilovßnφ a spuÜt∞nφ vskutku uvidφte kurzor (poznßmka: myÜ musφ mφt nastaven² cooperative level DISCL_EXCLUSIVE, jinak bude d∞lat neoΦekßvanΘ v∞ci). Pou₧φvßm funkce pro zφskßnφ a uvoln∞nφ kontextu za°φzenφ naÜeho zadnφho bufferu (co₧ je v tuto chvφli nejlepÜφ °eÜenφ, i kdy₧ jsou funkce pomalΘ). Pokud bychom pou₧ili kontext za°φzenφ okna, kurzor by problikßval. Nezapome≥te kontext za°φzenφ uvolnit.

JeÜt∞ jsme zapomn∞li na jednu funkci. Je to funkce ShowCursor(), kterß podle parametru bu∩ zobrazφ Φi skryje kurzor. Jist∞ tuÜφte, ₧e funkce bude velmi krßtkß. Zkrßtka nastavφ sv²m jedin²m parametrem atribut t°φdy m_bShowCursor. I tuto funkci exportujte z knihovny:

input1.h:

void ShowCursor(BOOL bShow = TRUE) {m_bShowCursor = bShow;}

INPUT_API void inpShowCursor(BOOL bShow = TRUE);
 

input1.cpp:

void inpShowCursor(BOOL bShow)
{
    g_theInput.ShowCursor(bShow);
}

Zvolil jsem parametr bShow implicitnφ, jeliko₧ primßrnφ ·kol funkce ShowCursor() je zobrazovat kurzor.

10.3. Detekce stisku klßvesy a tlaΦφtka myÜi

Budeme pokraΦovat v implementaci zbyl²ch funkcφ, kterΘ zajiÜ¥ujφ detekci stisku a¥ klßvesy na klßvesnici nebo tlaΦφtka myÜi.

Jsou to funkce:

IsKeyDown()
IsLButtonDown()
IsLButtonUp()
IsRButtonDown()
IsRButtonUp()

Jist∞ jste si vÜimli, ₧e n∞kterΘ tyto funkce majφ zvlßÜtnφ parametr _Handling. KoneΦn∞ se k tomuto problΘmu dostßvßme. Musφte se toti₧ starat o to, zda-li stisk klßvesy nebo tlaΦφtka byl ji₧ obslou₧en nebo na toto obslou₧enφ teprve Φekß. Pokud je tlaΦφtko dole, ale bylo ji₧ obslou₧eno, funkce vracφ FALSE a nikoliv TRUE jak by se dalo Φekat. To samΘ platφ u klßvesnice. Ve v²sledku to zp∙sobφ, ₧e na jeden stisk tlaΦφtka myÜi se provede po₧adovanß operace pouze jednou a ne 100x (tolikrßt by se toti₧ zopakovala za jedno stisknutφ, p°edstavte si, ₧e stisk testuje v ka₧dΘ smyΦce ve funkci UpdateFrame()). Tento jev je samoz°ejm∞ n∞kdy ne₧ßdoucφ nap°. kdy₧ chcete pohybovat grafick²m objektem po monitoru, tak by to asi moc neÜlo, proto₧e by se objekt posunul v₧dy o kousek a p°i dalÜφm stisku op∞t o kousek atd. Proto je obsluha volitelnß.

Abychom mohli obslou₧it jak klßvesnici tak myÜ, musφme mφt dalÜφ pole, kde bude nastaveno zda-li byla klßvesa obslou₧ena Φi nikoliv (TRUE nebo FALSE).

Funkce IsRButtonDown():

//
// Checking left mouse button
if(m_MouseState.rgbButtons[MOUSEBUTTON_RIGHT]) { // 1
   //
   // If handling is not set, set it
   if(_Handling) { // 2
      //
      // User press mouse button...handling activated
      if(!m_IsHandled[MOUSEBUTTON_RIGHT]) {  // 3
         m_IsHandled[MOUSEBUTTON_RIGHT] = TRUE; // 4
         return TRUE; // 5
      }
      else {
         //
         // Return false because handling is already set.
         return FALSE; // 6
      }
   }
   else {
      //
      // User does not want hadling, simply return TRUE
      return TRUE; // 7
   }
}
else {
   //
   // Reset handling because mouse button is up
   m_IsHandled[MOUSEBUTTON_RIGHT] = FALSE; // 8
   //
   // Button is up so return FALSE
   return FALSE;
}


K≤d je na prvnφ pohled slo₧it², ale nenφ to zase tak hroznΘ. VÜimn∞te si, jak jednoduchß by to byla funkce, kdybychom se nemuseli zab²vat obsluhou. Abychom porozum∞li zbytku funkce, musφme p°idat dalÜφ atributy do naÜφ t°φdy. P°edevÜφm to bude pole m_IsHandled, kterΘ mß pouze dva prvky, proto₧e pot°ebujeme obslou₧it dv∞ tlaΦφtka myÜφ. Navφc je pot°eba definovat n∞jakΘ symbolickΘ konstanty:

#define MOUSEBUTTON_LEFT      0
#define MOUSEBUTTON_RIGHT     1

Definuji je rovnou pro ob∞ tlaΦφtka, proto₧e je z°ejmΘ, ₧e ob∞ funkce budou toto₧nΘ prßv∞ a₧ na tyto konstanty. Nynφ se koneΦn∞ dostaneme k principu funkce. Prvnφ podmφnka (1) zjiÜ¥uje zda-li je pravΘ resp. levΘ tlaΦφtku stisknutΘ. Testujeme hodnotu v poli rgbButtons, o kterΘm ji₧ byla °eΦ. Pokud nenφ ₧ßdnΘ tlaΦφtko stisknutΘ (8), musφme vynulovat obsluhu, abychom zaznamenali nov² stisk. V p°φpad∞, ₧e tlaΦφtko stisknutΘ je, musφme zjistit, zda-li chce u₧ivatel pou₧φt obsluhu (2). Pokud ne (7), jednoduÜe vrßtφme TRUE. ProblΘm nastßvß, kdy₧ u₧ivatel po₧aduje obsluhu. Zase tak velk² problΘm to nenφ. Zkrßtka se podφvßme do obslu₧nΘho pole (3) a zjistφme, jestli u₧ tlaΦφtko bylo obslou₧eno Φi nikoliv. Pokud ano, vracφme FALSE (6) a pokud ne vracφme TRUE (5), ale navφc je pot°eba nastavit (4), ₧e tlaΦφtko bylo prßv∞ obslou₧eno.

Funkce IsRButtonUp():

return (m_MouseState.rgbButtons[MOUSEBUTTON_LEFT]) ? FALSE : TRUE;

Tato funkce je proti svΘmu opaku velmi jednoduchß. Prost∞ jen vracφme TRUE, pokud je tlaΦφtko naho°e a FALSE pokud je stisknutΘ. Op∞t jsou funkce pro pravΘ a levΘ tlaΦφtko identickΘ.

Zb²vß metoda IsKeyDown():

//
// Check validity of input param
if(Key >= 256) {
    return FALSE;
}
//
// Checking specified key
if(KEYDOWN(m_arKeyboard, Key)) {
    if(bHandle) {
       //
       // User press key button...handling activated
       if(!m_IsHandled[Key]) {
           m_IsHandled[Key] = TRUE;
           return TRUE;
       }
       else {
          //
          // Return false because handling is already set.
          return FALSE;
       }
    }
    //
    // User does not want handling, simply return TRUE
    else {
        return TRUE;
    }
}
//
// Key is up, reset handling, return FALSE
else {
    m_IsHandled[Key] = FALSE;
    return FALSE;

}

Funkce je v principu naprosto stejnß jako u myÜi. Musφme ovÜem definovat makro KEYDOWN:

#define KEYDOWN(name, key) (name[key] & 0x80)

Makro zjistφ hodnotu v obslu₧nΘm poli klßvesnice, kterΘ mß tentokrßt 256 prvk∙ (pro ka₧dou klßvesu jeden). Na zaΦßtku je test vstupnφho parametru, kter² pouze hlφdß, aby u₧ivatel nep°esßhl meze pole. Zbytek funkce je naprosto analogick².

VÜech 5 uveden²ch funkcφ budeme exportovat:

input1.h:

INPUT_API BOOL inpIsRButtonDown(BOOL _Handling = TRUE);
INPUT_API BOOL inpIsLButtonDown(BOOL _Handling = TRUE);
INPUT_API BOOL inpIsRButtonUp();
INPUT_API BOOL inpIsLButtonUp();
INPUT_API BOOL inpIsKeyDown(int Key, BOOL bHandle);

input1.cpp:

BOOL inpIsRButtonDown(BOOL _Handling)
{
    return g_theInput.IsRButtonDown(_Handling);
}

BOOL inpIsLButtonDown(BOOL _Handling)
{
    return g_theInput.IsLButtonDown(_Handling);
}

BOOL inpIsRButtonUp()
{
    return g_theInput.IsRButtonUp();
}

BOOL inpIsLButtonUp()
{
    return g_theInput.IsLButtonUp();
}

BOOL inpIsKeyDown(int Key, BOOL bHandle)
{
    return g_theInput.IsKeyDown(Key, bHandle);
}

10.4. Funkce RestoreDevices()

A je tu ·pln∞ poslednφ funkce! Je to  funkce, kterß obnovφ p°φstup k za°φzenφm, kdy₧ aplikace ztratφ fokus (vlastn∞, kdy₧ ho op∞tovn∞ dostane). Funkce bude velmi jednoduchß:

HRESULT CInput::RestoreDevices()
{
    DWORD dwRet = 1;
// INPUT IS NOT INIT
    if(m_bInit) {
        dwRet = m_lpDIDKeyboard->Acquire();
        if(dwRet != DI_OK) {
            TRACE("Cannot reacquire the keyboard due %d\n", dwRet);
            return dwRet;
        }
        dwRet = m_lpDIDMouse->Acquire();
        if(dwRet != DI_OK) {
            TRACE("Cannot reacquire the mouse due\n", dwRet);
            return dwRet;
        }
    }
    return dwRet;
}

Prost∞ zavolßme funkci Acquire() pro ka₧dΘ za°φzenφ, kterΘ mßme. I tuto funkci exportujte. To je vÜe k naÜφ t°φd∞.

Nynφ jeÜt∞ malinko upravφme projekt DirectDraw, abychom vyzkouÜeli detekΦnφ funkce.

Vlo₧te hlaviΦkov² soubor input1.h rovn∞₧ do souboru directdraw.cpp a pak funkci MainWndProc() upravte takto:

LRESULT CALLBACK MainWndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    WORD low;

    switch(msg) {
    case WM_SETCURSOR:
        SetCursor(NULL);
        break;
    case WM_ACTIVATE:
        low = LOWORD(wParam);
        // Start flipping loop if app is activated
        if(low == WA_ACTIVE || low == WA_CLICKACTIVE) {
           theApp.GetControl()->Activate(TRUE);

           inpRestoreDevices();
        }
        // Stop flipping loop if app is deactivated
        if(low == WA_INACTIVE) {
           theApp.GetControl()->Activate(FALSE);
        }
        break;
    }

    return DefWindowProc(hWnd, msg, wParam, lParam);
}

Tato ·prava zajistφ sprßvnou funkΦnost za°φzenφ i pro ztracenφ a op∞tnΘm navrßcenφ fokusu okna.

Na ·pln² zßv∞r jeÜt∞ drobn∞ upravte funkci UpdateFrame() takto:

void CControl::UpdateFrame()
{
   DWORD dwRet;
   if(m_bReady) {

      inpProcessInput();
      //...
      CRect rcBackground;
      rcBackground.SetRect(0,0,RES_X, RES_Y);

      dwRet = m_theDisplay.Blt(0, 0, m_surBackground, rcBackground);
      if(dwRet != DD_OK) {
          if(dwRet == DDERR_SURFACELOST) {
               m_surBackground->GetDDrawSurface()->Restore();
               m_surBackground->DrawBitmap((TCHAR*)(LPCSTR)m_csBackground, 0, 0);
          }
      }
      //...
      //
      // Bound moving sprite in the screen
      CPoint ptPos = m_sprShow.GetPosition();
      int iVelX = m_sprShow.GetVelX();
      if(ptPos.x >= RES_X-150 || ptPos.x <= 0
|| inpIsKeyDown(DIK_SPACE, TRUE)) {
         m_sprShow.SetVelX(-iVelX);
      }
      //
      // Update first sprite
      m_sprShow.MoveSprite();
      m_sprShow.DrawSprite();
      //...
      // Update second sprite
      m_sprAnimShow.ChangeAnimationPhase();
      m_sprAnimShow.DrawSprite();
 
      HDC hdc;
      m_theDisplay.GetBackBuffer()->GetDC(&hdc);
        inpUpdateCursor(hdc);
      m_theDisplay.GetBackBuffer()->ReleaseDC(hdc);

      //...
      m_theDisplay.Present();
   }
}

VÜimn∞te si, ₧e naÜe funkce inpIsKeyDown() p°ijφmß parametr DIK_SPACE. To je konstanta, kterß je unikßtnφ pro ka₧dou klßvesu. ┌pln² seznam t∞chto konstant najdete na tΘto strßnce. VyzkouÜejte si, co by program d∞lal, kdybyste nepou₧ili obsluhu (funkce bude naprosto nepou₧itelnß). Po tΘto ·prav∞ zm∞nφ raketka sm∞r po ka₧dΘm stisku mezernφku.

DneÜnφ k≤d si samoz°ejm∞ m∙₧ete stßhnout jako obvykle v sekci Downloads.

10.5. Zßv∞r

Tφmto dφlem jsme ·pln∞ dokonΦili kurz DirectInput, kter² samoz°ejm∞ nebyl zcela ·pln² a podrobn², ale zßklady jsme zvlßdli. Jak jsem naznaΦil minule i na zaΦßtku dneÜnφ lekce, hodlßm p°φÜt∞ zaΦφt v∞tÜφ p°φklad, kde kompletn∞ vyu₧ijeme t°φdy CInput avÜak mohutn∞ upravφme DirectDraw.

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

Ji°φ Formßnek