10. díl - Hra Tetris v XNA: Bodování a dokončení levelu

C# .NET XNA game studio Robotris Hra Tetris v XNA: Bodování a dokončení levelu

V minulém dílu seriálu XNA tutoriálů 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

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 dílech 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.cs, 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 intu :) Konstruktor atributy inicializuje.

Instanci hráče bude spravovat samotná třída Hra.cs, jelikož ji bude v dalších dílech 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

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 dílech 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

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

Příště do hry přidáme ještě nějaké vychytávky.


 

Stáhnout

Staženo 249x (5.92 MB)
Aplikace je včetně zdrojových kódů v jazyce C#

 

  Aktivity (1)

Článek pro vás napsal David Čápka
Avatar
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.

Jak se ti líbí článek?
Celkem (3 hlasů) :
55555


 


Miniatura
Všechny články v sekci
Od nuly k tetrisu v XNA game studio
Miniatura
Následující článek
Hra tetris v XNA: Vychytávky v levelu

 

 

Komentáře

Avatar
Martin Horáček:

Zdravím. Poraďtě prosím, když vytvořím ve třídě Hra.cs - public Hrac hrac, tak mi visual studio píše chybu: Inconsistent accessibility: field type 'Robotris.Hrac' is less accessible than field 'Robotris.Hra.hrac'
Co s tím? Díky za rady.

 
Odpovědět 30.3.2013 14:50
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Martin Horáček
David Čápka:

Nemáš před definicí třídy public :)

Odpovědět 30.3.2013 14:56
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
 
Odpovědět 30.3.2013 15:00
Avatar
mackulinm
Člen
Avatar
mackulinm:

Ahojte :) chcem najprve podakovat za tutorial. Moc fajn, vela som sa naucil. Este nesom ho nedokoncil. Ale konec sa pomali ale isto bliizi :D
Mam taky problem, ze po stlaceny Escape, normalne vyskoci tabulka ci chcem ukoncit... ale hra v pozadi dalej bezi.

 
Odpovědět 5.4.2013 23:18
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na mackulinm
David Čápka:

Nemáš v Update() ošetřený běh hry v závislosti na jejím stavu, je to tu popsané:

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

Můžeš si dole stáhnout zdroják a opravit si to.

Odpovědět 6.4.2013 8:20
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
rydlova.hanyss:

Ahoj, předem moc díky za tutoriál, moc mě to naučilo :)
Měla bych ale otázku.. Při otáčení kostek mi mění tvary, místo aby se jen otočila. Pole a já jsme nikdy nebyli moc kamarádi, jen kde dělám chybu?:)

 
Odpovědět 31.3.2014 14:20
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 6 zpráv z 6.