IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

6. diel - Hra tetris v MonoGame: Kostka

V minulej lekcii, Rozdelenie MonoGame hry do komponentov , sme si ukázali rozdelenie hry do komponentov. Dnes konečne začneme s Tetris, presnejšie s padajúce kockou.

Instance kocky budú v hre vždy len 2. Jedna aktuálne padajúce, druhá tá, čo padne nabudúce. Pretože ich teda bude viac a jedná sa o objekt v hre, nepoužijeme k reprezentácii kocky komponentu, ale iba jednoduchú triedu. Komponent zvyčajne spravuje väčší celok logiky. Po dopade sa kocka spojí s hracou plochou, ktorá teda nebude kolekcií kociek, ale jednoducho dvojrozmerných poľom políčok. Určite by sa hra dala riešiť aj inak, ale tento spôsob mi prišiel najjednoduchšie.

Vytvoríme si triedu reprezentujúci kocku Tetrisu. K projektu Robotris pridáme novú triedu Kostka, jej modifikátor prístupu nastavíme na public.

public class Kostka

Kocke pridáme verejnú vlastnosť Policka, ktorá bude dvojrozmerný poľom typu int. Tam budú uložené jednotlivé políčka, z ktorých sa kocka skladá. Pole má rozmer 4x4 (kvôli tej najdlhšej kocke tvaru I), mohli by sme si ho s kockou tvare S predstaviť asi takto:

0110
1100
0000
0000

1 označuje plné políčko, 0 prázdne. Setter vlastnosti označíme ako privátne, ale nie je to nutné a podobného výsledku by sme dosiahli aj obyčajným atribútom. V hrách sa na vlastnosti nedbá toľko, ako sme zvyknutí, je to mimojiné aj preto, že sú pomalšie a u hry väčšinou požadujeme maximálny výkon.

public int[,] Policka { get; private set; }

Ďalej pridáme pozíciu kocky na hracej ploche, bude typu Vector2 a bude sa jednať o bežnú, verejnú premennú. Keďže je Vector2 štruktúra (teda hodnotový typ), mali by sme pri použití vlastností problém jednoducho modifikovať jej zložky. Určite sa nebojte používať úplne obyčajné verejné premenné, v hre sa na to toľko nehrá. Pridáme potrebný using:

using Microsoft.Xna.Framework;

a atribút:

public Vector2 pozice;

Keďže vieme, že polia sú v C# riešené referencií, nemôžeme jednoducho dosadiť políčka jednej kocky do políčok druhej. Výsledkom by bolo, že by obe kocky používali tá istá políčka a keď by sa jedna treba orotovala, orotovaly by sa obe. Pretože políčka budeme kopírovať často, vytvoríme si k tomu metódu. Tá políčka znovu založia a dosadí do nich hodnoty z tých, ktorá kopírujeme. Okopírované pole metóda vráti. Celého efekt dosiahneme jednoducho pomocou 2 for cyklov.

private int[,] ZkopirujPolicka(int[,] policka)
{
    int [,] nova = new int [4, 4];
    for (int j = 0; j < 4; j++)
        for (int i = 0; i < 4; i++)
            nova[i, j] = policka[i, j];
    return nova;
}

Implementujte teraz konštruktor triedy, ten bude brať v parametri políčka, z ktorých kocku vytvorí. Ďalej tu resetujeme pozíciu kocky:

public Kostka(int[,] policka)
{
    Policka = ZkopirujPolicka(policka);

    pozice = new Vector2(0, 0);
}

Pád

Pridajme si jednoduchú metódu Spadni(), ktorá vyvolá pád kocky o 1 úroveň:

public void Spadni()
{
    pozice.Y++;
}

Rotácie

To bolo také odhlehčující : D Teraz zas niečo ťažšieho, napíšeme si metódu, ktorá naše polia orotujeme. Rotovať budeme v smere hodinových ručičiek. Rotácia dosiahneme tak, že pole dokopírujeme az tejto kópie budeme políčka dosadzovať do políčok pôvodných. Trik je v tom, že prehodíme X a Y súradnice políčok a 2. súradnicu odpočítame od pozície 3. Pre lepšie pochopenie prikladám ilustráciu:

Rotácia kocky tetris - Od nuly k tetrisu v MonoGame

Ak stále tápate, skúste si to namaľovať a papier otáčať. Ak ani to nepomohlo, budete mi proste musieť veriť :) Metóda vyzerá takto:

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

Rotácia má problém, ktorý v tutoriálu z dôvodu jednoduchosti zabudneme. Niektoré kocky sú rozmere 2x2 (v základnej verzii len kocka O), niektoré 3x3 a niektoré (v základnej verzii len kocka I) rozmeru 4x4. Metóda vyššie rotuje kocku vždy ako pole 4x4 a menšie kocky teda nie sú centrované. Toho by sa dalo docieliť nejakou ďalšou kontrolou a ak budete naliehať, môžem ju po dokončení kurzu doplniť.

