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.

widgety

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: Takhle se prodávají mražené potraviny

Takhle se prodávají mražené potraviny

Podnikatel.cz: Insolvence LevneElektro.cz? Začíná boj o peníze

Insolvence LevneElektro.cz? Začíná boj o peníze

Lupa.cz: Jak levné procesory změnily svět?

Jak levné procesory změnily svět?

Lupa.cz: Jak se prodává firma za miliardu?

Jak se prodává firma za miliardu?

Lupa.cz: Patička e-mailu závazná jako vlastnoruční podpis?

Patička e-mailu závazná jako vlastnoruční podpis?

DigiZone.cz: DVB-T2 ověřeno: seznam TV zveřejněn

DVB-T2 ověřeno: seznam TV zveřejněn

Vitalia.cz: Jak Ondra o astma přišel

Jak Ondra o astma přišel

Podnikatel.cz: Znáte už 5 novinek k #EET

Znáte už 5 novinek k #EET

Lupa.cz: Hackeři mají data z půlmiliardy účtů Yahoo

Hackeři mají data z půlmiliardy účtů Yahoo

Podnikatel.cz: Kalousek chce odklad EET. Předvolební tah?

Kalousek chce odklad EET. Předvolební tah?

Podnikatel.cz: Chystá se smršť legislativních novinek

Chystá se smršť legislativních novinek

Vitalia.cz: 5 důvodů, proč jet na výlov rybníka

5 důvodů, proč jet na výlov rybníka

Vitalia.cz: Tahák, jak vyzrát nad zápachem z úst

Tahák, jak vyzrát nad zápachem z úst

DigiZone.cz: Mordparta: trochu podchlazený 87. revír

Mordparta: trochu podchlazený 87. revír

DigiZone.cz: Wimbledon na Nova Sport až do 2019

Wimbledon na Nova Sport až do 2019

DigiZone.cz: Test: brýle pro virtuální realitu Exos Urban

Test: brýle pro virtuální realitu Exos Urban

Vitalia.cz: Jsou vegani a vyrábějí nemléko

Jsou vegani a vyrábějí nemléko

Podnikatel.cz: EET pro e-shopy? Postavené na hlavu

EET pro e-shopy? Postavené na hlavu

Podnikatel.cz: Babišovi se nedá věřit, stěžovali si hospodští

Babišovi se nedá věřit, stěžovali si hospodští

Vitalia.cz: Test dětských svačinek: Tyhle ne!

Test dětských svačinek: Tyhle ne!