Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
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í.

Lekce 6 - Hra tetris v MonoGame: Kostka

V minulé lekci, Rozdělení MonoGame hry do komponent, jsme si ukázali rozdělení hry do komponent.

Dnes konečně začneme s Tetrisem, přesněji s padající kostkou.

Instance kostky budou ve hře vždy jen 2. Jedna aktuálně padající, druhá ta, co padne příště. Protože jich tedy bude více a jedná se o objekt ve hře, nepoužijeme k reprezentaci kostky komponentu, ale pouze jednoduchou třídu. Komponenta obvykle spravuje větší celek logiky. Po dopadu se kostka spojí s hrací plochou, která tedy nebude kolekcí kostek, ale jednoduše dvourozměrným polem políček. Určitě by se hra dala řešit i jinak, ale tento způsob mi přišel nejjednodušší.

Vytvoříme si třídu reprezentující kostku Tetrisu. K projektu Robotris přidáme novou třídu Kostka, její modifikátor přístupu nastavíme na public.

public class Kostka

Kostce přidáme veřejnou vlastnost Policka, která bude dvourozměrný polem typu int. Tam budou uložena jednotlivá políčka, ze kterých se kostka skládá. Pole má rozměr 4x4 (kvůli té nejdelší kostce tvaru I), mohli bychom si ho s kostkou tvaru S představit asi takto:

0110
1100
0000
0000

1 označuje plné políčko, 0 prázdné. Setter vlastnosti označíme jako privátní, ale není to nutné a podobného výsledku bychom dosáhli i obyčejným atributem. Ve hrách se na vlastnosti nedbá tolik, jak jsme zvyklí, je to mimojiné i proto, že jsou pomalejší a u hry většinou požadujeme maximální výkon.

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

Dále přidáme pozici kostky na hrací ploše, bude typu Vector2 a bude se jednat o běžnou, veřejnou proměnnou. Jelikož je Vector2 struktura (tedy hodnotový typ), měli bychom při použití vlastností problém jednoduše modifikovat její složky. Určitě se nebojte používat úplně obyčejné veřejné proměnné, ve hře se na to tolik nehraje. Přídáme potřebný using:

using Microsoft.Xna.Framework;

a atribut:

public Vector2 pozice;

Jelikož víme, že pole jsou v C# řešeny referencí, nemůžeme jednoduše dosadit políčka jedné kostky do políček druhé. Výsledkem by bylo, že by obě kostky používaly ta samá políčka a když by se jedna třeba orotovala, orotovaly by se obě. Protože políčka budeme kopírovat často, vytvoříme si k tomu metodu. Ta políčka znovu založí a dosadí do nich hodnoty z těch, která kopírujeme. Okopírované pole metoda vrátí. Celého efektu dosáhneme jednoduše pomocí 2 for cyklů.

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;
}

Implementujme nyní konstruktor třídy, ten bude brát v parametru políčka, ze kterých kostku vytvoří. Dále zde resetujeme pozici kostky:

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

    pozice = new Vector2(0, 0);
}

Pád

Přidejme si jednoduchou metodu Spadni(), která vyvolá pád kostičky o 1 úroveň:

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

Rotace

To bylo takové odhlehčující :D Teď zas něco těžšího, napíšeme si metodu, která naše pole orotuje. Rotovat budeme ve směru hodinových ručiček. Rotace dosáhneme tak, že pole okopírujeme a z této kopie budeme políčka dosazovat do políček původních. Trik je v tom, že prohodíme X a Y souřadnice políček a 2. souřadnici odečteme od pozice 3. Pro lepší pochopení přikládám ilustraci:

Rotace kostky tetris - Od nuly k tetrisu v MonoGame

Pokud stále tápete, zkuste si to namalovat a papír otáčet. Pokud ani to nepomohlo, budete mi prostě muset věřit :) Metoda vypadá 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];
}

