IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

11. diel - Hra tetris v MonoGame: Vychytávky v leveli

V minulej lekcii, Hra Tetris v MonoGame: Bodovanie a dokončenie levelu , sme dokončili level Tetrisu a sprevádzkovali bodovania a pauzu. Dnešná tutoriál je posledný, kedy sa budeme levelu venovať. Vylepšíme rotáciu kocky a pridáme do hry prvok zameriavač.

Vylepšená rotácie kocky

Ako sa kocka rotuje určite nie je najprirodzenejší. V pravom Tetrisu sú kocky ručne nastavené v niekoľkých polohách, niektoré majú napríklad len 2 (kocka I), niektoré len jednu (kocka O). Tento spôsob ručného zadávania je ale veľmi neprogramátorský a nič by sme sa tým nenaučili. Náš spôsob zatiaľ rotuje každú kocku ako štvorcovú maticu o hrane 4. Problém je v tom, že väčšina kociek má hranu len 3, niektoré dokonca 2. Metódu rotácie teda upravíme tak, aby rotovala podľa rozmerov kocky, presnejšie podľa jej hrany.

Najprv si upravme naše kocky v textovom súbore tak, aby boli vždy umiestnené v ľavom hornom rohu. Samozrejme musíme brať kocku ako štvorec a podľa dlhšej hrany určiť jej polohu (to sa týka najmä kocky I). Súbor netfx.dll teda prepíšeme na nasledujúci obsah:

1000
1110
0000
0000

0010
1110
0000
0000

1100
1100
0000
0000

0000
1111
0000
0000

0100
1110
0000
0000

1100
0110
0000
0000

0110
1100
0000
0000

1000
0000
0000
0000

1010
0100
1010
0000

0100
1110
0100
0000

1110
0100
0100
0000

1100
0000
0000
0000

Otvoríme si triedu Kostka a pridajme ju verejný atribút hrana:

public int hrana;

Triede pridajme privátne metódu ZjistiHranu(), v ktorej budeme najprv predpokladať, že má kocka hranu 1, teda najnižšiu možnú, prázdnu kocku v hre mať určite nebudeme.

private void ZjistiHranu()
{
    hrana = 1;
}

Hranu zistíme tak, že v metóde prejdeme všetky políčka. Ak narazíme na políčko s nenulovú hodnotou, spýtame sa, či je od ľavého horného rohu ďalej, než je súčasná známa hrana. Ak áno, do hrany vždy uložíme tu väčšia súradnici plného políčka.

for (int j = 0; j < 4; j++)
    for (int i = 0; i < 4; i++)
    {
        // nalezeno políčko dále od levého horního rohu, než je současná hrana
        if ((Policka[i, j] > 0) && (((i + 1) > hrana) || ((j + 1) > hrana)))
        {
            if (i > j)
                hrana = i + 1;
            else
                hrana = j + 1;
        }
    }

Metódu zavoláme v konstruktoru po generovanie spritov:

ZjistiHranu();

Hranu máme uloženú, teraz je hračka upraviť metódu pre rotáciu (budeme pracovať iba s časťou poľa veľkosti hrany):

public void Orotuj()
{
    // pomocné pole
    int[,] a = ZkopirujPolicka(Policka);
    // rotace pole jako matice prohozením souřadnic
    for (int y = 0; y < hrana; y++)
        for (int x = 0; x < hrana; x++)
            Policka[x, y] = a[y, (hrana - 1) - x];
}

Na veľkosť kocky bude reagovať aj metóda PosadKostku() v triede HraciPlocha:

public Vector2 PosadKostku(Kostka kostka)
{
    return new Vector2((sirka / 2) - (kostka.hrana / 2), 0);
}

Hru teraz vyskúšame, rotácia je oveľa precíznejšie.

Zameriavač

Niektoré verzie Tetrisu ukazujú priamo miesto, kam kocka spadne, ak stlačíme šípku nadol. Tento prvok som nazval zameriavačom, ten do hry pridáme.

Získanie pozície

Prejdime do HraciPlocha.cs, kam pridáme metódu SpocitejSouradniceZamerovace(). Ako parameter bude brať kocku a vracať bude pozíciu ako Vector2. To budú súradnice miesta, kam by kocka spadla, keď by sme stlačili šípku nadol. Zistenie týchto súradníc už teda vieme, stačí jednoducho posúvať pozíciu kocky dole tak dlho, dokiaľ nebude kolidovať. Pozíciu o 1 vyššie po tom vrátime.

public Vector2 SpocitejSouradniceZamerovace(Kostka kostka)
{
    Vector2 pozice = kostka.pozice;
    // zkusíme posouvat kostku dolů tak dlouho, dokud nebude kolidovat
    while (!Kolize(kostka, pozice))
        pozice.Y++;
    // vrátíme souřadnici o 1 nahoru, kdy ještě nekolidovala
    return new Vector2(kostka.pozice.X, pozice.Y - 1);
}

Pretože zistenie tejto pozície je v hre teraz 2x, čo nie je pekný programátorský návyk, nahradíme logiku v obsluhe šípky nadol v metóde Update() v KomponentaLevel volaním tejto funkcie. Kus kódu s obsluhou bude vyzerať takto:

if (hra.NovaKlavesa(Keys.Down))
{
    kostka.pozice = hraciPlocha.SpocitejSouradniceZamerovace(kostka);
    casOdPoslednihoPadu += casMeziPady;
}

