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
.
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 360x (11.85 MB)
Aplikace je včetně zdrojových kódů v jazyce C#