IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
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 13 - MonoGame: Herní menu

V minulé lekci, MonoGame: Správa herních obrazovek, jsme si naprogramovali správu herních obrazovek a připravili komponentu pro herní menu.

Nic nám nebrání v tom, abychom se do menu dnes pustili :)

Menu budeme chtít samozřejmě hezké a s nějakými efekty. Nejprve si do naší komponenty KomponentaMenu přidáme pozadí. Zde si ho stáhněte:

Pozadí menu - Od nuly k tetrisu v MonoGame

a přidejte do složky Sprity/ ve hře. Přidáme příslušný atribut:

private Texture2D pozadi;

Načtení do LoadContent():

pozadi = hra.Content.Load<Texture2D>(@"Sprity\pozadi_menu");

A vykreslení do Draw():

hra.spriteBatch.Begin();
hra.spriteBatch.Draw(pozadi, new Vector2(0, 0), Color.White);
hra.spriteBatch.End();

Výsledek:

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

Menu si rozdělíme do dvou komponent, aby jsme ho měli znovupoužitelné a dalo se použít bez velkých změn i v jiné hře nebo obrazovce. KomponentaMenu se bude starat o vykreslení těch částí menu, které jsou specifické pro hru Robotris. Přidejme si ještě jednu univerzální komponentu, která bude spravovat jednotlivé položky menu. Komponenta bude vykreslitelná a bude se jmenovat KomponentaPolozkyMenu. Opět pro úplnost uvedu její prázdný zdrojový kód (nezapomeňte, že namespace má být opět jen Robotris, i když je ve složce Komponenty/):

namespace Robotris
{
    public class KomponentaPolozkyMenu : Microsoft.Xna.Framework.DrawableGameComponent
    {

            private Hra hra;

            public KomponentaPolozkyMenu(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);
            }
    }
}

Zatím ji ponecháme prázdnou a přidáme další třídu, jejíž instance bude reprezentovat jednu položku menu. Třídu přidáme mezi další třídy přímo v projektu Robotris.

Položka menu

Třída se bude jmenovat PolozkaMenu. Bude obsahovat údaje o své pozici (souřadnicích na obrazovce), velikosti a textu. Velikosti proto, že vybranou položku budeme škálovat a to včetně animace. Třída je velmi jednoduchá, uveďme si rovnou její zdrojový kód:

public class PolozkaMenu
{
    public string text;
    public Vector2 pozice;
    public float velikost;

    public PolozkaMenu(string text, Vector2 pozice)
    {
        this.text = text;
        this.pozice = pozice;
        velikost = 0.6f;
    }
}

Kvůli použití struktury Vector2 dodáme using na XNA framework:

using Microsoft.Xna.Framework;

Přesuňme se do KomponentaPolozkyMenu. Tato komponenta bude správce položek menu, bude je uchovávat, přepínat a vykreslovat. Konkrétní události (co která položka po stisknutí vykoná) vložíme později do KomponentaMenu, protože to je závislé na konkrétní hře.

Přidáme list položek jako atribut, dále aktuálně vybranou položku a pozici menu:

private List<PolozkaMenu> polozky;
public PolozkaMenu vybranaPolozka;
private Vector2 pozice;

Komponentu se pokusíme udělat co nejvíce uzpůsobitelnou, proto přidáme ještě barvu vybrané a nevybrané položky a výšku položky (tam poté uložíme výšku písma):

private Color nevybranaBarva;
private Color vybranaBarva;
private int vyska;

Přesuneme se do konstruktoru, kterému přidáme další parametry a kde inicializujeme atributy:

public KomponentaPolozkyMenu(Hra hra, Vector2 pozice, Color nevybranaBarva, Color vybranaBarva, int vyska): base(hra)
{
    this.pozice = pozice;
    this.hra = hra;
    this.nevybranaBarva = nevybranaBarva;
    this.vybranaBarva = vybranaBarva;
    this.vyska = vyska;
    polozky = new List<PolozkaMenu>();
    vybranaPolozka = null;
}

Správa položek menu

Přidejme několik metod pro práci s kolekcí položek menu. Začněmě přidáním nové položky. Metoda bude brát v parametru text položky. Uvnitř vytvoří novou položku, té nastaví pozici pod již existující položky v menu, podle jejich výšky. Položku následně přidá do interní kolekce a pokud není žádná vybraná, vybere právě ji (vybraná tedy bude vždy 1. položka v menu). Metoda by mohla vypadat takto:

public void PridejPolozku(string text)
{
    // nastavení pozice podle pořadí položky
    Vector2 p = new Vector2(pozice.X, pozice.Y + polozky.Count * vyska);
    PolozkaMenu polozka = new PolozkaMenu(text, p);
    polozky.Add(polozka);
    // výběr první položky
    if (vybranaPolozka == null)
        vybranaPolozka = polozka;
}

Po přidání položek bude třeba vybírat další a předchozí položku. V metodě pro vybrání další položky nebude mimo přechodu z poslední zpět na první nic zajímavého. Protože si neudržujeme index aktuální položky, ale jen její instanci, musíme její index vždy znovu zjistit, ale to nás v našem případě nijak netrápí.

