Letní akce PHP týden
Pouze tento týden sleva až 80 % na kurzy PHP. Lze kombinovat s akcí Letní slevy na prémiový obsah!
Brno? Vypsali jsme pro vás nové termíny školení Základů programování a OOP v Brně!

Lekce 8 - Hra tetris v MonoGame: Hrací plocha

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é lekci, {PREVIOS}, jsme si naprogramovali generátor kostek do hry Tetris. Dnes si vytvoříme další herní objekt, kterým bude hrací plocha.

Hrací plocha

Již máme komponentu level, ve které se bude hra odehrávat. Máme generátor náhodných kostek a objekt padající kostky. Chybí nám hrací plocha.

Jedná se o objekt, který bude obsahovat dvourozměrné pole již spadaných kostek. Dále metodu ke zjištění, zda padající kostka s hrací plochou koliduje, metodu k připojení kostky k napadaným kostkám plochy, metodu k vymazání plných řad a samozřejmě metodu k vykreslení políček plochy.

Připojme si k projektu třídu HraciPlocha, opět jí nastavíme jako public a přidáme potřebné using:

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

Atributy

Podobně jako u kostky přidáme atribut s políčky:

private int[,] policka;

Dále šířku a výšku hrací plochy v políčkách:

private int sirka;
private int vyska;

Posledním atributem bude vykreslovací pozice hrací plochy na pozadí levelu:

private Vector2 pozice;

Vyprázdnění políček

Přidáme si jednoduchou metodu, která hrací plochu vyprázdní, přesněji inicializuje políčka:

public void Vyprazdni()
{
    policka = new int[sirka, vyska];
}

Konstruktor

Nyní vytvoříme konstruktor, ten bude brát v parametrech rozměry a pozici hrací plochy. Dále instanci inicializuje:

public HraciPlocha(int sirka, int vyska, Vector2 pozice)
{
    this.sirka = sirka;
    this.vyska = vyska;
    this.pozice = pozice;
    Vyprazdni();
}

Posazení nové kostky

Další metodou bude PosadKostku(), které předáme v parametru novou kostku a ona nám vrátí souřadnice, na kterých se kostka objeví nahoře, ve středu hrací plochy.

public Vector2 PosadKostku(Kostka kostka)
{
    return new Vector2(((int)sirka / 2) - 1, 0);
}

Nic složitého k přemýšlení, šířku hrací plochy prostě vydělíme celočíselně dvěma a máme střed. Metoda by se měla chovat podle toho, jakou kostku ji předáváme, aby byla správně vycentrovaná a u některých kostek bychom měli nastavit složku Y na -1, protože je první řada políček kostky prázdná. To ale zatím pro jednoduchust zanedbáme.

Připojení kostky k hrací ploše

Pojďme dále, implementujme metodu Pripoj(), která bude opět brát v parametru kostku a její políčka připojí k políčkům hrací plochy, tedy mezi ostatní, již spadlé kostky. Projedeme cykly všechna políčka kostky a pokud je políčko kostky zaplněné a zároveň nevyčnívá nahoře z hrací plochy (to se může na konci hry stát a musíme s tím počítat), připojíme ho na příslušnou pozici do políček hrací plochy.

public void Pripoj(Kostka kostka)
{
    // projetí všech políček kostky
    for (int j = 0; j < 4; j++)
        for (int i = 0; i < 4; i++)
        {
            // získání souřadnic políčka v hrací ploše
            int x = Convert.ToInt32(i + kostka.pozice.X);
            int y = Convert.ToInt32((j + kostka.pozice.Y));
            // políčko je zaplněné a nevyčnívá zhora z hrací plochy
            if ((kostka.Policka[i, j] > 0) && (y >= 0))
                policka[x, y] = kostka.Policka[i, j];
        }
}

Trochu nešikovné zde je, že musíme zkonvertovat reálné složky vektoru na celočíselné. Někdy se nám vektory ale zas hodí.

Kolize kostky

Další důležitou metodou k implementaci je metoda Kolize(), která vrátí true, pokud kostka koliduje s nějakými políčky v hrací ploše. Metoda bude brát jako parametry kostku a její pozici. Kostka samotná sice svou pozici obsahuje, ale nám se bude hodit ptát se: "Kolidovala by kostka, kdyby byla na pozici...", proto budeme předávat i pozici.

