Hlavní navigace

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?

11. 8. 2005 19:43

V podstate to same je i v prikladu ve clanku akorat se nepouziva promenna, ale testuje se poloha mysi - predpoklada se presouvani vzdy na stejne misto.

Vase reseni nepocita se dvema validnimi udalostmi ve fronte - tj. prvni (validni) se osetri, druha (validni) se bude ignorovat a treti (nevalidni, vlivem osetreni prvni) bez problemu projde. Mozna by to chtelo vytvorit program a odzkouset pri praktickem provozu, ale moc bych na uspech nevsazel, udalosti pohybu mysi se generuji hodne casto. A bud…

14. 7. 2005 9:20

esmeralda (neregistrovaný)
V prikladu pri obsluze zpravy MOUSEMOTION je napsano, ze by zde bylo vhodne zavolat SDL_WarpMouse, ale ze je pak problem se zacyklenim. Problem lze vyresit jednoduse. V dane metode si nadefinujeme statickou promenou:
static BOOL bWarpCalled = FALSE;

pokud nekd v metode zavolame SDL_WarpMouse tak tuto promenou nastavime na TRUE. Zacatek obsluhy udalosti MOUSEMOTION by pak vypadal nasledovne:

case SDL_MOUSEMOTION:
if(bWarpCalled)
{
bWarpCalled = FALSE;
return TRUE;
}
....
break;











Podnikatel.cz: Platební brány a EET? Stále s otazníkem

Platební brány a EET? Stále s otazníkem

Vitalia.cz: Jak koupit Mikuláše a nenaletět

Jak koupit Mikuláše a nenaletět

Lupa.cz: Propustili je z Avastu, už po nich sahá ESET

Propustili je z Avastu, už po nich sahá ESET

Podnikatel.cz: Přehledná titulka, průvodci, responzivita

Přehledná titulka, průvodci, responzivita

Root.cz: Vypadl Google a rozbilo se toho hodně

Vypadl Google a rozbilo se toho hodně

Vitalia.cz: Říká amoleta - a myslí palačinka

Říká amoleta - a myslí palačinka

Vitalia.cz: Chtějí si léčit kvasinky. Lék je jen v Německu

Chtějí si léčit kvasinky. Lék je jen v Německu

Podnikatel.cz: Prodává přes internet. Kdy platí zdravotko?

Prodává přes internet. Kdy platí zdravotko?

Měšec.cz: U levneELEKTRO.cz už reklamaci nevyřídíte

U levneELEKTRO.cz už reklamaci nevyřídíte

Vitalia.cz: Paštiky plné masa ho zatím neuživí

Paštiky plné masa ho zatím neuživí

Lupa.cz: Google měl výpadek, nejel Gmail ani YouTube

Google měl výpadek, nejel Gmail ani YouTube

120na80.cz: Rakovina oka. Jak ji poznáte?

Rakovina oka. Jak ji poznáte?

Měšec.cz: Zdravotní a sociální pojištění 2017: Připlatíte

Zdravotní a sociální pojištění 2017: Připlatíte

Měšec.cz: Kdy vám stát dá na stěhování 50 000 Kč?

Kdy vám stát dá na stěhování 50 000 Kč?

Lupa.cz: Avast po spojení s AVG propustí 700 lidí

Avast po spojení s AVG propustí 700 lidí

Lupa.cz: Proč firmy málo chrání data? Chovají se logicky

Proč firmy málo chrání data? Chovají se logicky

Měšec.cz: Finančním poradcům hrozí vracení provizí

Finančním poradcům hrozí vracení provizí

Vitalia.cz: „Připluly“ z Německa a možná obsahují jed

„Připluly“ z Německa a možná obsahují jed

Podnikatel.cz: 1. den EET? Problémy s pokladnami

1. den EET? Problémy s pokladnami

Vitalia.cz: To není kašel! Správná diagnóza zachrání život

To není kašel! Správná diagnóza zachrání život