9. díl - Hra Tetris v XNA: Zprovoznění hry

C# .NET XNA game studio Robotris Hra Tetris v XNA: Zprovoznění hry

V minulém dílu seriálu XNA tutoriálů jsme si naprogramovali hrací plochu k tetrisu. Dnes zprovozníme základy hry tak, aby se dala hrát.

Přesuneme se do KomponentaLevel. Přidáme atribut hraciPlocha, ve kterém bude uložena instance třídy z minula:

private HraciPlocha hraciPlocha;

V Initialize() nastavíme hrací ploše rozměry a pozici:

hraciPlocha = new HraciPlocha(10, 20, poziceHraciPlochy);

Přidáme metodu, která nám podá další kostku. Bude velmi jednoduchá, vygeneruje novou kostku a nastaví ji pozici nahoru doprostřed:

public void DalsiKostka()
{
        kostka = generatorKostek.Generuj(7);
        kostka.pozice = hraciPlocha.PosadKostku(kostka);
}

Vidíme, že jsme použili k zarovnání kostky metodu hrací plochy. Metodu zavoláme na konci Initialize(), místo generování kostky:

DalsiKostka();

Hrací plochu budeme určitě chtít vykreslovat, za vykreslení kostky v Draw() tedy přidáme:

hraciPlocha.Vykresli(hra.spriteBatch, sprityPolicek);

Skvělé. Můžete zkusit spustit, výsledkem by zatím měla být náhodná kostka nahoře ve středu hrací plochy.

Real-time logika

Konečně se dostáváme k real-time logice celé hry. Začněme pádem kostky.

Pád kostky

Pád kostky nastane jednou za určitou dobu. Pro začátek bude tato doba 1 sekunda, později se však bude hra zrychlovat. Přidejme si atribut rychlost typu float:

private float rychlost;

V Initialize() ho nastavíme na hodnotu 1:

rychlost = 1;

Budeme potřebovat měřit čas od posledního spadnutí kostky. Přidejme podobně ještě jednu proměnnou jménem casOdPosledni­hoPadu:

private float casOdPoslednihoPadu;

V Initialize() ji nastavíme na 0:

casOdPoslednihoPadu = 0;

Přesuneme se do Update(). Zde si vytvoříme proměnnou sekundy, do které uložíme uběhlé vteřiny od posledního update (bude to samozřejmě desetinné číslo, tedy float):

float sekundy = (float)gameTime.ElapsedGameTime.TotalSeconds;

Takto získaný počet sekund poté přičteme k casOdPosledni­hoPadu:

casOdPoslednihoPadu += sekundy;

Nyní se stačí podívat, zda uběhlo již tolik, kolik říká rychlost hry. Pokud ano, necháme kostku spadnout a vynulujeme casOdPosledni­hoPadu:

if (casOdPoslednihoPadu >= rychlost)
{
        kostka.Spadni();
        casOdPoslednihoPadu = 0;
}

Můžeme vyzkoušet.

Ukázkový Tetris v XNA Game Studio

Kostka po chvilce samozřejmě vyjede z hrací plochy. Musíme reagovat na kolizi a v tomto případě kostku připojit k hrací ploše. K obojímu máme připravené metody a neměl by to být žádný problém.

Kolize nastane ve chvíli, kdy je kostka již tam, kde být nemá. To opravíme jednoduše tím, že ji snížíme Y o jedna. Kostku poté připojíme k hrací ploše a zavoláme vymazání případných plných řad. Nakonec si řekneme o další kostku.

if (hraciPlocha.Kolize(kostka, kostka.pozice))
{
        kostka.pozice.Y--;
        hraciPlocha.Pripoj(kostka);
        hraciPlocha.VymazRady();
        DalsiKostka();
}

Celou tuto kontrolu vložíme za volání metody Spadni() v Update(), protože logicky ke kolizi dojde poté, co kostka spadne příliš nízko nebo na jinou kostku.

Hru spustíme a necháme ji chvíli běžet.

Ukázkový Tetris v XNA Game Studio

Vidíme, že kolize nastane jak při dosažení dna hrací plochy, tak při střetu s jinou kostkou. Stejně nám kolize nastane i v případě, že s kostkou vyjedeme z hrací plochy po stranách.

Pohyb kostkou

