SDL: Hry nejen pro Linux (13)

Michal Turek 17. 5. 2005

Na řadě je další vstupní zařízení, tentokrát se jedná o myš. Opět se budeme věnovat jak událostem, tak přímému přístupu.

Stisk tlačítka myši

Vždy, když uživatel stiskne některé tlačítko myši, vygeneruje SDL dvě události – SDL_MOUSEBUTTONDOWN a SDL_MOUSEBUTTONUP. První z nich je odeslána při stisku a druhá při uvolnění. V obou případech se podrobnosti o události hledají v podobjektu event.button, který byl odvozen ze struktury SDL_MouseButto­nEvent.

typedef struct
{
    Uint8 type;
    Uint8 button;
    Uint8 state;
    Uint16 x, y;
} SDL_MouseButtonEvent; 

Atribut type je klasicky nastaven na jméno události a proměnná button ukládá jméno tlačítka, což je jedna ze symbolických konstant SDL_BUTTON_LEFT, SDL_BUTTON_MIDDLE a SDL_BUTTON_RIGHT. Ve verzi 1.2.5 SDL dále přibyla jména SDL_BUTTON_WHEELUP a SDL_BUTTON_WHE­ELDOWN, která oznamují točení rolovacím kolečkem nahoru a dolů.

Stejně jako u klávesnice, i zde může být state nastaveno na SDL_PRESSED nebo SDL_RELEASED, tuto informaci však už máme k dispozici z parametru type. Proměnné x a y poskytují pozici myši v klientské oblasti okna při stisku, bod [0, 0] se nachází v levém horním rohu.

V následujícím příkladu program zachytává stisk levého tlačítka myši a jako reakci vypíše do konzole informaci o pozici v okně.

// Ošetření událostí
case SDL_MOUSEBUTTONDOWN:
    switch(event.button.button)
    {
    case SDL_BUTTON_LEFT:
        printf("BUTTON_LEFT - pos(%d,%d)\n",
                event.button.x,
                event.button.y);
        fflush(stdout);
        break;

    default:
        break;
    }
    break; 

Výstup programu po dvou stisknutích levého tlačítka:

BUTTON_LEFT - pos(65,103)
BUTTON_LEFT - pos(91,104) 

Událost pohybu myší

Pohyb myší oznamuje SDL zprávou SDL_MOUSEMOTION, podrobnosti se následně hledají v objektu event.motion odvozeném od SDL_MouseMoti­onEvent.

typedef struct
{
    Uint8 type;
    Uint8 state;
    Uint16 x, y;
    Sint16 xrel, yrel;
} SDL_MouseMotionEvent; 

Proměnná state definuje stavy tlačítek při pohybu. Pro zjištění, které je stisknuté a které ne, může být výhodné použít makro SDL_BUTTON(). Parametry x a y specifikují pozici kurzoru myši v okně, xrel a yrel obsahují relativní hodnotu posunu.

Po příchodu události o pohybu myši v příkladu níže vypíše program absolutní polohu kurzoru v okně, změnu polohy od minula a případně informaci o stisku tlačítek.

// Ošetření událostí
case SDL_MOUSEMOTION:
    printf("MOUSEMOTION - pos(%d,%d), relpos(%d,%d)%s%s%s\n",
        event.motion.x, event.motion.y,
        event.motion.xrel, event.motion.yrel,
        (event.motion.state & SDL_BUTTON(SDL_BUTTON_LEFT))
                ? ", left" : "",
        (event.motion.state & SDL_BUTTON(SDL_BUTTON_MIDDLE))
                ? ", middle" : "",
        (event.motion.state & SDL_BUTTON(SDL_BUTTON_RIGHT))
                ? ", right" : "");
    fflush(stdout);
    break; 

Pokud je program spuštěn, začnou se při pohybování myší generovat výpisy podobné následujícím.

MOUSEMOTION - pos(130,91), relpos(4,0)
MOUSEMOTION - pos(134,91), relpos(4,0)
MOUSEMOTION - pos(138,91), relpos(4,0)
MOUSEMOTION - pos(136,91), relpos(-2,0), left
MOUSEMOTION - pos(132,93), relpos(-4,2), left, right
MOUSEMOTION - pos(130,93), relpos(-2,0), left, right
MOUSEMOTION - pos(128,95), relpos(-2,2), left, right 

