Jak na joystick v C++

V tomto článku si ukážeme, jak pracovat s joystickem v „běžné“ aplikaci, tedy nikoliv a aplikaci DirectX.
Vytvořme si aplikaci, jejíž jediné (hlavní) okno bude popup přes celou obrazovku. Ukončit samozřejmě půjde pomocí Alt-F4, a navíc si přidáme možnost ukončení na <Escape>. Vytvoříme tedy takto hlavní okno aplikace:

BOOL CJoystickDemoApp::InitInstance()
{
#ifdef _AFXDLL
  Enable3dControls();      // Call this when using MFC in a shared DLL
#else
  Enable3dControlsStatic();  // Call this when linking to MFC statically
#endif
  m_mainWnd = new CMainWnd();
  m_mainWnd->CreateEx(WS_EX_TOPMOST,
    AfxRegisterWndClass(0, ::LoadCursor(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDC_CROSSHAIR)),
      (HBRUSH)(COLOR_WINDOW+1), ::LoadIcon(NULL, IDI_APPLICATION)),
    "Joystickmain", WS_POPUP, 0,0,
    GetSystemMetrics(SM_CXSCREEN),
    GetSystemMetrics(SM_CYSCREEN),NULL, NULL, NULL);
  m_pMainWnd = m_mainWnd;
  m_pMainWnd->ShowWindow(SW_SHOW);
  m_pMainWnd->UpdateWindow();
  return TRUE;
}

Do zdrojů jsem předtím přidal vlastní kurzor, „zaměřovací kříž“, který bude reagovat na pohyb joysticku. Dále je do zdrojů přidaná ještě jedna ikona, která se bude zobrazovat při stisknutí 1. tlačítka joysticku v místě aktuální polohy kurzoru.

Pokud chceme, aby naše okno dostávalo příslušné zprávy od joysticku, musíme mu ho přiřadit funkcí joySetCapture:

MMRESULT joySetCapture(
  HWND hwnd,   
  UINT uJoyID, 
  UINT uPeriod,
  BOOL fChanged
);

Jednotlivé parametry mají tento význam:

  • hwnd – handle okna, které bude dostávat zprávy od joysticku
  • uJoyID – identifikátor joysticku (JOYSTICKID1 nebo JOYSTICKID2)
  • uPeriod – perioda (v milisekundách), ve které bude okno dostávat zprávy joysticku
  • fChanded – pokud je nastaven na TRUE, zprávy o pohybu joysticku jsou posílány pouze tehdy, pokud změna polohy přesáhne prahovou hodnotu nastavenou funkcí joySetThreshold; v opačném případě jsou zprávy posílány v intervalu uvedeném v parametru uPeriod.

MMRESULT joySetThreshold(
  UINT uJoyID,  / identifikátor joysticku
  UINT uThreshold  // prahová hodnota
);

Uvedli jsme si již 2 ze tří klíčových funkcí pro práci s joystickem. Po použití joysticku ho uvolníme funkcí joyReleaseCapture:

MMRESULT joyReleaseCapture(
  UINT uJoyID  // identifikátor joysticku
);

Tím jsme si ukázali začátek a konec, nyní k tomu hlavnímu, co je „uprostřed“. Ještě upozorním, že si trochu zjednodušíme situaci tím, že budeme uvažovat pouze jeden joystick. Když se podíváte do dokumentace na funkce a zprávy , kterými se zde budu zabývat, zjistíte, že všechny mají svůj ekvivalent lišící se pouze tím, že místo „1“ je v jejich názvu „2“. Existují v zásadě 4 dvojice zpráv joysticku, jejich názvy již napovídají o jejich významu:

MM_JOY1BUTTONDOWN (resp. MM_JOY2BUTTONDOW)