Aby se dala hra opravdu hrát, mělo by jít kostkou pohybovat. Posouvat kostku by nemělo být nic těžkého, pouze musíme kontrolovat kolize, aby nám nevyjela z hrací plochy nebo nenajela do jiné kostky. Zde se nám přesně hodí parametr pozice v metodě Kolize(), budeme se totiž ptát vždy na to, jestli je budoucí souřadnice kostky volná a pokud ne, pohyb vůbec neprovedeme.

if ((hra.klavesy.IsKeyDown(Keys.Right)) &&
   (!hraciPlocha.Kolize(kostka, new Vector2(kostka.pozice.X + 1, kostka.pozice.Y))))
        kostka.pozice.X++; // pohyb doprava
if ((hra.klavesy.IsKeyDown(Keys.Left)) &&
        (!hraciPlocha.Kolize(kostka, new Vector2(kostka.pozice.X - 1, kostka.pozice.Y))))
        kostka.pozice.X--; // doleva

Kód vložíme za uložení sekund do Update() a vyzkoušíme.

Pohyb je velmi rychlý, protože se klávesa vyhodnocuje příliš často. S tímto problémem jsme se již setkali v prvních dílech našeho seriálu. Jedna možnost by byla použít naši metodu NovaKlavesa() a pohybovat kostkou klikáním. To by však bylo nepohodlné. Vytvoříme si tedy časovač, podobně jako pro pád kostky a pohyb budeme vykonávat jen tehdy, pokud uběhl určitý čas.

Třídě přidáme 2 další atributy, casOdPosledni­hoStisku a prodlevaKlavesy:

private float casOdPoslednihoStisku;
private float prodlevaKlavesy;

Proměnné inicializujeme v Initialize():

prodlevaKlavesy = 0.06f;
casOdPoslednihoStisku = 0;

Nyní budeme postupovat úplně stejně jako u pádu, v Update() budeme přičítat uplynulé sekundy k času od posledního stisku klávesy. Obsluhu kláves poté vložíme do podmínky a případně vynulujeme čas od posledního stisku.

casOdPoslednihoStisku += sekundy;
if (casOdPoslednihoStisku > prodlevaKlavesy)
{
        if ((hra.klavesy.IsKeyDown(Keys.Right)) &&
           (!hraciPlocha.Kolize(kostka, new Vector2(kostka.pozice.X + 1, kostka.pozice.Y))))
                kostka.pozice.X++; // pohyb doprava
        if ((hra.klavesy.IsKeyDown(Keys.Left)) &&
                (!hraciPlocha.Kolize(kostka, new Vector2(kostka.pozice.X - 1, kostka.pozice.Y))))
                kostka.pozice.X--; // doleva
        casOdPoslednihoStisku = 0;
}

Vyzkoušejte si to, rychlost můžete případně regulovat změnou prodlevy v Initialize().

Vraťme se ještě k rotaci. I když funguje, může nastat situace, že po rotaci bude kostka kolidovat (natočí se tak, že se zapasuje do jiné nebo vyleze z hrací plochy). V takovém případě nebudeme chtít, aby k rotaci došlo. Tohoto chování dosáhneme malým trikem, kostku orotujeme stejně, jako to děláme teď, ale pokud v tu chvíli začne kolidovat, orotujeme ji ještě 3x, tím pádem se vrátí do původní polohy a navenek se nestane vůbec nic :)

Obsluhu rotace upravíme takto:

if (hra.NovaKlavesa(Keys.Enter) || hra.NovaKlavesa(Keys.Up))
{
        kostka.Orotuj();
        // Orotovaná kostka koliduje - vrátím rotaci zpět
        if (hraciPlocha.Kolize(kostka, kostka.pozice))
                for (int i = 0; i < 3; i++)
                        kostka.Orotuj();
}

Protože hráč bude jistě netrpělivý a kostky padají zpočátku pomalu, umožníme mu zrychlit jejich pád pravým CTRL. Rychlost již tedy nebude vždy stejná a proměnná rychlost nám přestává stačit. Přidejme si do Update() pod obsluhu kláves novou proměnnou casMeziPady, kterou nastavíme na rychlost. Pokud je držena klávesa pravé CTRL, nastavíme ji na nějakou nižší hodnotu, třeba 0.15f. K podmínce držení klávesy ještě musíme přidat, aby byla rychlost vyšší, než zde 0.15f, jinak by si hráč mohl klávesou hru ve vysokých obtížnostech naopak zpomalit.

