6. díl - Hra tetris v XNA: Kostka

C# .NET XNA game studio Robotris Hra tetris v XNA: Kostka

V minulém dílu seriálu XNA tutoriálů 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.cs, 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

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í seriálu 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 Solution Exploreru. Výsledek by měl vypadat takto:

Přidání složky spritů do XNA content

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 instanci nové Kostky. 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

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ě si naprogramujeme generátor vzorů kostek a naučíme kostku náhodně generovat sprity políček.


 

Stáhnout

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

 

  Aktivity (1)

Článek pro vás napsal David Čápka
Avatar
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.

Jak se ti líbí článek?
Celkem (3 hlasů) :
4.666674.666674.666674.666674.66667


 


Miniatura
Předchozí článek
Rozdělení XNA hry do komponent
Miniatura
Všechny články v sekci
Od nuly k tetrisu v XNA game studio
Miniatura
Následující článek
Hra tetris v XNA: Generátor kostek

 

 

Komentáře

Avatar
it.severa
Člen
Avatar
it.severa:

Ahoj super tutorial chtěl bych se zeptat jak na centrování jelikož vytvářím vlastní tetris a mám rozměr pole 6*6 což už je celkem znát - napadl mě medián ale to asi nebude to pravé ořechové děkuji moc za radu :)

 
Odpovědět 1.11.2012 17:54
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na it.severa
David Čápka:

Ahoj, centrování jsem nakonec přidal v pozdějších lekcích, dojdeš k němu :)

Odpovědět 1.11.2012 17:59
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
it.severa
Člen
Avatar
it.severa:

díky moc

 
Odpovědět 1.11.2012 21:45
Avatar
uvevax
Člen
Avatar
uvevax:

Moc tomuto nechápem, nie je tu návod ako pracovať s takýmito poliami?

public int[,] Policka { get; private set; }
 
Odpovědět 28.2.2013 11:37
Avatar
Flamuri
Člen
Avatar
Flamuri:

Ahoj, jak je ta ilustrace rotace, neotáčí se podle toho vzorce (X=Y, Y=3-X) spíš proti směru hodinových ručiček? Nějak na to nemůžu přijít. :D Děkuji moc za radu. :)

Odpovědět 4.3.2015 23:34
Neudělej stejnou chybu dvakrát!
Avatar
Odpovídá na Flamuri
Lukáš Hypša:

Já když jsem to zkoušel tak se mi to taky otáčelo na druhou stranu :D

Odpovědět 30. července 14:30
I když se programování učím jenom z interetu, velmi mě baví a doufám, že se tím jednou budu i živit.
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 6 zpráv z 6.