Zostaňme v KomponentaLevel a založme si privátne premennú s pozíciou zameriavača:

private Vector2 poziceZamerovace;

Nastavenie tejto premennej sa bude vykonávať v Update(), vo vetve so stavom Hra:

poziceZamerovace = hraciPlocha.SpocitejSouradniceZamerovace(kostka);

Vykreslenie

Pozíciu by sme mali, teraz musíme zameriavač na toto miesto vykresliť. Zameriavač bude vyzerať rovnako ako kocka. Nebudeme pre neho tvoriť ďalší objekt (aj keď by to určite tiež šlo), ale len naučíme kocku vykresľovať sa na iné súradnice a transparentne.

Prejdeme do Kostka.cs a upravíme metódu Vykresli(). Označíme ju ako privátne a pred jej názov vložíme _ (znak podčiarknutia). Pridáme ju ďalšie 2 parametre navyše. Parameter pozice typu float bude vykresľovací pozície kocky. Takto môžeme kocku vykresľovať inde, než naozaj je. Niečo podobné sme už robili u metódy Kolize(). Parameter alfa bude alfaprůhlednost kocky. V kóde len farbu vynásobíme alfa, C# sám pochopí, že myslíme pozíciu z parametra metódy a nie atribút triedy:

Kód metódy vyzerá teraz takto:

private void _Vykresli(Vector2 okraj, LepsiSpriteBatch spriteBatch, Texture2D[] sprity, Vector2 pozice, float alfa)
{
    for (int j = 0; j < 4; j++)
        for (int i = 0; i < 4; i++)
            if (Policka[i, j] > 0)
                spriteBatch.Draw(sprity[Policka[i, j] - 1],
                    new Vector2(okraj.X + (i + pozice.X) * sprity[0].Width,
                        okraj.Y + (j + pozice.Y) * sprity[0].Height), Color.White * alfa);
}

Pridať metódy k vykreslenie kocky a zameriavača bude teraz hračka. Metóda Vykresli() jednoducho zavolá _Vykresli(), kde bude ako pozícia atribút pozice a alfa bude 1:

public void Vykresli(Vector2 levyOkraj, LepsiSpriteBatch spriteBatch, Texture2D[] sprity)
{
    _Vykresli(levyOkraj, spriteBatch, sprity, pozice, 1);
}

Metóda VykresliZamerovac() využije všetky parametre metódy _Vykresli():

public void VykresliZamerovac(Vector2 levyOkraj, LepsiSpriteBatch spriteBatch, Texture2D[] sprity, Vector2 pozice, float alfa)
{
    _Vykresli(levyOkraj, spriteBatch, sprity, pozice, alfa);
}

Ak vás teraz napadá, že by nám stačili metódy len 2 a to tak, že by Vykresli() volala VykresliZamerovac(), máte pravdu. Z hľadiska návrhu mi to však nepríde logické.

Presunieme sa do KomponentaLevel, do metódy Draw(). Tu za vykreslenie kocky a budúci kocky vložíme vykreslenie zameriavača:

kostka.VykresliZamerovac(poziceHraciPlochy, hra.spriteBatch, sprityPolicek, poziceZamerovace, 0.3f);

A spustíme:

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

Hra sa hrá oveľa lepšie.

Pulzovanie

Poďme si na koniec ukázať ešte jeden efekt, necháme alfaprůhlednost zameriavača pulzovať, ako sme to už robili s farbou mrakov. Opäť budeme ukladať smer pulzovanie do premennej typu int ako hodnoty 1 a -1. Samotnú hodnotu priehľadnosti potom do premennej typu float. Pridáme k triede tieto 2 atribúty:

private int zamerovacSmer;
private float zamerovacAlfa;

V Initialize() nastavíme predvolené hodnoty:

zamerovacAlfa = 0.0f;
zamerovacSmer = 1;

Do Update() do vetvy pre stav Hra vložíme obsluhu pulzovanie:

zamerovacAlfa += 0.02f * zamerovacSmer;
if (zamerovacAlfa > 0.5)
    zamerovacSmer = -1;
if (zamerovacAlfa < 0.2)
    zamerovacSmer = 1;

Hodnota zamerovacAlfa teraz pulzuje medzi 0.2 a 0.5.

Presuňte sa do Draw() a pozmeňte vykreslenie zameriavača a budúci kocky takto:

pristiKostka.Vykresli(new Vector2(930, 200 + (40 * zamerovacAlfa)), hra.spriteBatch, sprityPolicek);
kostka.VykresliZamerovac(poziceHraciPlochy, hra.spriteBatch, sprityPolicek, poziceZamerovace, zamerovacAlfa);

Spustite a vychutnajte si hru, pretože je hotová :) Pulzovanie sme využili aj pre efekt levitácie budúci kocky. Nabudúce, v lekcii MonoGame: Správa herných obrazoviek , sa pozrieme na správu herných obrazoviek a pripravíme sa na tvorbu herného menu.


 

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

 

Predchádzajúci článok
Hra Tetris v MonoGame: Bodovanie a dokončenie levelu
Všetky články v sekcii
Od nuly k tetrisu v MonoGame
Preskočiť článok
(neodporúčame)
MonoGame: Správa herných obrazoviek
Č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