11. díl - Hra tetris v XNA: Vychytávky v levelu

C# .NET XNA game studio Robotris Hra tetris v XNA: Vychytávky v levelu

V minulém dílu seriálu XNA tutoriálů jsme dokončili level Tetrisu a zprovoznili bodování a pauzu. Tento díl je poslední, kdy se budeme levelu věnovat. Vylepšíme rotaci kostky a přidáme do hry prvek zaměřovač.

Vylepšená rotace kostky

Jak se kostka rotuje určitě není nejpřirozenější. V pravém Tetrisu jsou kostky ručně nastavené v několika polohách, některé mají třeba jen 2 (kostka I), některé jen jednu (kostka O). Tento způsob ručního zadávání je ale velmi neprogramátorský a nic bychom se tím nenaučili. Náš způsob zatím rotuje každou kostku jako čtvercovou matici o hraně 4. Problém je v tom, že většina kostek má hranu jen 3, některé dokonce 2. Metodu rotace tedy upravíme tak, aby rotovala dle rozměrů kostky, přesněji dle její hrany.

Nejprve si upravme naše kostky v textovém souboru tak, aby byly vždy umístěny v levém horním rohu. Samozřejmě musíme brát kostku jako čtverec a podle delší hrany určit její polohu (to se týká zejména kostky I). Soubor netfx.dll tedy přepíšeme na následující obsah:

1000
1110
0000
0000

0010
1110
0000
0000

1100
1100
0000
0000

0000
1111
0000
0000

0100
1110
0000
0000

1100
0110
0000
0000

0110
1100
0000
0000

1000
0000
0000
0000

1010
0100
1010
0000

0100
1110
0100
0000

1110
0100
0100
0000

1100
0000
0000
0000

Otevřeme si třídu Kostka.cs a přidejme ji veřejný atribut hrana:

public int hrana;

Třídě přidejme privátní metodu ZjistiHranu(), ve které budeme nejprve předpokládat, že má kostka hranu 1, tedy nejnižší možnou, prázdnou kostku ve hře mít jistě nebudeme.

private void ZjistiHranu()
{
        hrana = 1;
}

Hranu zjistíme tak, že v metodě projedeme všechna políčka. Pokud narazíme na políčko s nenulovou hodnotou, zeptáme se, jestli je od levého horního rohu dále, než je současná známá hrana. Pokud ano, do hrany vždy uložíme tu větší souřadnici plného políčka.

for (int j = 0; j < 4; j++)
        for (int i = 0; i < 4; i++)
        {
                // nalezeno políčko dále od levého horního rohu, než je současná hrana
                if ((Policka[i, j] > 0) && (((i + 1) > hrana) || ((j + 1) > hrana)))
                {
                        if (i > j)
                                hrana = i + 1;
                        else
                                hrana = j + 1;
                }
        }

Metodu zavoláme v konstruktoru po generování spritů:

ZjistiHranu();

Hranu máme uloženou, nyní je hračka upravit metodu pro rotaci (budeme pracovat pouze s částí pole velikosti hrany):

public void Orotuj()
{
        // pomocné pole
        int[,] a = ZkopirujPolicka(Policka);
        // rotace pole jako matice prohozením souřadnic
        for (int y = 0; y < hrana; y++)
                for (int x = 0; x < hrana; x++)
                        Policka[x, y] = a[y, (hrana - 1) - x];
}

Na velikost kostky bude reagovat i metoda PosadKostku() v třídě HraciPlocha.cs:

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

Hru nyní vyzkoušíme, rotace je mnohem preciznější.

Zaměřovač

Některé verze Tetrisu ukazují přímo místo, kam kostka spadne, pokud stiskneme šipku dolů. Tento prvek jsem nazval zaměřovačem, ten do hry přidáme.

Získání pozice

Přejděme do HraciPlocha.cs, kam přidáme metodu SpocitejSourad­niceZamerovace(). Jako parametr bude brát kostku a vracet bude pozici jako Vector2. To budou souřadnice místa, kam by kostka spadla, když bychom stiskli šipku dolů. Zjištění těchto souřadnic již tedy umíme, stačí jednoduše posouvat pozici kostky dolů tak dlouho, dokud nebude kolidovat. Pozice o 1 výše poté vrátíme.

public Vector2 SpocitejSouradniceZamerovace(Kostka kostka)
{
        Vector2 pozice = kostka.pozice;
        // zkusíme posouvat kostku dolů tak dlouho, dokud nebude kolidovat
        while (!Kolize(kostka, pozice))
                pozice.Y++;
        // vrátíme souřadnici o 1 nahoru, kdy ještě nekolidovala
        return new Vector2(kostka.pozice.X, pozice.Y - 1);
}

Protože zjištění této pozice je ve hře nyní 2x, což není hezký programátorský návyk, nahradíme logiku v obsluze šipky dolů v metodě Update() v KomponentaLevel voláním této funkce. Kus kódu s obsluhou bude vypadat takto:

if (hra.NovaKlavesa(Keys.Down))
{
        kostka.pozice = hraciPlocha.SpocitejSouradniceZamerovace(kostka);
        casOdPoslednihoPadu += casMeziPady;
}

Zůstaňme v KomponentaLevel a založme si privátní proměnnou s pozicí zaměřovače:

private Vector2 poziceZamerovace;

Nastavení této proměnné se bude provádět v Update(), ve větvi se stavem Hra:

