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?
Vitalia.cz: Malovaná těhotenská bříška

Malovaná těhotenská bříška

Podnikatel.cz: 3 velké průšvihy obchodních řetězců

3 velké průšvihy obchodních řetězců

DigiZone.cz: Skylink o půlnoci vypnul 12 525

Skylink o půlnoci vypnul 12 525

Měšec.cz: Se stavebkem k soudu už (většinou) nemusíte

Se stavebkem k soudu už (většinou) nemusíte

120na80.cz: I tuto vodu můžete pít

I tuto vodu můžete pít

Měšec.cz: Kurzy platebních karet: vyplatí se platit? (TEST)

Kurzy platebních karet: vyplatí se platit? (TEST)

Vitalia.cz: Signál roztroušené sklerózy: brnění končetin

Signál roztroušené sklerózy: brnění končetin

Podnikatel.cz: Fotogalerie: Jesenka už má skoro 50 let

Fotogalerie: Jesenka už má skoro 50 let

Vitalia.cz: Nejdůležitější změny v potravinářské novele

Nejdůležitější změny v potravinářské novele

Vitalia.cz: Tohle je Břicháč Tom, co zhubnul 27 kg

Tohle je Břicháč Tom, co zhubnul 27 kg

Měšec.cz: Udali ho na nelegální software a přišla Policie

Udali ho na nelegální software a přišla Policie

Lupa.cz: Japonská invaze. Proč SoftBank kupuje ARM?

Japonská invaze. Proč SoftBank kupuje ARM?

DigiZone.cz: Sázka na e-sporty stanici Prima vychází

Sázka na e-sporty stanici Prima vychází

Lupa.cz: eIDAS: Nepřehnali jsme to s výjimkami?

eIDAS: Nepřehnali jsme to s výjimkami?

120na80.cz: 7 překážek při odvykání kouření

7 překážek při odvykání kouření

Měšec.cz: Test: Výběry z bankomatů v cizině a kurzy

Test: Výběry z bankomatů v cizině a kurzy

Vitalia.cz: Patří maso do dětského jídelníčku?

Patří maso do dětského jídelníčku?

Podnikatel.cz: Italské těstoviny nebyly k mání, tak je začal vyrábět

Italské těstoviny nebyly k mání, tak je začal vyrábět

Podnikatel.cz: Přiznal prodej padělků. Pokuta ho nemine

Přiznal prodej padělků. Pokuta ho nemine

Podnikatel.cz: Selhala pokladna k EET. Kdo zaplatí pokutu?

Selhala pokladna k EET. Kdo zaplatí pokutu?