„Přímý“ přístup k myši

Stejně jako u klávesnice je i u myši možné používat metody přímého přístupu. Lze se tedy kdekoli v programu dotázat na aktuální polohu kurzoru nebo stisk tlačítek.

Uint8 SDL_GetMouseState(int *x, int *y); 

Tato funkce uloží na adresu ukazatelů v parametrech aktuální polohu myši v okně a vrátí bitové pole tlačítkových flagů. Pro rozlišení, které je stisknuté a které ne, je opět nejjednodušší použít bitový součin s makrem SDL_BUTTON(). Pokud nás zajímají pouze tlačítka, je možné předat do parametrů hodnoty NULL.

Před samotným přístupem k myši bývá vhodné zavolat funkci SDL_PumpEvents(), která aktualizuje informace v SDL.

Podobným způsobem se lze dotazovat i na relativní změny polohy od minulého volání této funkce nebo od zpracování události o pohybu myši.

Uint8 SDL_GetRelativeMouseState(int *x, int *y); 

Do proměnných x a y bude v příkladu níže uložena aktuální poloha myši, kód v sekci if se provede jen tehdy, je-li stisknuto levé tlačítko.

// Kdekoli v programu
int x, y;

SDL_PumpEvents();
if(SDL_GetMouseState(&x, &y) & SDL_BUTTON(SDL_BUTTON_LEFT))
    printf("Levé tlačítko na %d, %d.\n", x, y); 

Ruční změna polohy myši

Novou polohu myši lze specifikovat voláním funkce SDL_WarpMouse(), do parametrů se předávají požadované x a y souřadnice.

void SDL_WarpMouse(Uint16 x, Uint16 y); 

Tato funkce má jeden vedlejší efekt, způsobuje generování události SDL_MOUSEMOTION, což může být někdy, kvůli zacyklení, nežádoucí (v reakci na událost se ošetří změna polohy a myš se přesune na nové místo, tím se generuje další událost, která se opět ošetří, myš se přesune atd.). Asi nejjednodušší řešení spočívá v ignorování této „přebytečné“ události.

Při změnách natočení kamery ve 3D akčních hrách končí ošetření každého pohybu myši nastavením její polohy zpět na střed okna. Je to z důvodu, že kdyby opustila okno, mohlo by ztratit fokus (většinou po kliknutí na jiné okno při střelbě) a operační systém by v takovém případě přestal posílat zprávy. Hra by se každou chvíli stávala nehratelnou.

Implementace rotace kamery v závislosti na pohybech myši by mohla vypadat následovně.

// Ošetření událostí
case SDL_MOUSEMOTION:
    // SDL_WarpMouse() generuje SDL_MOUSEMOTION,
    // bez testu na střed okna by se aplikace zacyklila
    if(event.motion.x != GetWinWidth() >> 1
        || event.motion.y != GetWinHeight() >> 1)
    {
        m_cam.Rotate(event.motion.xrel,
            event.motion.yrel, GetFPS());

        // Přesun zpět doprostřed okna
        SDL_WarpMouse(GetWinWidth() >> 1,
            GetWinHeight() >> 1);
    }
    break; 

Všimněte si především ignorování událostí, které generuje funkce SDL_WarpMouse(). Mimochodem, tento kód jsme použili v příkladu Pohyb v mřížce z osmého dílu. Jedná se o metodu QGridApp::Pro­cessEvent(SDL_E­vent& event).

Další možností by mohl být zákaz pro myš opustit okno aplikace, zbavili bychom se tak neustálého měnění její polohy a následného rozlišování validity událostí. V SDL stačí zavolat funkci SDL_WM_GrabInput() s parametrem SDL_GRAB_ON (popsána v desátém dílu), v některých jiných knihovnách však takové vymoženosti nejsou.

Barevné kurzory

Ještě jedna specialitka na závěr. V sedmém dílu jsme si ukázali, jak požádat SDL, aby změnilo kurzor myši ze standardní šipky na jiný. Tato technika však měla tu nevýhodu, že kurzor mohl být pouze černobílý.

