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

7. diel - Hra tetris v MonoGame: Generátor kociek

V minulej lekcii, Hra tetris v MonoGame: Kostka , sme si naprogramovali kocku do hry Tetris. Dnes si vytvoríme generátor náhodných kociek, presnejšie vzorov kociek.

Súbor so vzormi

Vzory kociek budeme ukladať do textového súboru. Je to oveľa pohodlnejšie a čistejšie riešenie, než búšiť ich do zdrojového kódu, kam patrí naozaj len herné logika. Všetky takéto dáta, mapy, dlhšie texty a podobne by mala byť uložená externe. Súbor by mal byť v reáli opatrený nejakým hashom alebo vybudovaný do exe súboru hry, aby ho hráč nemohol editovať a dať si také kocky, s ktorými by nahral vysoké skóre. My si pre jednoduchosť súbor pomenujeme tak, aby nikoho nenapadlo v ňom kocky hľadať :)

K projektu Robotris pripojíme nový textový súbor (pridaním nového Item, ako to už poznáme, viď obrázok) s menom netfx.dll:

Pridanie nového súboru do projektu - Od nuly k tetrisu v MonoGame

Do súboru vložíme vždy na 4 riadky kocku a 5. riadok necháme prázdny. Počítačmi je to síce jedno, ale my sa v súbore lepšie vyznáme. Do súboru vložte nasledujúce dáta:

1000
1110
0000
0000

0010
1110
0000
0000

0000
0110
0110
0000

0000
1111
0000
0000

0100
1110
0000
0000

0000
1100
0110
0000

0000
0110
1100
0000

0000
0100
0000
0000

1010
0100
1010
0000

0100
1110
0100
0000

1110
0100
0100
0000

0000
0110
0000
0000

Prvých 7 kociek je z klasického tetrisu, presnejšie tetrisu verzie A, ktorý tu vyrábame. Ďalšie sú nové kocky, ktoré v súbore necháme, ale v tomto kurze je nebudeme používať. Vy si hru potom môžete vylepšiť a dať treba do ďalšieho módu alebo ich nechať padať od určitého levelu.

Posledné, čo je potrebné urobiť, je zaškrtnúť súbor netfx.dll v SolutionExploreru a potom v okne Properties nastaviť Copy to Output Directory na Copy always:

Súbory vo Visual Studio v priečinku s aplikáciou - Od nuly k tetrisu v MonoGame

Tým docielime to, že Visual Studio bude tento súbor pridávať do zložky sa skompilovanú hrou a my ho jednoducho budeme môcť načítať. Pre generátor je teda všetko pripravené.

Generátor kociek

Generátor kociek si načíta vzory kociek z textového súboru do pripravenej kolekcie vzorov. Ten potom vždy jeden náhodne vyberie a na jeho základe vygeneruje novú kocku.

Pridáme novú triedu GeneratorKostek, modifikátor prístupu opäť upravíme na public:

public class GeneratorKostek

Pridáme niekoľko atribútov. Bude to kolekcia vzorov kociek vzory, čo budú nám poznáme poľa intů 4x4. Atribút soubor udáva cestu k súboru so vzormi. Posledným atribútom je inštancia generátora náhodných čísel.

private List<int[,]> vzory;
private string soubor;
private Random nahodnyGenerator;

Atribúty následne inicializujeme v konstruktoru, ktorý bude brať parameter s cestou k súboru so vzormi:

public GeneratorKostek(string soubor)
{
    vzory = new List<int[,]>();
    nahodnyGenerator = new Random();
    this.soubor = soubor;
}

Parsovanie súboru

Prejdime k parsovanie súboru. Pripravme si metódu NactiKostky(). V nej si otvoríme textový súbor na čítanie:

public void NactiKostky()
{
    using (StreamReader sr = new StreamReader(soubor))
    {

    }
}

Nezabudnite do using (teraz myslím príkazy using na úplnom začiatku zdrojáky) pridať:

using System.IO;

V bloku using si pred čítaním pripravíme niekoľko premenných:

string radka;
int[,] vzor = new int[4, 4];
int j = 0;
int cisloRadky = 1;

Premenná radka je posledný načítané riadok textového súboru. Premenná vzor je nám známe pole, do ktorého budeme jednotlivé vzory zo súboru ukladať. j je zvislá súradnice vo vzore, ďalej bude i i. cisloRadky udáva na ktorej riadku súboru sa nachádzame.

Začne načítanie a za inicializácii premenných vložme nasledujúci kód:

while ((radka = sr.ReadLine()) != null)
{
    // každou pátou řádku vynecháme, jinak pokračujeme
    if ((cisloRadky % 5) != 0)
    {

    }
    cisloRadky++;
}

Cyklus while načíta nový riadok tak dlho, kým nie je na konci súboru. Ďalšie kód sa vykoná len vtedy, ak nie je číslo riadky deliteľné piatimi, pretože každá 5. riadok je prázdna.

Pokračujeme v bloku podmienky a načítame daný riadok do príslušného riadku vo vzore:

for (int i = 0; i < 4; i++)
    vzor[i, j] = int.Parse(radka[i].ToString());

Všetky 4 znaky na riadku prevedieme na string a následne naparsujeme ako int. Získanú hodnotu uložíme na aktuálne súradnice vo vzore.

Teraz nám ostáva len obsluha premennej j, ktorá nás posúva vo vzore o riadok nadol. Akonáhle však presiahne hodnotu 3 (teda 4., posledný riadok), musíme ju vynulovať, vzor pridať do zoznamu vzorov a vymazať:

