IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
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í.

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ť.

Ukážkový Tetris v XNA Game Studio - Od nuly k tetrisu v MonoGame

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ť.

Ukážkový Tetris v XNA Game Studio - Od nuly k tetrisu v MonoGame

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.

Ukážkový Tetris v XNA Game Studio - Od nuly k tetrisu v MonoGame

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é 327x (11.9 MB)
Aplikácia je vrátane zdrojových kódov v jazyku C#

 

Predchádzajúci článok
Hra tetris v MonoGame: Hracia plocha
Všetky články v sekcii
Od nuly k tetrisu v MonoGame
Preskočiť článok
(neodporúčame)
Hra Tetris v MonoGame: Bodovanie a dokončenie levelu
Článok pre vás napísal David Hartinger
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
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 sa informačné technológie naučil na Unicorn University - prestížnej súkromnej vysokej škole IT a ekonómie.
Aktivity