Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 4 - Zvuky, hudba, klávesnice a myš v MonoGame

V minulé lekci, Kreslíme a píšeme v MonoGame, jsme si ukázali vykreslování spritů a písma.

Dnes se podíváme na hudbu, zvukové efekty a reakci na klávesy.

Načtení obsahu

Stejně jako minule sprity, i zvuky a hudbu musíme nejprve načíst. Zvukové efekty jsou typu SoundEffect, hudba je typu Song. Do třídy s hrou tedy přidáme 2 proměnné:

private SoundEffect zvukRada;
public Song hudba_zardax;

Přesuňme se do metody LoadContent() a zvuky načtěme z obsahu:

zvukRada = Content.Load<SoundEffect>(@"Zvuky\zvuk_rada");
hudba_zardax = Content.Load<Song>(@"Zvuky\hudba_zardax");

Hudba

Přehrávání hudby nám zajišťuje statická třída MediaPlayer. Zajímat nás bude především metoda Play(), která bere jako parametr objekt typu Song, tedy hudbu, kterou chceme přehrát. Play() zavoláme na konci metody LoadContent(), prototože právě tehdy je hudba načtena.

MediaPlayer.Play(hudba_zardax);

Vyzkoušíme :)

Pro potřeby dalšího programování si vytvoříme veřejnou metodu PrepniHudbu(), která bude brát jako parametr Song. Pokud tento song ještě nehraje, spustí ho. Pokud již hraje, nechá ho hrát a neudělá nic. Touto kontrolou se vyhneme situacím, kdy např. přijdeme do lokace, kde má hrát hudba co již hraje a tato hudba by začala hrát odznovu, přestože může prostě pokračovat. Aktuálně přehrávané písničky najdeme ve vlastnosti Query.ActiveSong. Jak je vidět, přehrávač má i jakýsi playlist, ten my v seriálu ale nevyužijeme. Metoda by mohla vypadat takto:

public void PrepniHudbu(Song hudba)
{
    // Nehraje již ta samá hudba?
    if (MediaPlayer.Queue.ActiveSong != hudba)
        MediaPlayer.Play(hudba);
}

Původní přehrání hudby změníme na volání této metody. Ještě dodáme, aby se hudba opakovala nastavením vlastnosti IsRepeating. Konec metody LoadContent() tedy vypadá takto:

MediaPlayer.IsRepeating = true;
PrepniHudbu(hudba_zardax);

Pro zachování zdravého rozumu doporučuji obsah metody PrepniHudbu() pro účely vývoje zakomentovat, nebo ji budete po několika dnech programování a zkoušení hry nenávidět :)

Zvukové efekty

Dále zde máme zvukové efekty, ty jsou velmi jednoduché. Kdykoli je potřebujeme přehrát, zavoláme na nich pouze metodu Play(). Zvuk si zkusíme spustit opět na konci metody LoadContent():

zvukRada.Play();

Vyzkoušíme.

Klávesnice

Nějakým způsobem bude hráč hru samozřejmě ovládat. XNA nám poskytuje širokou podporu ovladačů, tedy klávesnice, myši, ale i dalších zařízení, jako joysticků nebo gamepadů. My se teď zaměříme zejména na klávesnici.

Pro práci s klávesnicí máme k dispozici třídu Keyboard. Zajímat nás na ní bude zejména statická metoda GetState(), která nám vrátí stav aktuálně stisknutých kláves. Na instanci tohoto stavu se můžeme poté ptát, zda obsahuje konkrétní klávesu. Instance stavu kláves je typu KeyboardState. Veřejnou proměnnou tohoto typu si přidáme do třídy s hrou:

public KeyboardState klavesy;

Již víme, že místo, kde se budeme na stav kláves ptát, je právě metoda Update(). Přesuňme se do ní a za zpracováním GamePadu pro XBox si uložme aktuální instanci stavu kláves:

klavesy = Keyboard.GetState();

Nyní máme v klavesy uložený současný stav klávesnice. Na instanci stavu máme 3 užitečné metody:

  • IsKeyDown() - Zeptá se, zda je stisknuta určitá klávesa.
  • IsKeyUp() - Zeptá se, zda je uvolněna určitá klávesa.
  • GetPressedKeys() - Vrátí pole všech stisknutých kláves.

Jednotlivé klávesy rozlišujeme pomocí výčtového typu Keys. Prvek Keys (tedy jednu konkrétní klávesu) berou jako parametr první 2 metody. 3. metoda vrací pole prvků Keys.

Zeptejme se, zda je stisknuta klávesa Enter a pokud ano, přehrajme náš zvuk:

if (klavesy.IsKeyDown(Keys.Enter))
    zvukRada.Play();

Vyzkoušíme.

Vidíme, že celý postup má jednu nevýhodu - klávesa se neustále vyhodnocuje po celou dobu, co ji držíme. Zvuků tedy hrají desítky přes sebe a nám z toho třeští hlava. Někdy opravdu můžeme chtít, aby hra takto reagovala na určitou klávesu, např. pokud držíme plyn, auto stále akceleruje, jakmile ho pustíme, otáčky klesají. Někdy ale chceme, aby se klávesa vyhodnotila jen jednou a podruhé pouze tehdy, když je puštěna a poté znovu stisknuta. Ukažme si, jak na to. K naší proměnné klavesy si výše deklarujme ještě jednu, reprezentující minulý stav kláves:

public KeyboardState klavesy, klavesyMinule;

Uložení stavu kláves v Update() upravme taky, aby v proměnné klavesyMinule byl minulý stav klávesnice:

klavesyMinule = klavesy;
klavesy = Keyboard.GetState();

Nyní není nic jednoduššího, než se zeptat, zda bylo Enter minule puštěné a nyní je stisknuté:

