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
:
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:
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:
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#