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 9 - Hra Tetris v MonoGame: Zprovoznění hry

V minulé lekci, Hra tetris v MonoGame: Hrací plocha, jsme si naprogramovali hrací plochu k tetrisu.

Dnes zprovozníme základy hry tak, aby se dala hrát.

Přesuneme se do KomponentaLevel. Přidáme atribut hraciPlocha, ve kterém bude uložena instance třídy z minula:

private HraciPlocha hraciPlocha;

V Initialize() nastavíme hrací ploše rozměry a pozici:

hraciPlocha = new HraciPlocha(10, 20, poziceHraciPlochy);

Přidáme metodu, která nám podá další kostku. Bude velmi jednoduchá, vygeneruje novou kostku a nastaví ji pozici nahoru doprostřed:

public void DalsiKostka()
{
    kostka = generatorKostek.Generuj(7);
    kostka.pozice = hraciPlocha.PosadKostku(kostka);
}

Vidíme, že jsme použili k zarovnání kostky metodu hrací plochy. Metodu zavoláme na konci Initialize(), místo generování kostky:

DalsiKostka();

Hrací plochu budeme určitě chtít vykreslovat, za vykreslení kostky v Draw() tedy přidáme:

hraciPlocha.Vykresli(hra.spriteBatch, sprityPolicek);

Skvělé. Můžete zkusit spustit, výsledkem by zatím měla být náhodná kostka nahoře ve středu hrací plochy.

Real-time logika

Konečně se dostáváme k real-time logice celé hry. Začněme pádem kostky.

Pád kostky

Pád kostky nastane jednou za určitou dobu. Pro začátek bude tato doba 1 sekunda, později se však bude hra zrychlovat. Přidejme si atribut rychlost typu float:

private float rychlost;

V Initialize() ho nastavíme na hodnotu 1:

rychlost = 1;

Budeme potřebovat měřit čas od posledního spadnutí kostky. Přidejme podobně ještě jednu proměnnou jménem casOdPoslednihoPadu:

private float casOdPoslednihoPadu;

V Initialize() ji nastavíme na 0:

casOdPoslednihoPadu = 0;

Přesuneme se do Update(). Zde si vytvoříme proměnnou sekundy, do které uložíme uběhlé vteřiny od posledního update (bude to samozřejmě desetinné číslo, tedy float):

float sekundy = (float)gameTime.ElapsedGameTime.TotalSeconds;

Takto získaný počet sekund poté přičteme k casOdPoslednihoPadu:

casOdPoslednihoPadu += sekundy;

Nyní se stačí podívat, zda uběhlo již tolik, kolik říká rychlost hry. Pokud ano, necháme kostku spadnout a vynulujeme casOdPoslednihoPadu:

if (casOdPoslednihoPadu >= rychlost)
{
    kostka.Spadni();
    casOdPoslednihoPadu = 0;
}

Můžeme vyzkoušet.

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

Kostka po chvilce samozřejmě vyjede z hrací plochy. Musíme reagovat na kolizi a v tomto případě kostku připojit k hrací ploše. K obojímu máme připravené metody a neměl by to být žádný problém.

Kolize nastane ve chvíli, kdy je kostka již tam, kde být nemá. To opravíme jednoduše tím, že ji snížíme Y o jedna. Kostku poté připojíme k hrací ploše a zavoláme vymazání případných plných řad. Nakonec si řekneme o další kostku.

if (hraciPlocha.Kolize(kostka, kostka.pozice))
{
    kostka.pozice.Y--;
    hraciPlocha.Pripoj(kostka);
    hraciPlocha.VymazRady();
    DalsiKostka();
}

Celou tuto kontrolu vložíme za volání metody Spadni() v Update(), protože logicky ke kolizi dojde poté, co kostka spadne příliš nízko nebo na jinou kostku.

Hru spustíme a necháme ji chvíli běžet.

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

Vidíme, že kolize nastane jak při dosažení dna hrací plochy, tak při střetu s jinou kostkou. Stejně nám kolize nastane i v případě, že s kostkou vyjedeme z hrací plochy po stranách.

Pohyb kostkou