if (j > 3)
{
    vzory.Add(vzor);
    j = 0;
    vzor = new int[4, 4];
}

Pretože to bolo trochu náročnejšie, tu je kompletný kód metódy:

public void NactiKostky()
{
    using (StreamReader sr = new StreamReader(soubor))
    {
        // příprava proměnných
        string radka;
        int[,] vzor = new int[4, 4];
        int j = 0;
        int cisloRadky = 1;
        // přečtení všech řádek textového souboru
        while ((radka = sr.ReadLine()) != null)
        {
            // každou pátou řádku vynecháme, jinak pokračujeme
            if ((cisloRadky % 5) != 0)
            {
                // načtení čísel z řádky do vzoru
                for (int i = 0; i < 4; i++)
                    vzor[i, j] = int.Parse(radka[i].ToString());
                j++;
                // načetli jsme poslední řádku?
                if (j > 3)
                {
                    // vložíme vzor
                    vzory.Add(vzor);
                    // a resetujeme proměnné
                    j = 0;
                    vzor = new int[4, 4];
                }
            }
            cisloRadky++;
        }
    }
}

Generovanie náhodné kocky

Generovanie náhodné kocky je už primitívne, iba si vyberieme nejaký vzor a vrátime kocku, vytvorenú na jeho základe. Pridajme metódu Generuj(), ktorá berie ako parameter počet vzorov, z ktorých sa má generovať. Takto môžeme s parametrom 7 generovať jednoduché kocky, s parametrom 12 aj tie zložité a ďalej si môžete pridať napríklad ešte nejaké bonusové.

public Kostka Generuj(int pocet)
{
    int index = nahodnyGenerator.Next(0, pocet);

    return new Kostka(vzory[index]);
}

Presuňme sa do KomponentaLevel. Triede pridáme atribút s inštanciou nášho generátora kociek:

private GeneratorKostek generatorKostek;

Presunieme sa do Initialize(), kde odstránime vytvorenie testovacieho vzoru aj vytvorenie kocky. Ďalej tu vytvoríme generátor kociek a rovno aj vzory načítame:

generatorKostek = new GeneratorKostek("netfx.dll");
generatorKostek.NactiKostky();

Za načítanie kociek pridáme vytvorenie kocky pomocou generátora:

kostka = generatorKostek.Generuj(7);

Hru niekoľkokrát spustíme, kocka bude vždy náhodná. Opäť je vidieť problém s centrovaním kociek, ale to zatiaľ nebudeme riešiť.

Náhodné sprity políčok

Všetky políčka kocky sú rovnaké a to pre nich máme 15 rôznych spritov. Budeme chcieť, aby si kocka pri vytvorení náhodne vygenerovala sprity pre svoje políčka, teda aby v jednotlivých políčkach neboli len hodnoty 0 - 1, ale 0 - 15. Vzor kocky, ktorý jej posielame do konstruktoru, bude vždy obsahovať len hodnoty 0 - 1. Kocka si ale potom sama vygeneruje tieto indexy pre sprity.

Presunieme sa späť do triedy Kostka. Pridáme atribút s inštanciou generátora náhodných čísel:

private Random nahodnyGenerator;

V konstruktoru ju inicializujeme:

nahodnyGenerator = new Random();

Pridáme novú metódu GenerujSprity(), ktorá nájde jednotky v políčkach a zamení ich za náhodné číslo medzi 1 - 15 (mätúce môže byť, že metóde Next() zadávame parametre 1 a 16, ale horná hranica už do rozmedzia nepatria):

private void GenerujSprity()
{
    // výběr políček pro kostku
    for (int j = 0; j < 4; j++)
        for (int i = 0; i < 4; i++)
            if (Policka[i, j] > 0)
                Policka[i, j] = nahodnyGenerator.Next(1, 16);
}

Metódu zavoláme v konstruktoru po skopírovaní políčok a vytvorenie náhodného generátora:

GenerujSprity();

Teraz upravíme metódu Vykresli() tak, aby namiesto jedného Spritu brala celé pole textúr a potom ich vykreslovala podľa indexu v políčku:

public void Vykresli(Vector2 okraj, LepsiSpriteBatch spriteBatch, Texture2D[] sprity)
{
    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);
}

Kocka je pre dnešok hotová. Ešte musíme zmeniť jej obsluhu, vráťme sa do KomponentaLevel.

Deklaráciu atribútu spritePolicka zmeníme na deklaráciu pole, atribút tiež premenujeme:

private Texture2D[] sprityPolicek;

Presunieme sa do LoadContent() a namiesto pôvodného načítanie jedného spritov jich načítame celé pole pomocou for cyklu. Pole je najprv nutné inicilizovat.

// Načtení spritů kostek
sprityPolicek = new Texture2D[15];
for (int i = 0; i < 15; i++)
    sprityPolicek[i] = hra.Content.Load<Texture2D>("Sprity\\Policka\\" + (i + 1).ToString());

Posledná úprava spočíva v premenovaní premennej u metódy Draw():

kostka.Vykresli(poziceHraciPlochy, hra.spriteBatch, sprityPolicek);

výsledok:

Ukážková hra Tetris v XNA - Od nuly k tetrisu v MonoGame

V budúcej lekcii, Hra tetris v MonoGame: Hracia plocha , na nás čaká hracia plocha.


 

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

 

Predchádzajúci článok
Hra tetris v MonoGame: Kostka
Všetky články v sekcii
Od nuly k tetrisu v MonoGame
Preskočiť článok
(neodporúčame)
Hra tetris v MonoGame: Hracia plocha
Č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