13. díl - XNA: Herní menu

C# .NET XNA game studio Robotris XNA: Herní menu

V minulém dílu seriálu XNA tutoriálů 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 něj 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

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();
Ukázkový Tetris v XNA Game Studio

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 vykreslovatelná a bude se jmenovat KomponentaPoloz­kyMenu. 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.cs. 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 KomponentaPoloz­kyMenu. 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 pozici 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.cs 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 KomponentaPoloz­kyMenu 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 KomponentaPoloz­kyMenu:

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 KomponentaPoloz­kyMenu:

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

A za ní rovnou přidání njě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.spri­tefont 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

Vidíme, že i přepínání položek funguje dobře. Příště zprovozníme klikání na položky a vytvoříme si obrazovku autoři se skrolujícím textem.


 

Stáhnout

Staženo 297x (6.66 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 (4 hlasů) :
55555


 


Miniatura
Předchozí článek
XNA: Správa herních obrazovek
Miniatura
Všechny články v sekci
Od nuly k tetrisu v XNA game studio
Miniatura
Následující článek
XNA: Skrolující text (autoři hry)

 

 

Komentáře

Avatar
Jakub Lásko[Saarix]:

Opravdu pěkný návod na menu :) díky.

Odpovědět 15.10.2012 15:37
Časem je vše možné.
Avatar
David Čápka
Tým ITnetwork
Avatar
Odpovídá na Jakub Lásko[Saarix]
David Čápka:

Další díl je na cestě :) Ještě jich pár bude.

Odpovědět 15.10.2012 15:43
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:

Proč text, velikost a pozice nejsou vlastnosti?

 
Odpovědět 23.10.2012 16:27
Avatar
Lukáš Hypša:

Nebylo by v metodě Update() mnohem bezpečnější použít enum pro výběr klávesy, než obyčejný string který se dá snadno pokazit?

Odpovědět 4. srpna 14:58
I když se programování učím jenom z interetu, velmi mě baví a doufám, že se tím jednou budu i živit.
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 4 zpráv z 4.