V tuto chvíli však už máme dostatek znalostí, abychom standardní kurzor myši vypnuli a vykreslovali si vlastní, na nějž už nejsou kladena žádná omezení.

// Pro vycentrování obrázku na aktivní bod kurzoru
// U šipek levý horní roh, u zaměřovačů střed apod.
#define POSUN_DOLEVA 0
#define POSUN_NAHORU 0

// Inicializace, skryje kurzor
SDL_ShowCursor(0);

// Vykreslování (kurzor by se měl vždy kreslit jako poslední)
SDL_Rect rect;

SDL_GetMouseState(&rect.x, &rect.y);
rect.x -= POSUN_DOLEVA;
rect.y -= POSUN_NAHORU;

SDL_BlitSurface(g_cur_press, NULL, g_screen, &rect); 

Tento kód předpokládá, že se scéna periodicky překresluje, nejlépe v „klasické“ herní smyčce nebo v reakci na pohyb myši, a pokaždé se kreslí úplně všechno.

Ukázkové programy

Kostky

Program zobrazuje v dolní části okna spoustu kostiček, které lze kliknutím myši zachytit a následně s nimi pohybovat. Jsou implementovány i kolize a také vlastní barevný kurzor, jenž se po kliknutí na nějakou kostku změní na jiný. (Zdrojový kód se zvýrazněním syntaxe.)

SDL 13

Download

Pokračování

Minule klávesnice, teď myš, takže přístě bude na řadě joystick…

Našli jste v článku chybu?
Podnikatel.cz: 5 důvodů, proč vaše e-maily končí ve spamu

5 důvodů, proč vaše e-maily končí ve spamu

120na80.cz: Jsou opalovací krémy pro děti jiné?

Jsou opalovací krémy pro děti jiné?

DigiZone.cz: Mobilní aplikace pro DVTV je tady

Mobilní aplikace pro DVTV je tady

120na80.cz: Běžecká lékárnička: jak si poradit?

Běžecká lékárnička: jak si poradit?

Root.cz: Quake slaví 20 let novou epizodou zdarma

Quake slaví 20 let novou epizodou zdarma

Podnikatel.cz: Eseróčko vs. živnost. Co vyhrává?

Eseróčko vs. živnost. Co vyhrává?

DigiZone.cz: Nova: technické pauzy každé 1. pondělí

Nova: technické pauzy každé 1. pondělí

Měšec.cz: Od kdy musí studenti platit pojistné?

Od kdy musí studenti platit pojistné?

DigiZone.cz: Skylink zapojil nový transpondér

Skylink zapojil nový transpondér

DigiZone.cz: Markíza: tady je předběžné opatření

Markíza: tady je předběžné opatření

Lupa.cz: Na základně u Dobříše se rozjel 3D tisk z kovu

Na základně u Dobříše se rozjel 3D tisk z kovu

Root.cz: Špína v počítačích: mrtvé myši, prach a pavouci

Špína v počítačích: mrtvé myši, prach a pavouci

Lupa.cz: eIDAS je tu. O co přijdeme u elektronických podpisů?

eIDAS je tu. O co přijdeme u elektronických podpisů?

Podnikatel.cz: Oblíbené Babišovo reverse charge. Potopilo je?

Oblíbené Babišovo reverse charge. Potopilo je?

Vitalia.cz: Jíme přesolené potraviny. Zrovna tyhle

Jíme přesolené potraviny. Zrovna tyhle

DigiZone.cz: TV Nova a její postoj k DVB-T2

TV Nova a její postoj k DVB-T2

DigiZone.cz: Skylink: Nova Sport volně

Skylink: Nova Sport volně

Vitalia.cz: 5 porcí ovoce a zeleniny: no ale jak na to?

5 porcí ovoce a zeleniny: no ale jak na to?

DigiZone.cz: ČTÚ květen: rušení TV vysílání narůstá

ČTÚ květen: rušení TV vysílání narůstá

Podnikatel.cz: "Okurku" vyřeší slevové servery. Už jim věřte

"Okurku" vyřeší slevové servery. Už jim věřte