9. diel - Hra Tetris v MonoGame: Sprevádzkovanie hry
V minulej lekcii, Hra tetris v MonoGame: Hracia plocha , sme si naprogramovali hraciu plochu k tetrisu. Dnes sprevádzkujeme základy hry tak, aby sa dala hrať.
Presunieme sa do KomponentaLevel
. Pridáme atribút
hraciPlocha
, v ktorom bude uložená inštancie triedy z
minula:
private HraciPlocha hraciPlocha;
V Initialize()
nastavíme hracej ploche rozmery a pozíciu:
hraciPlocha = new HraciPlocha(10, 20, poziceHraciPlochy);
Pridáme metódu, ktorá nám podá ďalšie kocku. Bude veľmi jednoduchá, vygeneruje novú kocku a nastaví ju pozíciu nahor doprostred:
public void DalsiKostka() { kostka = generatorKostek.Generuj(7); kostka.pozice = hraciPlocha.PosadKostku(kostka); }
Vidíme, že sme použili na zarovnanie kocky metódu hracej plochy. Metódu
zavoláme na konci Initialize()
, namiesto generovanie kocky:
DalsiKostka();
Hraciu plochu budeme určite chcieť vykresľovať, za vykreslenie kocky v
Draw()
teda pridáme:
hraciPlocha.Vykresli(hra.spriteBatch, sprityPolicek);
Skvelé. Môžete skúsiť spustiť, výsledkom by zatiaľ mala byť náhodná kocka hore v strede hracej plochy.
Real-time logika
Konečne sa dostávame k real-time logike celej hry. Začnime pádom kocky.
Pád kocky
Pád kocky nastane raz za určitú dobu. Pre začiatok bude táto doba 1
sekunda, neskôr sa však bude hra zrýchľovať. Pridajme si atribút
rychlost
typu float
:
private float rychlost;
V Initialize()
ho nastavíme na hodnotu 1
:
rychlost = 1;
Budeme potrebovať merať čas od posledného spadnutie kocky. Pridajme
podobne ešte jednu premennú menom casOdPoslednihoPadu
:
private float casOdPoslednihoPadu;
V Initialize()
ju nastavíme na 0
:
casOdPoslednihoPadu = 0;
Presunieme sa do Update()
. Tu si vytvoríme premennú
sekundy
, do ktorej uložíme ubehlo sekundy od posledného update
(bude to samozrejme desatinné číslo, teda float
):
float sekundy = (float)gameTime.ElapsedGameTime.TotalSeconds;
Takto získaný počet sekúnd potom pripočítame k
casOdPoslednihoPadu
:
casOdPoslednihoPadu += sekundy;
Teraz sa stačí pozrieť, či ubehlo už toľko, koľko hovorí rýchlosť
hry. Ak áno, necháme kocku spadnúť a vynulujeme
casOdPoslednihoPadu
:
if (casOdPoslednihoPadu >= rychlost) { kostka.Spadni(); casOdPoslednihoPadu = 0; }
Môžeme vyskúšať.
Kocka po chvíľke samozrejme vyjde z hracej plochy. Musíme reagovať na kolíziu a v tomto prípade kocku pripojiť k hracej ploche. K obidvom máme pripravené metódy a nemal by to byť žiadny problém.
Kolízia nastane vo chvíli, keď je kocka už tam, kde byť nemá. To
opravíme jednoducho tým, že ju znížime Y
o jedna. Kocku potom
pripojíme k hracej ploche a zavoláme vymazanie prípadných plných radov.
Nakoniec si povieme o ďalšie kocku.
if (hraciPlocha.Kolize(kostka, kostka.pozice))
{
kostka.pozice.Y--;
hraciPlocha.Pripoj(kostka);
hraciPlocha.VymazRady();
DalsiKostka();
}
Celú túto kontrolu vložíme za volanie metódy Spadni()
v
Update()
, pretože logicky ku kolízii dôjde potom, čo kocka
spadne príliš nízko alebo na inú kocku.
Hru spustíme a necháme ju chvíľu bežať.
Vidíme, že kolízie nastane tak pri dosiahnutí dna hracej plochy, tak pri strete s inou kockou. Rovnako nám kolízie nastane aj v prípade, že s kockou vyjdeme z hracej plochy po stranách.
Pohyb kockou
Aby sa dala hra naozaj hrať, malo by ísť kockou pohybovať. Posúvať
kocku by nemalo byť nič ťažké, len musíme kontrolovať kolízie, aby nám
nevyšla z hracej plochy alebo nenarazili do inej kocky. Tu sa nám presne hodí
parameter pozície v metóde Kolize()
, budeme sa totiž pýtať
vždy na to, či je budúci súradnice kocky voľná a ak nie, pohyb vôbec
neprevedieme.
if ((hra.klavesy.IsKeyDown(Keys.Right)) && (!hraciPlocha.Kolize(kostka, new Vector2(kostka.pozice.X + 1, kostka.pozice.Y)))) kostka.pozice.X++; // pohyb doprava if ((hra.klavesy.IsKeyDown(Keys.Left)) && (!hraciPlocha.Kolize(kostka, new Vector2(kostka.pozice.X - 1, kostka.pozice.Y)))) kostka.pozice.X--; // doleva
Kód vložíme za uloženie sekúnd do Update()
a
vyskúšame.
Pohyb je veľmi rýchly, pretože sa klávesa vyhodnocuje príliš často. S
týmto problémom sme sa už stretli v prvých lekciách nášho kurzu. Jedna
možnosť by bola použiť našu metódu NovaKlavesa()
a pohybovať
kockou klikaním. To by však bolo nepohodlné. Vytvoríme si teda časovač,
podobne ako pre pád kocky a pohyb budeme vykonávať len vtedy, ak ubehol
určitý čas.
Triede pridáme 2 ďalšie atribúty, casOdPoslednihoStisku
a
prodlevaKlavesy
:
private float casOdPoslednihoStisku; private float prodlevaKlavesy;
Premenné inicializujeme v Initialize()
:
prodlevaKlavesy = 0.06f; casOdPoslednihoStisku = 0;
Teraz budeme postupovať úplne rovnako ako pri páde, v
Update()
budeme pričítať uplynulej sekundy k času od
posledného stlačenia klávesy. Obsluhu klávesov potom vložíme do podmienky
a prípadne vynulujeme čas od posledného stlačenia.
casOdPoslednihoStisku += sekundy; if (casOdPoslednihoStisku > prodlevaKlavesy) { if ((hra.klavesy.IsKeyDown(Keys.Right)) && (!hraciPlocha.Kolize(kostka, new Vector2(kostka.pozice.X + 1, kostka.pozice.Y)))) kostka.pozice.X++; // pohyb doprava if ((hra.klavesy.IsKeyDown(Keys.Left)) && (!hraciPlocha.Kolize(kostka, new Vector2(kostka.pozice.X - 1, kostka.pozice.Y)))) kostka.pozice.X--; // doleva casOdPoslednihoStisku = 0; }
Vyskúšajte si to, rýchlosť môžete prípadne regulovať zmenou
oneskorenia v Initialize()
.
Vráťme sa ešte k rotácii. Aj keď funguje, môže nastať situácia, že po rotáciu bude kocka kolidovať (natočí sa tak, že sa zapasuje do inej alebo vylezie z hracej plochy). V takom prípade nebudeme chcieť, aby k rotácii došlo. Tohto správania dosiahneme malým trikom, kocku orotujeme rovnako, ako to robíme teraz, ale ak v tej chvíli začne kolidovat, orotujeme ju ešte 3x, tým pádom sa vráti do pôvodnej polohy a navonok sa nestane vôbec nič
Obsluhu rotácie upravíme takto:
if (hra.NovaKlavesa(Keys.Enter) || hra.NovaKlavesa(Keys.Up)) { kostka.Orotuj(); // Orotovaná kostka koliduje - vrátím rotaci zpět if (hraciPlocha.Kolize(kostka, kostka.pozice)) for (int i = 0; i < 3; i++) kostka.Orotuj(); }
Pretože hráč bude iste netrpezlivý a kocky padajú spočiatku pomaly,
umožníme mu zrýchliť ich pád pravým Ctrl. Rýchlosť už teda
nebude vždy rovnaká a premenná rychlost
nám prestáva stačiť.
Pridajme si do Update()
pod obsluhu klávesov novú premennú
casMeziPady
, ktorú nastavíme na rychlost
. Ak je
držaná klávesa pravej Ctrl, nastavíme ju na nejakú nižšiu
hodnotu, treba 0.15f
. O podmienke držania klávesy ešte musíme
pridať, aby bola rýchlosť vyššia, než tu 0.15f
, inak by si
hráč mohol klávesom hru vo vysokých obtiažnostiach naopak spomaliť.
float casMeziPady = rychlost; if (hra.klavesy.IsKeyDown(Keys.RightControl) && (rychlost > 0.15f)) casMeziPady = 0.15f;
Ešte upravíme podmienku pádu kocky, kde namiesto premenné
rychlost
použijeme casMeziPady
:
if (casOdPoslednihoPadu >= casMeziPady)
Vyskúšame.
V Tetrisu býva zvykom ešte tlačidlo, ktoré kocku uzemní okamžite, tým
mám na mysli, že spadne dolu až tam, kde bude kolidovať. Toto správanie
naprogramujeme na kláves šípka dole. Bude stačiť volať metódu
Spadni()
v cykle tak dlho, kým o pozíciu nižšie nedôjde ku
kolízii. Takto bude kocka pripravená tesne pred kolíziou. Ešte potom
pripočítame do premennej casOdPoslednihoPadu
, aby sa vyvolala
obsluha pádu kocky nižšie. Nasledujúci kód umiestnime za obsluhu
kláves:
if (hra.NovaKlavesa(Keys.Down)) { while (!hraciPlocha.Kolize(kostka, new Vector2(kostka.pozice.X, kostka.pozice.Y + 1))) kostka.Spadni(); casOdPoslednihoPadu += casMeziPady; }
Vyskúšame.
To je pre dnešok všetko. Nabudúce, v lekcii Hra Tetris v MonoGame: Bodovanie a dokončenie levelu , level dokončíme
Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 330x (11.9 MB)
Aplikácia je vrátane zdrojových kódov v jazyku C#