Došlo ke stlačení některého z tlačítek joysticku. Význam jednotlivých parametrů je následující: wParam určuje, která tlačítka jsou aktuálně stačená a stav kterého se změnil:

  • JOY_BUTTON1CHG – změnil se stav 1. tlačítka
  • JOY_BUTTON2CHG - změnil se stav 2. tlačítka
  • JOY_BUTTON3CHG - změnil se stav 3. tlačítka
  • JOY_BUTTON4CHG - změnil se stav 4. tlačítka
  • JOY_BUTTON1 – je stisknuto 1. tlačítko
  • JOY_BUTTON2 – je stisknuto 2. tlačítko
  • JOY_BUTTON3 – je stisknuto 3. tlačítko
  • JOY_BUTTON4 – je stisknuto 4. tlačítko

    V parametru lParam jsou pak uloženy souřadnice joysticku takto

    x-pozice = LOWORD(lParam)

    y-pozice = HIWORD(lParam)

    Souřadnice joysticku mají v obou osách v rozsahu 0 – 65535 (16bitové číslo), jak uvidíme za chvíli.

    MM_JOY1BUTTONUP (resp. MM_JOY2BUTTONUP)

    Došlo k uvolnění některého tlačítka joysticku. Význam parametrů je stejný u zprávy MM_JOY1BUTTONDOWN.

    MM_JOY1MOV (resp. MM_JOY2MOVE)

    Došlo ke změně polohy joysticku (v ose x nebo y). Parametr wParam určuje stav tlačítek joysticku, může mít některou z hodnot:

  • JOY_BUTTON1 – je stisknuto 1. tlačítko
  • JOY_BUTTON2 – je stisknuto 2. tlačítko
  • JOY_BUTTON3 – je stisknuto 3. tlačítko
  • JOY_BUTTON4 – je stisknuto 4. tlačítko

    V parametru lParam jsou pak uloženy souřadnice joysticku:

    x-pozice = LOWORD(lParam)

    y-pozice = HIWORD(lParam)

    MM_JOY1ZMOVE (resp. MM_JOY2ZMOVE)

    Došlo ke změně z-souřadnice joysticku. wParam určuje stav tlačítek joysticku

  • JOY_BUTTON1 – je stisknuto 1. tlačítko
  • JOY_BUTTON2 – je stisknuto 2. tlačítko
  • JOY_BUTTON3 – je stisknuto 3. tlačítko
  • JOY_BUTTON4 – je stisknuto 4. tlačítko
lParam pak určuje z-souřadnici joysticku.

Přesný význam z-souřadnice nemohu vyzkoušet, mám k disposici obyčejný 2osý joystick se 4 tlačítky, ale mělo by jít o nějaký ten „klobouček“ na vrcholu páky, o němž však vím jen teoreticky, popř. některé joysticky mají jakýsi „slider“, řekněme „plynové kolečko“. Pokud máte někdo k disposici sofistikovanější joystick, můžete to vyzkoušet a podělit se o přesné poznatky z praxe.

Nyní se vraťme k naší ukázkové aplikaci. Řekněme, že budeme chtít, aby při vyklonění joysticku v jedné ose se v příslušném směru posunul kurzor na obrazovce, dále pokud při tomto posunu bude současně drženo stlačené 2. tlačítko, aby kurzor kreslil čáru ve stopě svého pohybu, a nakonec aby při stisknutí 1. tlačítka se v místě kurzoru nakreslila ikona.

Změnu kurzoru okna již máme zajištěnu jeho načtením ze zdrojů (ve funkci CWnd::CreateEx):

//....
AfxRegisterWndClass(0, ::LoadCursor(AfxGetInstanceHandle(),
  MAKEINTRESOURCE(IDC_CROSSHAIR)),
//....

Ikonu si načteme do členské proměnné okna (typu HICON) třeba hned v konstrukturu:

CMainWnd::CMainWnd()
{
  m_hIconShot = LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_SHOT));
}

Budeme tedy zachytávat zprávy MM_JOY1BUTTONDOWN a MM_JOY1MOVE. Musíme tedy přepsat proceduru okna - WindowProc:

LRESULT CMainWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
  switch ( message )
  {
    case MM_JOY1BUTTONDOWN:
      OnMM_JOY1BUTTONDOWN(wParam, lParam);
      break;
    case MM_JOY1MOVE:
      OnMM_JOY1MOVE(wParam, lParam);
      break;
    case WM_KEYDOWN:
      if ( wParam == VK_ESCAPE )
        DestroyWindow();
      break;
  } 
  return CWnd::WindowProc(message, wParam, lParam);
}

Vzhledem k tomu, že musíme nechat joysticku nějaký rozsah souřadnic kolem středu, který budeme považovat za klidovou polohu, řekněme třeba, že za výkyv budeme považovat souřadnice menší než 28000 a větší než 36000. Funkce „ošetřující pohyb joysticku pak vypadá takto:

#define _JOY_STEP 10

