Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 12 - MonoGame: Správa herních obrazovek

V minulé lekci, Hra tetris v MonoGame: Vychytávky v levelu, jsme do levelu s Tetrisem přidali vylepšenou rotaci a zaměřovač. Tím pro nás Tetrisování končí a budeme hru považovat za hotovou. Vy si tam samozřejmě můžete dodat další power-upy, herní módy a podobně.

Dnes se zaměříme na herní obrazovky, díky kterým budeme poté schopni vytvořit např. herní menu.

Herní obrazovka

Víme, že MonoGame není engine, ale framework. Proto MonoGame samotná kromě komponent neposkytuje žádný způsob, jak se vypořádat s přepínáním herních obrazovek. Herní obrazovkou myslím nějakou samostatnou část hry, třeba level, menu, skóre tabulku, obrazovku autoři a podobně. Hlavní menu bude mít určitě jinou logiku, než level s Tetrisem. Navíc je třeba docílit toho, abychom herní obrazovky mohli přepínat a ukládat jejich stav.

Možností je samozřejmě mnoho. Úplně ta nejhloupější je napsat celou hru do jednoho souboru a udělat mnoho stavů (stav pro menu, stav pro hru a tak). Výsledný soubor by ale asi nevypadal úplně hezky a když umíme používat herní komponenty, určitě je využijeme. Občas jsem viděl způsob, kdy je co obrazovka, to komponenta. Komponenty se poté mezi sebou přepínají (např. z komponenty Menu do komponenty Level). Pro malé hry to může být dostačující, ale pro větší projekty již nikoli. Problém je v tom, že jsme omezeni na 1 komponentu pro každou obrazovku, nemohli bychom mít např. v tetrisu oddělené Mraky a Level, případně ještě nějaké další součásti, které by se ve složitější hře našly.

Řešení, které si zde ukážeme, definuje herní obrazovku jako soubor komponent. Do hry jsou vloženy všechny komponenty a při přepnutí obrazovky se zakáží a povolí se jen ty, které používá konkrétní obrazovka.

Přidejme si k projektu třídu HerniObrazovka. Její instance budou reprezentovat jednotlivé herní obrazovky. Třídu si nastavte jako public. Bude mít 2 privátní atributy, jeden bude kolekce komponent, které obrazovka obsahuje. Druhý instance Hry:

private List<GameComponent> komponenty;
private Hra hra;

Kvůli použití typu GameComponent dodáme nad třídu using:

using Microsoft.Xna.Framework;

Přidáme veřejnou metodu PridejKomponentu(), která komponentu předanou v parametru vloží do interní kolekce komponenty a zároveň i do kolekce Components hry. Do Components se musí komponenta vložit pouze tehdy, když tam již není. Některé obrazovky totiž využívají stejné komponenty. Metoda vypadá takto:

public void PridejKomponentu(GameComponent komponenta)
{
    komponenty.Add(komponenta);
    if (!hra.Components.Contains(komponenta))
        hra.Components.Add(komponenta);
}

Díky tomu, že je list privátní, všechna přidání proběhnou přes tuto metodu a můžeme si být jistí, že budou dané komponenty i v Components hry.

V konstruktoru třídy si předáme v parametru instanci hry tak, jak jsme zvyklí z komponent a herních objektů. Dále pomocí klíčového slova params umožníme vložit několik parametrů typu GameComponent. Ty proiterujeme a přidáme pomocí naší metody.

public HerniObrazovka(Hra hra, params GameComponent[] komponenty)
{
    this.hra = hra;
    this.komponenty = new List<GameComponent>();
    foreach (GameComponent komponenta in komponenty)
    {
        PridejKomponentu(komponenta);
    }
}

Jako poslední přidáme metodu VratKomponenty(), která vrátí komponenty v obrazovce jako pole:

public GameComponent[] VratKomponenty()
{
    return komponenty.ToArray();
}

Pro úplnost si můžete napsat metodu pro vymazání komponenty, v nějaké složitější hře by se mohly za běhu přidávat a odebírat, ale pro naše účely to není nutné.

Menu

Abychom měli na čem testovat, přidáme si ke hře komponentu KomponentaMenu (do složky Komponenty/, ale namespace ponechte pouze na Robotris). Bude se opět jednat o DrawableComponent, jak ji přidat jsme si ukázali v tutoriálu Rozdělení MonoGame hry do komponent. Pro jistotu si ukážeme i kód třídy:

public class KomponentaMenu : Microsoft.Xna.Framework.DrawableGameComponent
{

        private Hra hra;

        public KomponentaMenu(Hra hra)
                : base(hra)
        {
                this.hra = hra;
        }

        public override void Initialize()
        {
                base.Initialize();
        }

        protected override void LoadContent()
        {
                base.LoadContent();
        }

        public override void Update(GameTime gameTime)
        {
                base.Update(gameTime);
        }

