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…

Ohodnoťte jako ve škole:

Průměrná známka 3,11

Našli jste v článku chybu?
Zasílat nově přidané názory e-mailem
DigiZone.cz: Druhá anglická liga pro Digi TV

Druhá anglická liga pro Digi TV

Vitalia.cz: Dnešní patolog o mrtvolu téměř nezavadí

Dnešní patolog o mrtvolu téměř nezavadí

120na80.cz: Tady se vaří padělané léky

Tady se vaří padělané léky

DigiZone.cz: Mafra varuje před stíháním za pomluvu

Mafra varuje před stíháním za pomluvu

Lupa.cz: Schváleno: Rockaway může převzít Heureku

Schváleno: Rockaway může převzít Heureku

Vitalia.cz: Před, nebo po snídani? Kdy je lepší čistit si zuby

Před, nebo po snídani? Kdy je lepší čistit si zuby

Lupa.cz: Kam si doma dáte internet věcí? Na polici?

Kam si doma dáte internet věcí? Na polici?

Podnikatel.cz: Proměny stavebnice Seva. Znáte ji?

Proměny stavebnice Seva. Znáte ji?

Vitalia.cz: Muži kouří 24 cigaret denně, ženy o dost míň

Muži kouří 24 cigaret denně, ženy o dost míň

Lupa.cz: Válka e-shopů. Alza končí s Heurekou

Válka e-shopů. Alza končí s Heurekou

Vitalia.cz: Taky ji kupujete? Je šizená

Taky ji kupujete? Je šizená

120na80.cz: 5 triků, jak zastavit krvácení po holení

5 triků, jak zastavit krvácení po holení

DigiZone.cz: Šlágr TV: pokuta 100 tisíc za on-line

Šlágr TV: pokuta 100 tisíc za on-line

Lupa.cz: Přenos hokeje padal kvůli útoku, tvrdí O2

Přenos hokeje padal kvůli útoku, tvrdí O2

120na80.cz: Co jí dělá? Sklerotizaci

Co jí dělá? Sklerotizaci

Podnikatel.cz: Šizený guláš na pultě. Jako Lidl to nedělejte

Šizený guláš na pultě. Jako Lidl to nedělejte

DigiZone.cz: Změní se veřejnoprávní status ČT?

Změní se veřejnoprávní status ČT?

Vitalia.cz: Proč máme prasklý chléb nejraději?

Proč máme prasklý chléb nejraději?

Vitalia.cz: 7 nemocí očí, které musíte léčit včas

7 nemocí očí, které musíte léčit včas

Root.cz: Zákon o hazardu je v rozporu s ústavou

Zákon o hazardu je v rozporu s ústavou