if (klavesy.IsKeyDown(Keys.Enter) && klavesyMinule.IsKeyUp(Keys.Enter))
    zvukRada.Play();

Opět vyzkoušíme.

Jelikož takovéto chování budeme chtít často a musíme do podmínky psát tu samou klávesu 2x, vytvoříme si k tomuto účelu metodu NovaKlavesa(). Ta bude v parametru brát prvek výčtového typu Keys a zjistí, zda je klávesa nyní držena a minule držena nebyla:

public bool NovaKlavesa(Keys klavesa)
{
    return klavesy.IsKeyDown(klavesa) && klavesyMinule.IsKeyUp(klavesa);
}

Metoda je veřejná, protože ji budeme používat časem i mimo třídu s hrou. Upravíme naši reakci na Enter v Update():

if (NovaKlavesa(Keys.Enter))
    zvukRada.Play();

Jistě uznáte, že nyní je zápis mnohem přehlednější.

Myš

Ještě máme trochu času a i když myš v našem tetrisu používat nebudeme, ukážeme si, jak se s ní pracuje. Neprve použijeme standardní kurzor Windows. Ten je standardně na okně s hrou vypnutý, zapneme ho přepnutím vlastnosti IsMouseVisible přímo na hře v metodě Initialize():

IsMouseVisible = true;

Takový kurzor je docela ošklivý, proto ho zas vypneme :) Jak jsem se již zmínil, naše hra používat myš nebude, proto jsem pro vás nepřipravil žádný sprite s kurzorem myši. Pokud chcete, nějaký si nakreslete a přidejte, já zde místo kurzoru vykreslím písmeno "X".

Podobně jako klávesnice měla třídu Keyboard, pro myš nám XNA nachystalo třídu Mouse. Funguje úplně stejně, opět tu máme stav MouseState. Novou proměnnou mys tohoto typu si přidáme do třídy s hrou:

public MouseState mys;

V Update() si stav uložíme, stejně jako jsme to udělali se stavem kláves:

mys = Mouse.GetState();

Nyní se přesuneme do metody Draw(), kde vykreslíme "kurzor" a dále vypíšeme aktuální souřadnice a stav tlačítka.

Začneme kurzorem, zde stačí použít vlastností X a Y na instanci stavu myši, které označují pozici kurzoru. Kurzor vykreslíme jako poslední, aby byl nad vším ostatním (samozřejmě ale před spriteBatch.End()):

spriteBatch.TextSeStinem(fontCourierNew, "X", new Vector2(mys.X, mys.Y), Color.White);

Nyní vykreslíme pozici myši a stav tlačítka:

string text = String.Format("{0},{1} {2}", mys.X, mys.Y, mys.LeftButton);
spriteBatch.TextSeStinem(fontCourierNew, text, new Vector2(0, 700), Color.White);

Kolize kurzoru a obdélníku

Nakonec si ještě ukážeme, jak udělat kolizi obdélníku a bodu. Již víme, že XNA má strukturu Vector2. Ono má podobných struktur více, my použijeme Rectangle (obdélník) a Point (bod). Rozdíl mezi bodem a vektorem je ten, že bod má celočíselné souřadnice a postrádá metody jako vektorový součet. Přesto je v XNA zvykem používat téměř vždy vektor, jelikož s nimi pracují vnitřní metody XNA, např. metody vykreslovací.

Vytvořme si obdélník, který nastavíme přibližně na pozici robota na pozadí. Po kliknutí na robota se zobrazí text: "Neklikej na me" :) Nyní samozřejmě pouze bastlíme, v praxi bychom pracovali s objektem robot, to si časem ukážeme na objektu kostka. Do třídy si přidejme tyto proměnné:

private Rectangle obdelnikRobota;

V Initialize() obdélník nastavíme přibližně na souřadnice robota pomocí levého horního bodu a délky stran:

obdelnikRobota = new Rectangle(775, 345, 115, 245);

K číslům jsem došel tak, že jsem si pozadí otevřel ve Windows Malování a oblast označil, v dolní liště mi MSPaint ukázal její souřadnice a rozměry. Nyní se přesuňme do Draw() a přidejme vykreslení hlášky:

if (obdelnikRobota.Contains(new Point(mys.X, mys.Y))
&& (mys.LeftButton == ButtonState.Pressed))
    spriteBatch.TextSeStinem(fontBlox, "Neklikej na me", new Vector2(600, 220), Color.Lime);

Stav tlačítka porovnáváme pomocí výčtového typu ButtonState, obsahující prvky Pressed a Released (stisknuto a uvolněno). Ideálně by tato kontrola měla být v metodě Update(), ale pro náš pokus to nevadí. Pokud bychom chtěli složitější chování, stejně jako u kláves bychom založili proměnnou minulaMys.

Robotris, ukázková hra v XNA Game Studio - Od nuly k tetrisu v MonoGame

Příště, v lekci Rozdělení MonoGame hry do komponent, se podíváme, jak hru rozložit do několika herních komponent. Celý projekt s dnešním řešením si opět můžete stáhnout níže.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 359x (11.85 MB)
Aplikace je včetně zdrojových kódů v jazyce C#

 

Předchozí článek
Kreslíme a píšeme v MonoGame
Všechny články v sekci
Od nuly k tetrisu v MonoGame
Přeskočit článek
(nedoporučujeme)
Rozdělení MonoGame hry do komponent
Článek pro vás napsal David Hartinger
Avatar
Uživatelské hodnocení:
19 hlasů
David je zakladatelem ITnetwork a programování se profesionálně věnuje 15 let. Má rád Nirvanu, nemovitosti a svobodu podnikání.
Unicorn university David se informační technologie naučil na Unicorn University - prestižní soukromé vysoké škole IT a ekonomie.
Aktivity