public void VyberDalsi()
{
    int index = polozky.IndexOf(vybranaPolozka);
    if (index < polozky.Count - 1)
        vybranaPolozka = polozky[index + 1];
    else
        vybranaPolozka = polozky[0];
}

Metoda pro výběr předchozí položky bude obdobná:

public void VyberPredchozi()
{
    int index = polozky.IndexOf(vybranaPolozka);
    if (index > 0)
        vybranaPolozka = polozky[index - 1];
    else
        vybranaPolozka = polozky[polozky.Count - 1];
}

Obsluha položek

Obsluhu položek nastavíme na klávesy dolů a nahoru. Budeme tak přepínat další a předchozí položku. Přesuňme se do Update() a dodejme reakci na tyto klávesy:

// reakce na klávesy
if (hra.NovaKlavesa(Keys.Up))
    VyberPredchozi();
if (hra.NovaKlavesa(Keys.Down))
    VyberDalsi();

Vykreslení položek

Protože u položek budeme potřebovat specifikovat i velikost, nevystačíme si s naší metodou TextSeStinem(). Proto si třídu LepsiSpriteBatch obohatíme o další metodu, která vykreslí škálovaný text se stínem pomocí dalšího přetížení metody DrawString():

public void TextSeStinemSkalovany(SpriteFont spriteFont, string text, Vector2 position, Color color, float size)
{
    DrawString(spriteFont, text, new Vector2(position.X + 2, position.Y + 2), Color.Black * 0.8f, 0.0f, new Vector2(0, 0), size, SpriteEffects.None, 0);
    DrawString(spriteFont, text, position, color, 0.0f, new Vector2(0, 0), size, SpriteEffects.None, 0);
}

Nyní už je pro nás hračka vykreslit v Draw() položky menu:

hra.spriteBatch.Begin();
foreach (PolozkaMenu polozka in polozky)
{
    Color barva = nevybranaBarva;
    if (polozka == vybranaPolozka)
        barva = vybranaBarva;
    hra.spriteBatch.TextSeStinemSkalovany(hra.fontBlox, polozka.text, polozka.pozice, barva, polozka.velikost);
}
hra.spriteBatch.End();

Zde je závislost na font fontBlox ze hry. Ideálně bychom měli umožnit font předat, ale kvůli tomu, že font existuje až po volání LoadContent() by se nám práce zkomplikovala a proto toto zanedbáme.

Vložení komponent do sebe

Komponentu KomponentaPolozkyMenu nyní vložíme do komponenty KomponentaMenu. Takto jsme do sebe komponenty ještě nevkládali, pojďme si to vyzkoušet. Přesuňme se do KomponentaMenu.cs, kde přidejme atribut s instancí komponenty KomponentaPolozkyMenu:

private KomponentaPolozkyMenu polozkyMenu;

Samotnou instanci si však vytvoříme v Hra.cs (protože zde potřebujeme komponentu přidat do obrazovky). Šlo by to samozřejmě navrhnout i jinak, ale preferujme jednoduchost. Do KomponentaMenu instanci dostaneme konstruktorem, který modifikujeme:

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

Přesuňme se do Hra.cs a v Initialize() a přidejme vytvoření instance komponenty KomponentaPolozkyMenu:

KomponentaPolozkyMenu polozkyMenu = new KomponentaPolozkyMenu(this, new Vector2(700, 250), Color.Red, Color.Yellow, 80);

A za ní rovnou přidání několika položek do menu:

polozkyMenu.PridejPolozku("StArT");
polozkyMenu.PridejPolozku("SkOrE");
polozkyMenu.PridejPolozku("AuToRi");
polozkyMenu.PridejPolozku("KoNeC");

Střídám v názvu položek velká a malá písmena, protože to s fontem Blox vypadá dobře :)

Do volání konstruktoru KomponentaMenu doplníme instanci polozkyMenu.

KomponentaMenu menu = new KomponentaMenu(this, polozkyMenu);

A stejně tak ji dodáme i do vytváření obrazovky obrazovkaMenu:

obrazovkaMenu = new HerniObrazovka(this, mraky, menu, polozkyMenu);

Otevřete si font_blox.spritefont ve složce Fonty/ a změňte Style z Regular na Bold. Projekt spustíme, měli byste dospět takovéhoto výsledku:

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

Vidíme, že i přepínání položek funguje dobře.

Příště, v lekci MonoGame: Skrolující text (autoři hry), zprovozníme klikání na položky a vytvoříme si obrazovku autoři se skrolujícím textem.


 

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

 

Předchozí článek
MonoGame: Správa herních obrazovek
Všechny články v sekci
Od nuly k tetrisu v MonoGame
Přeskočit článek
(nedoporučujeme)
MonoGame: Skrolující text (autoři hry)
Článek pro vás napsal David Hartinger
Avatar
Uživatelské hodnocení:
6 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