Rotace má problém, který v tutoriálu z důvodu jednoduchosti opomeneme. Některé kostky jsou rozměru 2x2 (v základní verzi jen kostka O), některé 3x3 a některé (v základní verzi jen kostka I) rozměru 4x4. Metoda výše rotuje kostku vždy jako pole 4x4 a menší kostky tedy nejsou centrovány. Toho by se dalo docílit nějakou další kontrolou a pokud budete naléhat, mohu ji po dokončení kurzu doplnit.

Zbývá kostku nějak vykreslit. Již jsme si ukázali, jak se vykreslují komponenty. U kostky to bude podobné, také ji přidáme metodu Vykresli(). SpriteBatch a příslušnou texturu políčka do ní dostaneme přes parametr. Je to kromě komponent, kde se předávala instance hry v konstruktoru, druhý způsob, jak se vypořádat se závislostmi. Opět opakuji, že tento způsob je lepší u jednotlivých herních objektů (např. jednotlivé kostky), komponenty jsou spíše pro celky (např. level obsahující kostky, hrací plochu...). Jelikož souřadnice kostky odpovídají souřadnicím v hrací ploše (tedy např. [6; 10]) a ne souřadnicím na obrazovce, přidáme ještě parametr okraj, který nám umožní kostku posouvat. Metoda projede jednotlivá políčka a ty s hodnotou větší než 0 vykreslí. V cyklech budeme pracovat s rozměry jednoho políčka, abychom je mohli skládat vedle sebe, rozměr zjistíme vlastnostmi Width a Height na instanci textury.

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žití Texture2D přidáme using:

using Microsoft.Xna.Framework.Graphics;

Třídu prozatím necháme a kostku si vyzkoušíme. Stáhněte si sprity políček z archivu pod článkem. Jedná se o sadu 15ti spritů pro políčka. Složku Policka/ rozbalte a celou ji přetáhněte do složky Sprity/ v MonoGame Pipeline Tool. Výsledek by měl vypadat takto:

Content po přidání políček - Od nuly k tetrisu v MonoGame

Přesuneme se do komponenty KomponentaLevel. Zde přidáme 3 privátní atributy:

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

První je instance právě padající kostky, druhý je pozice hrací plochy na pozadí levelu, třetí je sprite pro políčko.

V Initialize() do kostky dosadíme novou instanci třídy Kostka. Jelikož ještě nemáme generátor kostek a kostka potřebuje v konstruktoru vzor, ze kterého se má vytvořit, vytvoříme si na zkoušku jedno pole sami. Jelikož se v .NETu můžeme spolehnout, že nové celočíselné pole má v prvcích samé nuly, stačí nám nastavit 4 hodnoty, třeba té kostky, jak je výše u obrázku k otáčení:

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);

Ve stejné metodě ještě nastavíme proměnnou poziceHraciPlochy:

poziceHraciPlochy = new Vector2(366, 50);

V LoadContent() načteme sprite nějakého políčka do námi připravené proměnné:

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

Nyní se přesuneme do Draw() a za vykreslení pozadí přidáme vykreslení kostky:

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

Zde vidíte, jak kostka kód čistí, stará se o své vykreslování sama, i když ho musíme volat. Tak by to mělo být u všech herních objektů.

Vyzkoušíme:

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

Přidáme obsluhu rotace. Do Update() doplníme reakci na Enter nebo šipku nahoru:

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

Opět můžete vyzkoušet. Až na nešvar vzniklý rotací kostky 3x3 jako pole 4x4 to funguje docela dobře.

Příště, v lekci Hra tetris v MonoGame: Generátor kostek, si naprogramujeme generátor vzorů kostek a naučíme kostku náhodně generovat sprity políček.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 425x (11.9 MB)
Aplikace je včetně zdrojových kódů v jazyce C#

 

Předchozí článek
Rozdělení MonoGame hry do komponent
Všechny články v sekci
Od nuly k tetrisu v MonoGame
Přeskočit článek
(nedoporučujeme)
Hra tetris v MonoGame: Generátor kostek
Článek pro vás napsal David Hartinger
Avatar
Uživatelské hodnocení:
10 hlasů
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 se informační technologie naučil na Unicorn University - prestižní soukromé vysoké škole IT a ekonomie.
Aktivity