Aby se dala hra opravdu hrát, mělo by jít kostkou pohybovat. Posouvat kostku by nemělo být nic těžkého, pouze musíme kontrolovat kolize, aby nám nevyjela z hrací plochy nebo nenajela do jiné kostky. Zde se nám přesně hodí parametr pozice v metodě Kolize(), budeme se totiž ptát vždy na to, jestli je budoucí souřadnice kostky volná a pokud ne, pohyb vůbec neprovedeme.

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žení sekund do Update() a vyzkoušíme.

Pohyb je velmi rychlý, protože se klávesa vyhodnocuje příliš často. S tímto problémem jsme se již setkali v prvních lekcích našeho kurzu. Jedna možnost by byla použít naši metodu NovaKlavesa() a pohybovat kostkou klikáním. To by však bylo nepohodlné. Vytvoříme si tedy časovač, podobně jako pro pád kostky a pohyb budeme vykonávat jen tehdy, pokud uběhl určitý čas.

Třídě přidáme 2 další atributy, casOdPoslednihoStisku a prodlevaKlavesy:

private float casOdPoslednihoStisku;
private float prodlevaKlavesy;

Proměnné inicializujeme v Initialize():

prodlevaKlavesy = 0.06f;
casOdPoslednihoStisku = 0;

Nyní budeme postupovat úplně stejně jako u pádu, v Update() budeme přičítat uplynulé sekundy k času od posledního stisku klávesy. Obsluhu kláves poté vložíme do podmínky a případně vynulujeme čas od posledního stisku.

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;
}

Vyzkoušejte si to, rychlost můžete případně regulovat změnou prodlevy v Initialize().

Vraťme se ještě k rotaci. I když funguje, může nastat situace, že po rotaci bude kostka kolidovat (natočí se tak, že se zapasuje do jiné nebo vyleze z hrací plochy). V takovém případě nebudeme chtít, aby k rotaci došlo. Tohoto chování dosáhneme malým trikem, kostku orotujeme stejně, jako to děláme teď, ale pokud v tu chvíli začne kolidovat, orotujeme ji ještě 3x, tím pádem se vrátí do původní polohy a navenek se nestane vůbec nic :)

Obsluhu rotace 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();
}

Protože hráč bude jistě netrpělivý a kostky padají zpočátku pomalu, umožníme mu zrychlit jejich pád pravým Ctrl. Rychlost již tedy nebude vždy stejná a proměnná rychlost nám přestává stačit. Přidejme si do Update() pod obsluhu kláves novou proměnnou casMeziPady, kterou nastavíme na rychlost. Pokud je držena klávesa pravé Ctrl, nastavíme ji na nějakou nižší hodnotu, třeba 0.15f. K podmínce držení klávesy ještě musíme přidat, aby byla rychlost vyšší, než zde 0.15f, jinak by si hráč mohl klávesou hru ve vysokých obtížnostech naopak zpomalit.

float casMeziPady = rychlost;
    if (hra.klavesy.IsKeyDown(Keys.RightControl) && (rychlost > 0.15f))
        casMeziPady = 0.15f;

Ještě upravíme podmínku pádu kostky, kde místo proměnné rychlost použijeme casMeziPady:

if (casOdPoslednihoPadu >= casMeziPady)

Vyzkoušíme.

V Tetrisu bývá zvykem ještě tlačítko, které kostku uzemní okamžitě, tím mám na mysli, že spadne dolů až tam, kde bude kolidovat. Toto chování naprogramujeme na klávesu šipka dolů. Bude stačit volat metodu Spadni() v cyklu tak dlouho, dokud o pozici níže nedojde ke kolizi. Takto bude kostka připravena těsně před kolizí. Ještě poté přičteme do proměnné casOdPoslednihoPadu, aby se vyvolala obsluha pádu kostky níže. Následující kód umístíme 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;
}

Vyzkoušíme.

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

To je pro dnešek vše.

Příště, v lekci Hra Tetris v MonoGame: Bodování a dokončení levelu, level dokončíme :).


 

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

 

Předchozí článek
Hra tetris v MonoGame: Hrací plocha
Všechny články v sekci
Od nuly k tetrisu v MonoGame
Přeskočit článek
(nedoporučujeme)
Hra Tetris v MonoGame: Bodování a dokončení levelu
Článek pro vás napsal David Hartinger
Avatar
Uživatelské hodnocení:
7 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