ITnetwork Flashka zdarma C a C++ týden
Akce! Pouze tento týden sleva až 80 % na kurzy C++. Lze kombinovat s akcí 50 % bodů navíc na prémiový obsah!
Brno? Vypsali jsme pro vás nové termíny školení Základů programování a OOP v Brně!

Lekce 6 - Hra tetris v MonoGame: Kostka

Unicorn College Tento obsah je dostupný zdarma v rámci projektu IT lidem.
Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V minulém dílu seriálu MonGame 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 MonoGame Pipeline Tool. Výsledek by měl vypadat takto:

Content po přidání políček

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 380x (11.9 MB)
Aplikace je včetně zdrojových kódů v jazyce C#

 

 

Článek pro vás napsal David Čápka
Avatar
Jak se ti líbí článek?
3 hlasů
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 sítě se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.
Předchozí článek
Rozdělení MonoGame hry do komponent
Všechny články v sekci
Od nuly k tetrisu v MonoGame
Miniatura
Následující článek
Hra tetris v MonoGame: Generátor kostek
Aktivity (3)

 

 

Komentáře
Zobrazit starší komentáře (1)

Avatar
David Čápka
Tým ITnetwork
Avatar
David Čápka:1.11.2012 17:59

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
Jsem moc rád, že jsi na síti, a přeji ti top IT kariéru, ať jako zaměstnanec nebo podnikatel. Máš na to! :)
Avatar
it.severa
Člen
Avatar
it.severa:1.11.2012 21:45

díky moc

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

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:4.3.2015 23:34

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:30.7.2016 14:30

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

Odpovědět 30.7.2016 14:30
Jsem lama co se roky snaží naučit napsat aspoň pár řádků a furt mu to nejde...
Avatar
Michal
Člen
Avatar
Michal:18.8.2018 16:20

ahoj
zkusil jsem dle příkladu rotovat tvar ESKO (S). Otáčelo se to po směru. Když jsem se ale na to podíval lépe, tak ESKO se otáčí zároveň i proti směru. Tak jsem vyzkoušel rotovat tvar ELKO (L). ELKO se otáčí pouze proti směru ručiček.

 
Odpovědět 18.8.2018 16:20
Avatar
Michal
Člen
Avatar
Michal:18.8.2018 16:28

pro rotaci po směru hodinových ručiček jsem si odvodil toto:
Y=X, X=3-Y

 
Odpovědět 18.8.2018 16:28
Avatar
Patrik Pastor:21. června 21:21

funguje to vklidu, ale stale me nenapada jak na to autor prisel. Hledam jsem na netu transponovane matice, kde se prohazuji sloupce za radky, ale nenasel jsem na to vzorec. Pokud je to ono, poprosil bych autora o nahledovy vypocet nebo fakt jenom strucny, aych nebyl dotiravy, ale zajima me proste jak ma to autor prisel

 
Odpovědět 21. června 21:21
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Patrik Pastor
David Čápka:21. června 21:40

Když prohodíš v matici souřadnice (X <-> Y), tak ji orotuješ. To jsou základy práce s maticemi, které jsem se učil na SŠ a určitě to půjde vygooglit. Ta trojka tam je velikost matice a v tomto případě to bude hodnoty v dané ose zrcadlit (protože místo první je poslední, místo druhé předposlední atd.). Předpokládám, že jsem to tam dal, že to jinak rotovalo na jinou stranu. Můžeš to zkusit dát pryč, nějaká rotace by měla proběhnout i bez toho.

Odpovědět 21. června 21:40
Jsem moc rád, že jsi na síti, a přeji ti top IT kariéru, ať jako zaměstnanec nebo podnikatel. Máš na to! :)
Avatar
Odpovídá na David Čápka
Patrik Pastor:21. června 21:42

vklidu, ale spis jsem se ptal jestli jsi to nejak pocital, nebo to proste zkousel dokud to nevyjde (i tak se da)

 
Odpovědět 21. června 21:42
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 10 zpráv z 11. Zobrazit vše