Zostáva kocku nejako vykresliť. Už sme si ukázali, ako sa vykresľujú komponenty. U kocky to bude podobné, tiež ju pridáme metódu Vykresli(). SpriteBatch a príslušnú textúru políčka do nej dostaneme cez parameter. Je to okrem komponentov, kde sa odovzdávala inštancie hry v konstruktoru, druhý spôsob, ako sa vysporiadať so závislosťami. Opäť opakujem, že tento spôsob je lepší pri jednotlivých herných objektov (napr. Jednotlivé kocky), komponenty sú skôr pre celky (napr. Level obsahujúce kocky, hraciu plochu ...). Keďže súradnice kocky zodpovedajú súradniciam v hracej ploche (teda napr. [6; 10]) a nie súradniciam na obrazovke, pridáme ešte parameter okraj, ktorý nám umožní kocku posúvať. Metóda prejde jednotlivé políčka a tie s hodnotou väčšou ako 0 vykreslí. V cykloch budeme pracovať s rozmermi jedného políčka, aby sme ich mohli skladať vedľa seba, rozmer zistíme vlastnosťami Width a Height na inštanciu textúry.

public void Vykresli(Vector2 okraj, LepsiSpriteBatch spriteBatch, Texture2D sprite)
{
    for (int j = 0; j < 4; j++)
        for (int i = 0; i < 4; i++)
            if (Policka[i, j] > 0)
                spriteBatch.Draw(sprite, new Vector2(okraj.X + (i + pozice.X) * sprite.Width,
                okraj.Y + (j + pozice.Y) * sprite.Height), Color.White);
}

Kvôli použitie Texture2D pridáme using:

using Microsoft.Xna.Framework.Graphics;

Triedu zatiaľ necháme a kocku si vyskúšame. Stiahnite si sprity políčok z archívu pod článkom. Jedná sa o sadu 15tich spritov pre políčka. Zložku Policka/ rozbaľte a celú ju pretiahnite do zložky Sprity/ v MonoGame Pipeline Tool. Výsledok by mal vyzerať takto:

Content po pridaní políčok - Od nuly k tetrisu v MonoGame

Presunieme sa do komponenty KomponentaLevel. Tu pridáme 3 privátne atribúty:

private Kostka kostka;
private Vector2 poziceHraciPlochy;
private Texture2D spritePolicka;

Prvá je inštancia práve padajúce kocky, druhý je pozícia hracej plochy na pozadí leveli, tretí je sprite pre políčko.

V Initialize() do kocky dosadíme novú inštanciu triedy Kostka. Keďže ešte nemáme generátor kociek a kocka potrebuje v konstruktoru vzor, z ktorého sa má vytvoriť, vytvoríme si na skúšku jedno pole sami. Keďže sa v .NET môžeme spoľahnúť, že nové celočíselné pole má v prvkoch samé nuly, stačí nám nastaviť 4 hodnoty, treba tej kocky, ako je vyššie pri obrázku k otáčania:

int[,] vzor = new int[4, 4];
vzor[1, 0] = 1;
vzor[2, 0] = 1;
vzor[0, 1] = 1;
vzor[1, 1] = 1;
kostka = new Kostka(vzor);

V rovnakej metóde ešte nastavíme premennú poziceHraciPlochy:

poziceHraciPlochy = new Vector2(366, 50);

V LoadContent() načítame sprite nejakého políčka do nami pripravenej premennej:

spritePolicka = hra.Content.Load<Texture2D>(@"Sprity\Policka\5");

Teraz sa presunieme do Draw() a za vykreslenie pozadia pridáme vykreslenie kocky:

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

Tu vidíte, ako kocka kód čistí, stará sa o svoje vykresľovanie sama, aj keď ho musíme volať. Tak by to malo byť u všetkých herných objektov.

vyskúšame:

Ukážkový tetris Robotris v XNA - Od nuly k tetrisu v MonoGame

Pridáme obsluhu rotácie. Do Update() doplníme reakciu na Enter alebo šípku nahor:

if (hra.NovaKlavesa(Keys.Enter) || hra.NovaKlavesa(Keys.Up))
    kostka.Orotuj();

Opäť môžete vyskúšať. Až na nešvár vzniknutý rotáciou kocky 3x3 ako pole 4x4 to funguje celkom dobre. Nabudúce, v lekcii Hra tetris v MonoGame: Generátor kociek , si naprogramujeme generátor vzorov kociek a naučíme kocku náhodne generovať sprity políčok.


 

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

 

Predchádzajúci článok
Rozdelenie MonoGame hry do komponentov
Všetky články v sekcii
Od nuly k tetrisu v MonoGame
Preskočiť článok
(neodporúčame)
Hra tetris v MonoGame: Generátor kociek
Č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