float casMeziPady = rychlost;
        if (hra.klavesy.IsKeyDown(Keys.RightControl) && (rychlost > 0.15f))
                casMeziPady = 0.15f;

Ještě upravíme podmínku pádu kostky, kde místo proměnné rychlost použijeme casMeziPady:

if (casOdPoslednihoPadu >= casMeziPady)

Vyzkoušíme.

V Tetrisu bývá zvykem ještě tlačítko, které kostku uzemní okamžitě, tím mám na mysli, že spadne dolů až tam, kde bude kolidovat. Toto chování naprogramujeme na klávesu šipka dolů. Bude stačit volat metodu Spadni() v cyklu tak dlouho, dokud o pozici níže nedojde ke kolizi. Takto bude kostka připravena těsně před kolizí. Ještě poté přičteme do proměnné casOdPosledni­hoPadu, aby se vyvolala obsluha pádu kostky níže. Následující kód umístíme za obsluhu kláves:

if (hra.NovaKlavesa(Keys.Down))
{
        while (!hraciPlocha.Kolize(kostka, new Vector2(kostka.pozice.X, kostka.pozice.Y + 1)))
                kostka.Spadni();
        casOdPoslednihoPadu += casMeziPady;
}

Vyzkoušíme.

Ukázkový Tetris v XNA Game Studio

To je pro dnešek vše, příště level dokončíme :)


 

Stáhnout

Staženo 288x (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 (3 hlasů) :
55555


 



 

 

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

Avatar
David Čápka
Tým ITnetwork
Avatar
David Čápka:

V Initialize() se do pozice dosazuje, k té hlášce nevidím důvod a ani ji u sebe nedostávám.

Odpovědět 4.10.2012 21: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
matesax
Redaktor
Avatar
Odpovídá na David Čápka
matesax:

Nullová byla proměnná mraky - nyní jí nastavím na texturu - to by pak mělo jít... (Snad.) :)

Ovšem jak vyřeším jeden problém, narážím na další nullové hodnoty... :)

Editováno 4.10.2012 22:05
 
Odpovědět 4.10.2012 22:03
Avatar
samo007
Redaktor
Avatar
Odpovídá na David Čápka
samo007:

Máš správu!

Nechcem rušiť,
Asi je priam nutné upozorňovať priamo na devbook-u na správy. Vraj je to zložité, ale bez toho správy takmer nemajú zmysel a člen môže len dúfať, že si správu prijímateľ všimne.

 
Odpovědět 4.10.2012 22:10
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na samo007
David Čápka:

Děláme na tom, není to jen o tom nabastlit nějaký kód, ta správa událostí se musí navrhnout.

Odpovědět 4.10.2012 22:14
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
matesax
Redaktor
Avatar
Odpovídá na samo007
matesax:

Ale to je na koho? :) (Netuším, kam to chodí...)

 
Odpovědět 5.10.2012 5:54
Avatar
samo007
Redaktor
Avatar
Odpovídá na matesax
samo007:

Správy zobrazíš kliknutím na Můj účet > Zprávy

 
Odpovědět 5.10.2012 6:06
Avatar
matesax
Redaktor
Avatar
Odpovídá na samo007
matesax:

No tam mám jen možnost poslat zprávu... :)

 
Odpovědět 5.10.2012 6:20
Avatar
samo007
Redaktor
Avatar
Odpovídá na matesax
samo007:

To preto, že je to zatiaľ v alfa verzii. Ak pošleš správu, alebo ti príde správa, vytvorí sa nový priečinok so správami.

 
Odpovědět 5.10.2012 14:26
Avatar
mackulinm
Člen
Avatar
mackulinm:

Ahoj, kde by mohla byt chyba? vsetko som ochcekoval co sa dalo aj tak som to nemohol nastelovat.

 
Odpovědět 3.4.2013 12:48
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na mackulinm
David Čápka:

U článku máš ke stažení zdrojový kód, tak se podívej :) Vypadá to, že u vykreslování nepřičítáš offset nebo ho máš nastavený na 0.

Odpovědět 3.4.2013 12:53
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
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 24. Zobrazit vše