8. díl - Hra tetris v XNA: Hrací plocha

C# .NET XNA game studio Robotris Hra tetris v XNA: Hrací plocha

V minulém dílu seriálu XNA tutoriálů 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.cs, opět jí nastavíme jako public a přidáme potřebné usingy:

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ě si ji vyzkoušíme a hru zprovozníme :)


 

Stáhnout

Staženo 184x (5.79 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 (1 hlasů) :
55555


 


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: Zprovoznění hry

 

 

Komentáře

Avatar
Merry
Člen
Avatar
Merry:

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 Merry
matesax:

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 Merry
David Čápka:

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
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
Merry
Člen
Avatar
Merry:

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í.
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 4 zpráv z 4.