poziceZamerovace = hraciPlocha.SpocitejSouradniceZamerovace(kostka);

Vykreslení

Pozici bychom měli, nyní musíme zaměřovač na toto místo vykreslit. Zaměřovač bude vypadat stejně jako kostka. Nebudeme pro něj tvořit další objekt (i když by to jistě také šlo), ale jen naučíme kostku vykreslovat se na jiné souřadnice a průhledně.

Přejdeme do Kostka.cs a upravíme metodu Vykresli(). Označíme ji jako privátní a před její název vložíme "_" (podtržítko). Přidáme ji další 2 parametry navíc. Parametr pozice typu float bude vykreslovací pozice kostky. Takto můžeme kostku vykreslovat jinde, než opravdu je. Něco podobného jsme již dělali u metody Kolize(). Parametr alfa bude alfaprůhlednost kostky. V kódu jen barvu vynásobíme alfa, C# sám pochopí, že myslíme pozici z parametru metody a ne atribut třídy:

Kód metody vypadá nyní takto:

private void _Vykresli(Vector2 okraj, LepsiSpriteBatch spriteBatch, Texture2D[] sprity, Vector2 pozice, float alfa)
{
        for (int j = 0; j < 4; j++)
                for (int i = 0; i < 4; i++)
                        if (Policka[i, j] > 0)
                                spriteBatch.Draw(sprity[Policka[i, j] - 1],
                                        new Vector2(okraj.X + (i + pozice.X) * sprity[0].Width,
                                                okraj.Y + (j + pozice.Y) * sprity[0].Height), Color.White * alfa);
}

Přidat metody k vykreslení kostky a zaměřovače bude nyní hračka. Metoda Vykresli() jednoduše zavolá _Vykresli(), kde bude jako pozice atribut pozice a alfa bude 1:

public void Vykresli(Vector2 levyOkraj, LepsiSpriteBatch spriteBatch, Texture2D[] sprity)
{
        _Vykresli(levyOkraj, spriteBatch, sprity, pozice, 1);
}

Metoda VykresliZamerovac() využije všechny parametry metody _Vykresli():

public void VykresliZamerovac(Vector2 levyOkraj, LepsiSpriteBatch spriteBatch, Texture2D[] sprity, Vector2 pozice, float alfa)
{
        _Vykresli(levyOkraj, spriteBatch, sprity, pozice, alfa);
}

Pokud vám nyní napadá, že by nám stačily metody jen 2 a to tak, že by Vykresli() volala VykresliZamero­vac(), máte pravdu. Z hlediska návrhu mi to však nepřijde logické.

Přesuneme se do KomponentaLevel, do metody Draw(). Zde za vykreslení kostky a příští kostky vložíme vykreslení zaměřovače:

kostka.VykresliZamerovac(poziceHraciPlochy, hra.spriteBatch, sprityPolicek, poziceZamerovace, 0.3f);

A spustíme:

Ukázkový Tetris v XNA Game Studio

Hra se hraje mnohem lépe.

Pulzování

Pojďme si na konec ukázat ještě jeden efekt, necháme alfaprůhlednost zaměřovače pulzovat, jako jsme to již dělali s barvou mraků. Opět budeme ukládat směr pulzování do proměnné typu int jako hodnoty 1 a -1. Samotnou hodnotu průhlednosti poté do proměnné typu float. Přidáme k třídě tyto 2 atributy:

private int zamerovacSmer;
private float zamerovacAlfa;

V Initialize() nastavíme výchozí hodnoty:

zamerovacAlfa = 0.0f;
zamerovacSmer = 1;

Do Update() do větve pro stav Hra vložíme obsluhu pulzování:

zamerovacAlfa += 0.02f * zamerovacSmer;
if (zamerovacAlfa > 0.5)
        zamerovacSmer = -1;
if (zamerovacAlfa < 0.2)
        zamerovacSmer = 1;

Hodnota zamerovacAlfa nyní pulzuje mezi 0.2 a 0.5.

Přesuňte se do Draw() a pozměňte vykreslení zaměřovače a příští kostky takto:

pristiKostka.Vykresli(new Vector2(930, 200 + (40 * zamerovacAlfa)), hra.spriteBatch, sprityPolicek);
kostka.VykresliZamerovac(poziceHraciPlochy, hra.spriteBatch, sprityPolicek, poziceZamerovace, zamerovacAlfa);

Spusťte a vychutnejte si hru, protože je hotová :) Pulzování jsme využili i pro efekt levitace příští kostky. Příště se podíváme na správu herních obrazovek a připravíme se na tvorbu herního menu.


 

Stáhnout

Staženo 222x (5.92 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?
Ještě nikdo nehodnotil, buď první!


 



 

 

Komentáře

Avatar
wazzirr
Člen
Avatar
wazzirr:

Kde se dá zvýšit průhlednost?

 
Odpovědět 24.10.2012 13:24
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na wazzirr
David Čápka:

Pokud myslíš zaměřovače, tak tu přeci určují hodnoty v podmínkách u pulzování, tedy 0.2 a 0.5. Když chceš průhlednější, tak je o něco sniž.

Odpovědět 24.10.2012 13:33
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
wazzirr
Člen
Avatar
wazzirr:

ajo :D díky :)

 
Odpovědět 24.10.2012 22:17
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 3 zpráv z 3.