10. diel - Hra Tetris v MonoGame: Bodovanie a dokončenie levelu
V minulej lekcii, Hra Tetris v MonoGame: Sprevádzkovanie hry , sme sprevádzkovali základy hry a urobili tak level hrateľný. Dnes level dokončíme.
Budúci kocka
Ako je u Tetrisu zvykom, hráč vidí budúci kocku, aby sa podľa toho mohol
u súčasnej kocky zariadiť. Naša hra nebude výnimkou. Do
KomponentaLevel
si pridáme nový atribút s inštanciou budúci
kocky:
private Kostka pristiKostka;
Pred volanie DalsiKostka();
v Initialize()
vložíme vytvorení inštancie budúci kocky:
pristiKostka = generatorKostek.Generuj(7);
DalsiKostka();
Presunieme sa do metódy DalsiKostka()
, ktorá teraz do
kostka
uloží budúci kocku a zároveň vygeneruje novú. Jej kód
bude novo vyzerať takto:
public void DalsiKostka() { kostka = pristiKostka; kostka.pozice = hraciPlocha.PosadKostku(kostka); pristiKostka = generatorKostek.Generuj(7); }
Nakoniec budúci kocku vykreslíme. Na pozadí je na to pripravená
natiahnutá ruka robota, ktorá vyžaruje modré pole. Na to kocku umiestnime,
do Draw()
teda za vykreslenie kocky pridáme:
pristiKostka.Vykresli(new Vector2(930, 200), hra.spriteBatch, sprityPolicek);
výsledok:
Skóre
Hra by mala hráča samozrejme nejako vyzývať, v Tetrisu je na tento účel skóre (body) a dosiahnutý level. V súvislosti s hráčom nás bude zaujímať ešte počet zaplnených radov a jeho prezývka. V ďalších lekciách si totiž ukážeme, ako skóre nahrať na internet tak, aby bol hráč pod prezývkou vidieť v tabuľke najlepších výsledkov.
K projektu Robotris
pripojíme novú triedu Hrac
,
do ktorej vložíme spomínané atribúty. Tie budú pre zjednodušenie verejné
a trieda bude slúžiť len na držanie dát o hráčovi. Nezabudnite dať
triede 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 triede nie je inak nič zaujímavé, asi len to, že body
sú
typu long
, pretože šikovný hráč by sa nemusel zmestiť do
rozsahu typu int
Konštruktor atribúty inicializuje.
Inštanciu hráča bude spravovať samotná trieda Hra
, pretože
ju bude v ďalších lekciách potreba zdieľať medzi ďalšími komponentmi
(skóre tabuľkou). Presuňme sa teda do Hra.cs
a pridajme atribút
hrac
:
public Hrac hrac;
Tým sme v Hra.cs
skončili, prejdime opäť do
KomponentaLevel
, kde v Initialize()
vytvoríme
inštanciu hráča:
hra.hrac = new Hrac();
Vykreslenie HUDu
Teraz vykreslíme tzv. HUD (informačný panel s hodnotami hráča), ktorý
bude zobrazovať skóre a level. Prejdime do metódy Draw()
a
jednoducho pridajme výpis týchto hodnôt z inštancie hráča. Budeme na to
potrebovať menší font, ktorý máme pripravený. Font sa bude používať len
v tejto komponente, preto ho načítame tu. Ako ďalšiu možnosť by sme ho
mohli umiestniť do Hra.cs
ako verejný. Vytvoríme privátne
atribút font
:
private SpriteFont font;
A v LoadContent()
do neho načítame font z obsahu:
font = hra.Content.Load<SpriteFont>("Fonty/font_blox_maly");
Teraz len v Draw()
vykreslíme hodnoty na príslušnú
pozíciu:
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ýsledok:
Bodovania
Poďme hru bodovať, samozrejme pôjde o bodovania radov. Budeme vychádzať z originálneho Tetrisu verzie A. Body sa pripisujú podľa počtu zaplnených radov raz kockou:
Počet zaplnených radov | vzorec |
---|---|
1 | level * 40 + 40 |
2 | level * 100 + 100 |
3 | level * 300 + 300 |
4 | level * 1200 + 1200 |
Ak nejaká rad zmizne, ešte prehráme zvuk. Ten si najskôr pripravíme. Pridáme atribút triedy:
private SoundEffect zvukRada;
A načítame v LoadContent()
:
zvukRada = hra.Content.Load<SoundEffect>(@"Zvuky\zvuk_rada");
Ide sa bodovať, presuňme sa do Update()
, do podmienky, kde
kontrolujeme kolízii kocky, pripájame ju do hracej plochy a tak podobne.
Spomínaný blok kódu teraz vyzerá takto:
if (hraciPlocha.Kolize(kostka, kostka.pozice))
{
kostka.pozice.Y--;
hraciPlocha.Pripoj(kostka);
hraciPlocha.VymazRady();
DalsiKostka();
}
Návratovú hodnotu metódy VymazRady()
uložíme do premennej
rady
(je to počet radov, ktoré boli vymazané). Kód teda
upravíme:
int rady = hraciPlocha.VymazRady();
Pri vymazaní aspoň jednej rady prehráme pripravený zvuk:
if (rady > 0) zvukRada.Play();
Radu hráči pripočítame:
hra.hrac.rady += rady;
A tiež mu za to pridelíme body podľa príslušného vzorca:
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 sa bude zvyšovať každých 10 radov a pri 0 radoch bude 1. Vypočítame ho nasledovne:
int level = hra.hrac.rady / 10 + 1;
Pokiaľ má hráč iný level, než tento nový, vypočítaný, aktualizujeme
ho a zároveň zvýšime rýchlosť hry o 5/6
:
if (hra.hrac.level != level) { hra.hrac.level = level; rychlost = rychlost * 5 / 6; }
Môžete vyskúšať.
Prehra
Zostaňme ešte v bloku kolízie kocky s hracou plochou. Budeme reagovať na
prehru. Hráč prehrá vo chvíli, keď dovŕši kocky až na horný okraj
hracej plochy. Inými slovami pokiaľ nie je kam položiť novo padajúce kocku,
hra skončila. Preto sa hneď po volaní DalsiKostka()
spýtame,
či koliduje. Ak nová kocka ihneď po položení koliduje, hru ukončíme. To
zatiaľ urobíme tak, že ukončíme celú aplikáciu. V ďalších lekciách
zobrazíme obrazovku pre odoslanie skóre a vrátime sa do menu.
Pridajme teda za DalsiKostka()
jednoduchú podmienku:
if (hraciPlocha.Kolize(kostka, kostka.pozice))
hra.Exit();
Metódou Exit()
na inštanciu hry sme hru ukončili. Level je
teraz už dobre hrateľný.
Pauza
Určite je dobré hráči umožniť hru zapauzovať. Existuje mnoho
spôsobov, ako to dosiahnuť, mohli by sme si pre pauzu vytvoriť ďalšie
komponent a potom KomponentuLevel
zastaviť. Uchýlime sa však k
jednoduchšiemu riešeniu, komponente KomponentaLevel
pridelíme
stavy. Tie budú dva: Hra
a Pauza
. V triede si
deklarujeme Výučbový typ StavHry
a atribút tohto typu:
public enum eStavHry { Hra, Pauza, } public eStavHry stavHry;
V Initialize()
stav nastavíme na Hra
:
stavHry = eStavHry.Hra;
Pri pauze nebude hra aktívnu a cez obrazovku bude vykreslený zašednutý sprite s hláškou, že je hra zapauzovaní. Sprite si stiahnite:
a pridajte do RobotrisContent
, do zložky
Sprity/
.
Ako vždy si pre sprite vytvoríme atribút:
private Texture2D sprPauza;
A načítame ho v LoadContent()
:
sprPauza = hra.Content.Load<Texture2D>(@"Sprity\spr_pauza");
Podľa stavu hry sa bude správať metóda Update()
. Rozdelíme
ju na 2 vetvy podľa stavov. Celý súčasný obsah metódy vložíme do
podmienky, či je stav hry Hra
. Pridáme do neho ešte reakciu na
kláves Escape, ktorá spôsobí prepnutie stavu na pauzu a
pozastavenie prehrávania hudby.
// Hra if (stavHry == eStavHry.Hra) { . . . if (hra.klavesy.IsKeyDown(Keys.Escape)) { MediaPlayer.Pause(); stavHry = eStavHry.Pauza; } }
Za vetvu s hrou pridáme ďalšiu vetvu s pauzou. V stave Pauza
bude reagovať na klávesy A (Áno, ukončenie hry) a N
(Nie, 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(); } }
Zostáva, aby na stav pauzy reagovala aj vykresľovací metóda. Na koniec
vykresľovanie v metóde Draw()
pridáme vykreslenie 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 vyskúšame:
Nabudúce, v lekcii Hra tetris v MonoGame: Vychytávky v leveli , do hry pridáme ešte ďalšie vychytávky.
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é 287x (12.09 MB)
Aplikácia je vrátane zdrojových kódov v jazyku C#