Z počátku budeme předpokládat, že ke kolizi nedošlo. Vytvoříme si tedy proměnnou kolize a nastavíme ji na false. Poté jako vždy projedeme všechna políčka kostky:

public bool Kolize(Kostka kostka, Vector2 pozice)
{
    bool kolize = false;
    for (int j = 0; j < 4; j++)
        for (int i = 0; i < 4; i++)

Nyní nás čeká poměrně složitá podmínka, kterou si však hezky rozepíšeme. Pokud je políčko v kostce plné:

if ((kostka.Policka[i, j] > 0) &&

a vyjelo z hrací plochy:

((j + pozice.Y >= vyska) || (i + pozice.X < 0) || (i + pozice.X >= sirka) ||

nebo se překrývá s jiným políčkem hrací plochy:

(policka[Convert.ToInt32(i + pozice.X), Convert.ToInt32(j + pozice.Y)] > 0)))

pak ke kolizi došlo:

kolize = true;

Nakonec výsledek vrátíme. Kompletní kód metody je následující:

public bool Kolize(Kostka kostka, Vector2 pozice)
{
    // budeme předpokládat, že nedošlo ke kolizi
    bool kolize = false;
    // projetí všech políček kostky
    for (int j = 0; j < 4; j++)
        for (int i = 0; i < 4; i++)
               // Pokud je políčko v kostce plné a
            if ((kostka.Policka[i, j] > 0) &&
               // vyjelo z hrací plochy nebo
               ((j + pozice.Y >= vyska) || (i + pozice.X < 0) || (i + pozice.X >= sirka) ||
               // překrývá se s jiným políčkem hrací plochy
               (policka[Convert.ToInt32(i + pozice.X), Convert.ToInt32(j + pozice.Y)] > 0)))
                    // došlo ke kolizi
                    kolize = true;
    return kolize;
}

Zbývá nám již jen vymazání řad a vykreslení.

Vymazání řad

Metoda VymazRady() vymaže plné řady v políčkách hrací plochy a samozřejmě políčka nad řadou sesune dolů. Návratová hodnota bude typu int a bude udávat, kolik řad bylo vymazáno. To bude později důležité pro výpočet skóre.

Nejprve si vytvoříme počítadlo vymazaných řad a to nastavíme na 0. Dále for cyklem projedeme všechny řady hrací plochy. Nakonec počítadlo vrátíme:

public int VymazRady()
{
    // kontrola plných řad
    int pocet = 0;
    for (int rada = 0; rada < vyska; rada++)
    {

    }
    return pocet;

V každé iteraci cyklu musíme zjistit, zda je daná řada plná. Zpočátku budeme předpokládat že ano, poté projedeme řadu políčko po políčku a pokud nalezneme nějaké prázdné, usoudíme, že řada plná není:

// předpokládáme, že je řada plná
bool plna = true;
// pokud je někde prázdné políčko, plná není
for (int i = 0; i <= (sirka - 1); i++)
    if (policka[i, rada] == 0)
        plna = false;

V případě plné řady zbývá posunout všechna políčka nad řadou o 1 dolů a zvýšit počítadlo plných řad. Pojedeme pozpátku:

// smazání plné řady
if (plna)
{
    // posun políček nad řadou dolů
    for (int j = (rada - 1); j > 0; j--)
        for (int i = 0; i < sirka; i++)
            policka[i, j + 1] = policka[i, j];
    pocet++;
}

Opět pro jistotu přikládám kód celé metody:

public int VymazRady()
{
    // kontrola plných řad
    int pocet = 0;
    for (int rada = 0; rada < vyska; rada++)
    {
        // předpokládáme, že je řada plná
        bool plna = true;
        // pokud je někde prázdné políčko, plná není
        for (int i = 0; i <= (sirka - 1); i++)
            if (policka[i, rada] == 0)
                plna = false;
        // smazání plné řady
        if (plna)
        {
            // posun políček nad řadou dolů
            for (int j = (rada - 1); j > 0; j--)
                for (int i = 0; i < sirka; i++)
                    policka[i, j + 1] = policka[i, j];
            pocet++;
        }
    }
    return pocet;
}

Vykreslení hrací plochy

Doplňme vykreslovací metodu, na té není nic složitého, vykreslení hrací plochy probíhá úplně stejně, jako tomu bylo u vykreslení kostky:

public void Vykresli(SpriteBatch spriteBatch, Texture2D[] sprity)
{
    for (int j = 0; j <= (vyska - 1); j++)
        for (int i = 0; i <= (sirka - 1); i++)
            if (policka[i, j] != 0)
                spriteBatch.Draw(sprity[policka[i, j] - 1], new Vector2(pozice.X + i * sprity[0].Width, pozice.Y + j * sprity[0].Height), Color.White);
}

Třída HraciPlocha je prozatím hotová. Příště, v lekci Hra Tetris v MonoGame: Zprovoznění hry, si ji vyzkoušíme a hru zprovozníme :)


 

Stáhnout

Staženo 196x (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?
1 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
Hra tetris v MonoGame: Generátor kostek
Všechny články v sekci
Od nuly k tetrisu v MonoGame
Miniatura
Následující článek
Hra Tetris v MonoGame: Zprovoznění hry
Aktivity (4)

 

 

Komentáře

Avatar
Mini
Člen
Avatar
Mini:9.12.2012 2:00

Ahoj, prosím vás, mám menší problém s kolizemi. Jde o simulaci gravitace. Mam panáčka kterého nechám z výšky spadnout a ptám se když jeho Rectangle bude kolidovat se Zemi tak jeho pozice bude na zemi. Jenže tohle mam a když už je na zemi tak pořad tak miniaturně skáče. Proste se to tam volá dokola. Dělám to asi tak 3 hodiny a furt nevím jak to vyladit tohle. Díky :)

Odpovědět 9.12.2012 2:00
Jste dobří jen v tom, co vás baví.
Avatar
matesax
Redaktor
Avatar
Odpovídá na Mini
matesax:9.12.2012 5:57

A toto souvisí s tímto článkem jak? Tím jej kritizuješ, doplňuješ, nebo přikládáš užitečné rady? Nebrání-li nic dopadu - plošiny atd., tak tě nechápu ani za mák - v Update přičítáš y pokud není menší než xxx a co řešit? Pokud máš kolem překážky, tak buď projížděj v cyklu - nebo pracuj v políčkové soustavě a budeš se moci ptát jen - Contains? etc...

Na kolize rectanglů je nejlepší a nejjednodušší:

http://msdn.microsoft.com/…10fyck0.aspx

Editováno 9.12.2012 5:58
 
Odpovědět 9.12.2012 5:57
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Mini
David Čápka:9.12.2012 11:05

Bez kódu ti těžko pomůžeme, ukaž nám, jak to máš udělané. A jak již pravil matesax, tyto dotazy příště prosím směřuj do diskuzního fóra, kde za ně budeme rádi. Zde zabírají místo u článku, se kterým nijak nesouvisí.

Odpovědět 9.12.2012 11:05
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
Mini
Člen
Avatar
Mini:9.12.2012 12:42

Aha, já si myslel že tady právě. Omlouvám se.

Odpovědět  +1 9.12.2012 12:42
Jste dobří jen v tom, co vás baví.
Avatar
Patrik Pastor:23. června 10:19

Jak to ze tady ve vykreslovanim neni ve vektorech ani na ose X ani Y zahrnuta hrana? Pri vykreslovani v kostce mame a navic Vektor okrajem, kde pridavame jeho slozky, pro lepsi efekt, ale tady ne, proc prosim? Dik za odpoved

 
Odpovědět 23. června 10:19
Avatar
Patrik Pastor:24. června 20:29

U metody vymazani kostek, mi prijde ze to vymazavani jde neprirozene dolu. Sice to opravdu ma efekt, ktery je popsan, ale vlastne radek o 1 vyssi dostane o jedna nizsi, a jde se dolu az na predposledni radek. Cili ano funguje to, ale logictejsi mi prijde aby radek o 1 nizsi dostal radek nad nim. Abych ukazal co presne myslim:

policka[i, j + 1] = policka[i, j]

po celem cyklu, prebira VYSSI radek (j+1) od NIZSIHO (j). A toto bych dal presne naopak, aby to bylo prirozenejsi. Nebo to je pro autora vice prirozenejsi takto?

 
Odpovědět 24. června 20:29
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.