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 10 - Hra Tetris v MonoGame: Bodování a dokončení levelu

V minulé lekci, Hra Tetris v MonoGame: Zprovoznění hry, jsme zprovoznili základy hry a učinili tak level hratelný.

Dnes level dokončíme.

Příští kostka

Jak je u Tetrisu zvykem, hráč vidí příští kostku, aby se podle toho mohl u současné kostky zařídit. Naše hra nebude výjimkou. Do KomponentaLevel si přidáme nový atribut s instancí příští kostky:

private Kostka pristiKostka;

Před volání DalsiKostka(); v Initialize() vložíme vytvoření instance příští kostky:

pristiKostka = generatorKostek.Generuj(7);
DalsiKostka();

Přesuneme se do metody DalsiKostka(), která nyní do kostka uloží příští kostku a zároveň vygeneruje novou. Její kód bude nově vypadat takto:

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

Nakonec příští kostku vykreslíme. Na pozadí je k tomu připravena napřažená ruka robota, která vyzařuje modré pole. Na to kostku umístíme, do Draw() tedy za vykreslení kostky přidáme:

pristiKostka.Vykresli(new Vector2(930, 200), hra.spriteBatch, sprityPolicek);

Výsledek:

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

Skóre

Hra by měla hráče samozřejmě nějak vyzývat, v Tetrisu je k tomuto účelu skóre (body) a dosažený level. V souvislosti s hráčem nás bude zajímat ještě počet zaplněných řad a jeho přezdívka. V dalších lekcích si totiž ukážeme, jak skóre nahrát na internet tak, aby byl hráč pod přezdívkou vidět v tabulce nejlepších výsledků.

K projektu Robotris připojíme novou třídu Hrac, do které vložíme zmíněné atributy. Ty budou pro zjednodušení veřejné a třída bude sloužit jen k držení dat o hráči. Nezapomeňte dát třídě modifikátor public.

public class Hrac
{
    public long body;
    public int rady;
    public int level;
    public string prezdivka;

    public Hrac()
    {
        body = 0;
        rady = 0;
        level = 1;
    }
}

Na třídě není jinak nic zajímavého, asi jen to, že body jsou typu long, protože šikovný hráč by se nemusel vejít do rozsahu typu int :) Konstruktor atributy inicializuje.

Instanci hráče bude spravovat samotná třída Hra, jelikož ji bude v dalších lekcích potřeba sdílet mezi dalšími komponentami (skóre tabulkou). Přesuňme se tedy do Hra.cs a přidejme atribut hrac:

public Hrac hrac;

Tím jsme v Hra.cs skončili, přejděme opět do KomponentaLevel, kde v Initialize() vytvoříme instanci hráče:

hra.hrac = new Hrac();

Vykreslení HUDu

Nyní vykreslíme tzv. HUD (informační panel s hodnotami hráče), který bude zobrazovat skóre a level. Přejděme do metody Draw() a jednoduše přidejme výpis těchto hodnot z instance hráče. Budeme k tomu potřebovat menší font, který máme připravený. Font se bude používat jen v této komponentě, proto ho načteme zde. Jako další možnost bychom ho mohli umístit do Hra.cs jako veřejný. Vytvoříme privátní atribut font:

private SpriteFont font;

A v LoadContent() do něj načteme font z obsahu:

font = hra.Content.Load<SpriteFont>("Fonty/font_blox_maly");

Nyní pouze v Draw() vykreslíme hodnoty na příslušnou pozici:

hra.spriteBatch.TextSeStinem(font, "skore\n " + hra.hrac.body.ToString(), new Vector2(30, 390), Color.Red);
hra.spriteBatch.TextSeStinem(font, "level\n " + hra.hrac.level.ToString(), new Vector2(215, 390), Color.Red);

Výsledek:

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

Bodování

Pojďme hru bodovat, samozřejmě půjde o bodování řad. Budeme vycházet z originálního Tetrisu verze A. Body se připisují podle počtu zaplněných řad jednou kostkou:

Počet zaplněných řad Vzorec
1 level * 40 + 40
2 level * 100 + 100
3 level * 300 + 300
4 level * 1200 + 1200

Zaplnění čtyř řad najednou se říká tetris a hráč toho docílí pouze kostkou tvaru I.

Pokud nějaká řada zmizí, ještě přehrajeme zvuk. Ten si nejdříve připravíme. Přidáme atribut třídy:

private SoundEffect zvukRada;

A načteme v LoadContent():

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

Jde se bodovat, přesuňme se do Update(), do podmínky, kde kontrolujeme kolizi kostky, připojujeme ji do hrací plochy a tak podobně. Zmíněný blok kódu nyní vypadá takto:

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

Návratovou hodnotu metody VymazRady() uložíme do proměnné rady (je to počet řad, které byly vymazány). Kód tedy upravíme:

int rady = hraciPlocha.VymazRady();

Při vymazání alespoň jedné řady přehrajeme připravený zvuk:

if (rady > 0)
    zvukRada.Play();

