Lekce 8 - Hra tetris v MonoGame: Hrací plocha
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
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 206x (11.9 MB)
Aplikace je včetně zdrojových kódů v jazyce C#