        public override void Draw(GameTime gameTime)
        {
                base.Draw(gameTime);
        }
}

Komponentu zatím ponecháme prázdnou.

Obsluha herních obrazovek

Samotnou obsluhu herních obrazovek vložíme do třídy Hra. Docela mi to dává smysl a je jednoduše viditelná ze všech komponent. Další možnost by byla vytvořit nějaký manažer obrazovek.

Přesuneme se do Hra.cs, kde do atributů třídy přidáme 2 veřejné herní obrazovky, budou to obrazovky pro menu a pro level:

public HerniObrazovka obrazovkaMenu, obrazovkaLevel;

Do Initialize() přidáme k vytvoření komponent i vytvoření komponenty menu:

KomponentaMenu menu = new KomponentaMenu(this);

Připojení komponent ke hře do kolekce Components úplně odstraníme, protože to za nás dělá herní obrazovka. Namísto toho si vytvoříme herní obrazovky:

obrazovkaMenu = new HerniObrazovka(this, mraky, menu);
obrazovkaLevel = new HerniObrazovka(this, mraky, level);

Vidíme, že můžeme použít tu samou komponentu Mraky ve více obrazovkách.

Přidáme privátní metodu k přepnutí stavu obrazovky. Jako parametr bude brát komponentu a stav (zapnuto/vypnuto). GameComponent v MonoGame má vlastnost Enabled, která umožňuje zapínat/vypínat vykonávání metody Update(). Pokud ho vypneme, komponenta se zastaví. Pokud je komponenta ještě typu DrawableGameComponent (což téměř vždy bude), nastavíme i vlastnost Visible. Ta udává, jestli se má vykonávat metoda Draw() a tedy jestli se má komponta vykreslovat.

private void PrepniKomponentu(GameComponent komponenta, bool zapnout)
{
    komponenta.Enabled = zapnout;
    if (komponenta is DrawableGameComponent)
        ((DrawableGameComponent)komponenta).Visible = zapnout;
}

Když takto komponentu vypneme, nebude se vykreslovat ani obsluhovat. Stále však existuje a když ji poté zapneme, bude přesně ve stavu, v jakém jsme ji nechali. To se nám někdy může hodit (např. přechod mezi různými lokacemi, minihrami, z menu do hry atd.).

Vrátíme se do Initialize(), po vytvoření obrazovek proiterujeme všechny komponenty hry a vypneme je:

foreach (GameComponent komponenta in Components)
{
    PrepniKomponentu(komponenta, false);
}

Nakonec přidáme samotnou metodu k přepnutí obrazovky. Bude veřejná a jako parametr bude brát obrazovku, do které se chceme přepnout.

public void PrepniObrazovku(HerniObrazovka obrazovka)
{
}

Nejprve si z obrazovky vyžádáme komponenty, které obsahuje:

GameComponent[] povolene = obrazovka.VratKomponenty();

Proiterujeme komponenty hry a zjistíme, jestli je daná komponenta povolená. To uděláme tak, že se ji pokusíme vyhledat v poli povolených komponent. Nakonec komponentu nastavíme na požadovaný stav.

foreach (GameComponent komponenta in Components)
{
    bool povolena = povolene.Contains(komponenta);
    PrepniKomponentu(komponenta, povolena);
}

V metodě ještě updatujeme minulý stav kláves, protože přepnutí komponent má za následek jeho vynechání:

klavesyMinule = klavesy;

Na konec metody LoadContent() přidáme přepnutí obrazovky (nastane až ve chvíli, kdy bude vše načteno):

PrepniObrazovku(obrazovkaMenu);

Když hru nyní spustíme, měli bychom vidět menu, které je představováno zatím jen mraky.

Ukázkový Tetris v XNA Game Studio - Od nuly k tetrisu v MonoGame

Přesuneme se do KomponentaMenu a přidáme do Update() reakci na Enter, která nás přesune do hry:

if (hra.NovaKlavesa(Keys.Enter))
                hra.PrepniObrazovku(hra.obrazovkaLevel);

Vyzkoušíme a vidíme, že jsme naprogramovali přepínání obrazovek.

Příště, v lekci MonoGame: Herní menu, se zaměříme na menu :)


 

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 277x (12.1 MB)
Aplikace je včetně zdrojových kódů v jazyce C#

 

Předchozí článek
Hra tetris v MonoGame: Vychytávky v levelu
Všechny články v sekci
Od nuly k tetrisu v MonoGame
Přeskočit článek
(nedoporučujeme)
MonoGame: Herní menu
Článek pro vás napsal David Hartinger
Avatar
Uživatelské hodnocení:
4 hlasů
David je zakladatelem ITnetwork a programování se profesionálně věnuje 15 let. Má rád Nirvanu, nemovitosti a svobodu podnikání.
Unicorn university David se informační technologie naučil na Unicorn University - prestižní soukromé vysoké škole IT a ekonomie.
Aktivity