Řady hráči připočteme:

hra.hrac.rady += rady;

A také mu za to přidělíme body podle příslušného vzorce:

switch (rady)
{
    case 1: hra.hrac.body += hra.hrac.level * 40 + 40; break;
    case 2: hra.hrac.body += hra.hrac.level * 100 + 100; break;
    case 3: hra.hrac.body += hra.hrac.level * 300 + 300; break;
    case 4: hra.hrac.body += hra.hrac.level * 1200 + 1200; break;
}

Level se bude zvyšovat každých 10 řad a při 0 řadách bude 1. Vypočteme ho takto:

int level = hra.hrac.rady / 10 + 1;

Pokud má hráč jiný level, než tento nový, vypočtený, aktualizujeme ho a zároveň zvýšíme rychlost hry o 5/6:

if (hra.hrac.level != level)
{
    hra.hrac.level = level;
    rychlost = rychlost * 5 / 6;
}

Můžete vyzkoušet.

Prohra

Zůstaňme ještě v bloku kolize kostky s hrací plochou. Budeme reagovat na prohru. Hráč prohraje ve chvíli, kdy dovrší kostky až na horní okraj hrací plochy. Jinými slovy pokud není kam položit nově padající kostku, hra skončila. Proto se hned po volání DalsiKostka() zeptáme, zda koliduje. Pokud nová kostka ihned po položení koliduje, hru ukončíme. To zatím uděláme tak, že ukončíme celou aplikaci. V dalších lekcích zobrazíme obrazovku pro odeslání skóre a vrátíme se do menu.

Přidejme tedy za DalsiKostka() jednoduchou podmínku:

if (hraciPlocha.Kolize(kostka, kostka.pozice))
    hra.Exit();

Metodou Exit() na instanci hry jsme hru ukončili. Level je nyní již dobře hratelný.

Pauza

Určitě je dobré hráči umožnit hru zapauzovat. Existuje mnoho způsobů, jak toho dosáhnout, mohli bychom si pro pauzu vytvořit další komponentu a poté KomponentuLevel zastavit. Uchýlíme se však k jednoduššímu řešení, komponentě KomponentaLevel přidělíme stavy. Ty budou dva: Hra a Pauza. Ve třídě si deklarujeme výčtový typ StavHry a atribut tohoto typu:

public enum eStavHry
{
    Hra,
    Pauza,
}
public eStavHry stavHry;

V Initialize() stav nastavíme na Hra:

stavHry = eStavHry.Hra;

Při pauze nebude hra aktivní a přes obrazovku bude vykreslen zašedlý sprite s hláškou, že je hra zapauzovaná. Sprite si stáhněte:

Sprite pauzy - Od nuly k tetrisu v MonoGame

a přidejte do RobotrisContent, do složky Sprity/.

Jako vždy si pro sprite vytvoříme atribut:

private Texture2D sprPauza;

A načteme ho v LoadContent():

sprPauza = hra.Content.Load<Texture2D>(@"Sprity\spr_pauza");

Podle stavu hry se bude chovat metoda Update(). Rozdělíme ji na 2 větve podle stavů. Celý současný obsah metody vložíme do podmínky, zda je stav hry Hra. Přidáme do něj ještě reakci na klávesu Escape, která způsobí přepnutí stavu na pauzu a pozastavení přehrávání hudby.

// Hra
if (stavHry == eStavHry.Hra)
{
    . . .

    if (hra.klavesy.IsKeyDown(Keys.Escape))
    {
        MediaPlayer.Pause();
        stavHry = eStavHry.Pauza;
    }
}

Za větev s hrou přidáme další větev s pauzou. Ve stavu Pauza bude reagovat na klávesy A (Ano, ukončení hry) a N (Ne, návrat do hry).

if (stavHry == eStavHry.Pauza)
{
    if (hra.klavesy.IsKeyDown(Keys.A))
        hra.Exit(); // Ukončení

    if (hra.klavesy.IsKeyDown(Keys.N))
    {
        stavHry = eStavHry.Hra; // Pokračování
        MediaPlayer.Resume();
    }
}

Zbývá, aby na stav pauzy reagovala i vykreslovací metoda. Na konec vykreslování v metodě Draw() přidáme vykreslení spritu s pauzou a textu:

if (stavHry == eStavHry.Pauza)
{
    hra.spriteBatch.Draw(sprPauza, new Rectangle(0, 0, hra.sirkaOkna, hra.vyskaOkna), Color.White);
    hra.spriteBatch.TextSeStinem(font, "hra pozastavena", new Vector2(480, 260), Color.Red);
    hra.spriteBatch.TextSeStinem(hra.fontCourierNew, "Opravdu si přejete hru ukončit?\n\n\'A\' - ukončení hry \n\n\'N\' - pokračovat ve hře", new Vector2(440, 340), Color.Red);
}

A vyzkoušíme:

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

Příště, v lekci Hra tetris v MonoGame: Vychytávky v levelu, do hry přidáme ještě další vychytávky.


 

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

 

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