void CMainWnd::OnMM_JOY1MOVE(WPARAM wParam, LPARAM lParam)
{
  POINT ptCursor;
  HDC hdc;
  GetCursorPos(&ptCursor);
  int x = LOWORD(lParam);
  int y = HIWORD(lParam);
  BOOL Button2Down = wParam & JOY_BUTTON2;
  if ( Button2Down )
  {
    hdc = ::GetDC(m_hWnd);
    MoveToEx(hdc, ptCursor.x, ptCursor.y, NULL);
  }
  if ( x < 28000 )
    ptCursor.x -= _JOY_STEP;
  if ( x > 36000 )
    ptCursor.x += _JOY_STEP;
  if ( y < 28000 )
    ptCursor.y -= _JOY_STEP;
  if ( y > 36000 )
    ptCursor.y += _JOY_STEP;
  SetCursorPos(ptCursor.x, ptCursor.y);
  if ( Button2Down )
  {
    LineTo(hdc, ptCursor.x, ptCursor.y);
    ::ReleaseDC(m_hWnd, hdc);
  }
}

Změnou hodnoty _JOY_STEP ovlivníme rychlost pohybu joysticku.

Vykreslení ikony na stačení 1. tlačítka joysticku pak vypadá takto:

void CMainWnd::OnMM_JOY1BUTTONDOWN(WPARAM wParam, LPARAM lParam)
{
  POINT ptCursor;
  HDC hdc;
  GetCursorPos(&ptCursor);
  if (wParam & JOY_BUTTON1CHG)
  {
    hdc = ::GetDC(m_hWnd);
    DrawIcon(hdc, ptCursor.x - 16, ptCursor.y - 16, m_hIconShot);
    PlaySound(MAKEINTRESOURCE(IDR_WAVE_SHOT), AfxGetInstanceHandle(),
      SND_RESOURCE | SND_ASYNC);
    ::ReleaseDC(m_hWnd, hdc);
  }
}

Jak vidíte, kromě nakreslení ikony ještě přehrajeme pomocí funkce PlaySound zvuk (formátu WAVE) přidaný do zdrojů.

Ukázkový projekt si můžete stáhnout zde: joystick.zip.

Váš názor Další článek: Aktualizace WinOnCD 3.8 PE pro Windows XP

Témata článku: Software, Programování, Změna polohy, Slider, Hicon, Přesná poloha, Joystick, Význam, Message, Přesná pozice, Tlačítko, Jak, Přesná hodnota, Klidový stav, Změna poloh, Escape, Joy


Určitě si přečtěte

Starlink podle betatesterů: Rychlejší a levnější než satelitní internet v Česku

Starlink podle betatesterů: Rychlejší a levnější než satelitní internet v Česku

** Reddit se začíná plnit zkušenostmi se Starlinkem ** Při přímé viditelnosti dá i 120 Mb/s ** Klasický satelitní internet už teď dalece překonává

Jakub Čížek | 48

Jakub Čížek
StarlinkPoskytovatelé internetu
Šmírování kamerami Googlu: Koukněte, co šíleného se objevilo na Street View

Šmírování kamerami Googlu: Koukněte, co šíleného se objevilo na Street View

Google stále fotí celý svět do své služby Street View. A novodobou zábavou je hledat v mapách Googlu vtipné záběry. Podívejte se na výběr nejlepších!

redakce | 4

redakce
Mapy GoogleStreet View
Vybíráme nejlepší monitory: Od úplně levných až po displeje na rozmazlování očí

Vybíráme nejlepší monitory: Od úplně levných až po displeje na rozmazlování očí

** Vybrali jsme nejlepší monitory na práci i pořádné hraní ** Nejlevnější monitor s kvalitním panelem nestojí ani tři tisíce ** Rozlišení 4K a větší obrazovka už není nedostupný luxus

David Polesný | 30

David Polesný
Monitory
Vybrali jsme 12 programovatelných hraček a stavebnic pro děti a jejich rodiče

Vybrali jsme 12 programovatelných hraček a stavebnic pro děti a jejich rodiče

** Získejte děti pro matematiku a základy techniky ** Kupte jim hračku nebo stavebnici, které vdechnou vlastní život ** Vybrali jsme stavebnice pro malé caparty i budoucí experty

Jakub Čížek | 10

Jakub Čížek
Stavebnice
Šéf Spotify: Budeme zdražovat. Náš obsah se zlepšil
Markéta Mikešová
PředplatnéSpotify
Vodafonu se zhroutila kabelovka. Síť bývalého UPC má výpadky
Lukáš Václavík
VodafoneUPC
Lidl buduje chytrou domácnost, propojí všechno se vším
Lukáš Václavík
LidlChytrá domácnostIoT

Aktuální číslo časopisu Computer

Jak prodloužit výdrž notebooku

Velké testy: gamepady a inkoustové tiskárny

Důkladný test Sony Playstation 5