IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

5. diel - Rozdelenie MonoGame hry do komponentov

V minulej lekcii, Zvuky, hudba, klávesnica a myš v MonoGame , sme si ukázali prehrávanie zvukov a prácu s klávesnicou a myšou. Dnes si povieme niečo o architektúre hry, ukážeme si, ako ju rozdeliť do niekoľkých samostatných komponentov. Po dnešnom dieli budeme mať už všetko pripravené na to, aby sme mohli začať programovať samotnú logiku hry Tetris.

Asi nám je jasné, že hra bude obsahovať niekoľko obrazoviek. Tými bude menu, samotný level, potom napríklad obrazovka s Rolujúcim textom o autoroch hry, on-line skóre tabuľka a tak podobne. Každá táto obrazovka bude potom zložená z ďalších súčastí, napr. Padajúce kocka, hracia plocha alebo tie plaviace mraky, čo sme si na začiatku seriálu naprogramovali.

Keby sme celú hru nabušili do jednej triedy, bolo by to veľmi neprehľadné. K dekompozícii hry do viacerých tried sa nám ponúka niekoľko spôsobov, všetky si počas seriálu ukážeme, aby sme videli ich výhody i nevýhody. Počas kurzu si vytvoríme jednoduchú komponentová-objektovú architektúru.

Herné komponenty

Jedným zo spôsobu, ako hru dekomponovať na niekoľko časti sú tzv. Herné komponenty. Tie pre nás sú nachystané priamo ako súčasti frameworku MonoGame. Väčšina tutoriálov o nich úplne mlčia, aj keď sa jedná o pomerne dobrý spôsob, ako si uľahčiť prácu.

Komponent je čo najviac samostatná časť hry s čo najmenším počtom závislostí. Svojou vnútornou štruktúrou pripomína samotnú hru, teda triedu Game. Má metódy ako LoadContent(), Initialize(), Update() a podobne. Môžeme si ju predstaviť ako hru v hre. Komponenta by mala byť čo najviac nezávislá, tento predpoklad nám dokonale spĺňajú naše plaviace mraky. Presunieme ich teda do komponenty.

Z hry vymažeme všetko nepotrebné z minulých pokusov, teda hlavne myš a nejaké zbytočné vykresľovanie. Konkrétne odstránime premenné mys, obdelnikRobota a všetko s nimi spojené, ďalej vykresľovanie písma a reakciu na Enter. Naše 2 metódy pre prácu s klávesnicou a hudbou ponecháme, rovnako tak idúci mraky a ďalšie 2 pozadia. Celý obsah hry ponecháme (teraz mám na mysli Content).

K projektu Robotris pridáme nový komponent, ktorú pomenujeme KomponentaMraky (pravým na projekt Robotris v Solution Exploreru -> Add -> New Item -> Class):

Visual Studio nám vygenerovalo triedu, ktorú podědíme od Microsoft.Xna.Framework.GameComponent. V konstruktoru sa odovzdáva inštancie našej hry, presnejšie jej predka Game.

Komponent bude vyzerať takto:

public class KomponentaMraky : Microsoft.Xna.Framework.GameComponent
{
    public KomponentaMraky(Game game)
        : base(game)
    {

    }
}

Metódy LoadContent(), Initialize() a Update() sú poděděné a môžeme ich prepísať.

Template na vytvorenie komponenty v základnom balíčku MonoGame bohužiaľ nie je a teda musíme komponent vytvoriť takto ručne.

Vykreslovatelná komponenta

GameComponent nedisponuje vykresľovaním. Aj keď sa nám v hre aj takéto komponenty môžu hodiť, vykresľovanie u mrakov potrebovať budeme. Našťastie MonoGame ponúka ešte jeden typ komponenty a to DrawableGameComponent, na tohto predka zmeníme súčasnej GameComponent v hlavičke triedy:

public class KomponentaMraky : Microsoft.Xna.Framework.DrawableGameComponent

Budeme potrebovať komponente nejakým spôsobom odovzdať spoločné dáta hry, to urobíme modifikáciou konstruktoru tak, aby jeho parametrom bola konkrétna trieda našej hry, zameníme teda Game na Hra. Ďalej vytvoríme v komponente privátnej premennú hra typu Hra a inštanciu odovzdanú konštruktor do nej uložíme. Modifikovaný konštruktor komponenty vyzerá takto:

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

Teraz komponente pridáme metódy LoadContent(), Initialize(), Update() a Draw(). Stačí začať písať public override... a VS nám už ponúka zoznam metód, ktoré môžeme triede dodať púhym odenterováním:

Pridanie metód XNA komponente - Od nuly k tetrisu v MonoGame

Kostra komponenty je pripravená. Pretože sa určite bude hodiť aj do budúcna (k tvorbe ďalších komponentov), vložím sem jej zdrojový kód:

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

    private Hra hra;

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

Až budete s týmto kódom tvoriť ďalší komponent (čo bude za moment), nezabudnite sa menom triedy zmeniť aj meno konstruktoru.

Komentáre som opäť vynechal, sú na vás. Teraz do komponenty presunieme z hry všetko, čo sa týka mrakov. Malo by to byť jednoduché, len musíme dodať, že k Content v LoadContent() pristúpime ako k hra.Content, podobne tomu urobíme v Draw() s hra.spriteBatch. Ten sme nemali verejný, preto to v hre napravíme:

public LepsiSpriteBatch spriteBatch;

V Draw() nezabudneme pripísať Begin() a End(), musí byť tak v Draw() v hre, tak v Draw() v komponente!

Dnes to máme také veľa zdrojákové, mali by ste dospieť k niečomu podobnému:

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

    private int zmena;
    private int smer;
    private Texture2D mraky;
    private Vector2 pozice;
    private Hra hra;

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

    public override void Initialize()
    {
        pozice = new Vector2(0, 0);
        zmena = 0;
        smer = 1;

        base.Initialize();
    }

    protected override void LoadContent()
    {
        mraky = hra.Content.Load<Texture2D>(@"Sprity\spr_mraky");

        base.LoadContent();
    }

    public override void Update(GameTime gameTime)
    {
        // posun mraků doleva
        pozice.X--;
        // návrat na startovní pozici po vyjetí z obrazovky
        if (pozice.X < -(mraky.Width))
            pozice.X = 0;

        // změna barvy mraků dle směru
        zmena += smer;
        if (zmena >= 96)
            smer = -1;
        if (zmena <= 0)
            smer = 1;

        base.Update(gameTime);
    }

    public override void Draw(GameTime gameTime)
    {
        hra.spriteBatch.Begin();
        Color barva = new Color(128 + zmena, 255 - zmena, 128 + zmena);
        for (int i = 0; i < 6; i++)
            hra.spriteBatch.Draw(mraky, new Vector2(pozice.X + i * mraky.Width, 0), barva * 0.8f);
        hra.spriteBatch.End();

        base.Draw(gameTime);
    }

}

Sprevádzkovanie necháme na koniec. Hre pridáme ďalšie vykreslovatelnou komponent, pomenujeme ju KomponentaLevel. V nej sa bude odohrávať samotný gameplay. Ten my zatiaľ nemáme, ale vykreslíme v nej aspoň pozadí levelu, to je to s robotom. Určite to zvládnete, postup je obdobný, ako pri mrakov, presunúť vykreslenie pozadia z hry do tohto komponentu.

Výsledok bude vyzerať asi takto:

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

    private Hra hra;
    private Texture2D pozadi;

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

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

    protected override void LoadContent()
    {
        pozadi = hra.Content.Load<Texture2D>(@"Sprity\pozadi_level");
        base.LoadContent();
    }

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

    public override void Draw(GameTime gameTime)
    {
        hra.spriteBatch.Begin();
        hra.spriteBatch.Draw(pozadi, new Vector2(0, 0), Color.White);
        hra.spriteBatch.End();

        base.Draw(gameTime);
    }

}

Pripojenie komponentov k hre

V hre nám zostalo v Draw() len vykreslenie pozadia kociek a v Update() obsluha stavu klávesnice. Presunieme sa do Initialize() a na začiatku vytvoríme naše komponenty, ktorým odovzdáme inštanciu hry v konstruktoru:

KomponentaMraky mraky = new KomponentaMraky(this);
KomponentaLevel level = new KomponentaLevel(this);

Teraz príde mágie! Hra má na sebe vlastnosť Components. To je kolekcia (presnejšie List) komponentov, z ktorých sa skladá. Do listu pridáme komponenty v tom poradí, v akom chceme, aby sa obnovovali a vykresľovali:

Components.Add(mraky);
Components.Add(level);

Spustíme a vidíme, že hra funguje rovnako dobre, ako predtým. Komponenty sa totiž obsluhujú úplne samy a my sa o ne nemusíme starať. Kód hry je prehľadný a veľmi krátky. Komponenty obsahujú len to, čo sa ich týka. Všetko je možné vďaka volanie base.Initialize(), base.Update() atď. V metódach hry, čo má v predkovi za následok volanie metód všetkých aktívnych komponentov hry.

Ešte malé, kozmetické úpravy na záver. Súbor Game1.cs premenujeme na Hra.cs. Pretože komponentov bude viac, vytvoríme na ne zložku (Pravým na Robotris -> Add -> New Folder), ktorú pomenujeme Komponenty/. Naše 2 komponenty do nej jednoducho presunieme myšou.

Solution explorer XNA hry Robotris - Od nuly k tetrisu v MonoGame

Dospeli sme do fázy, kedy nám nič nebráni začať tetrisové : P Hneď nabudúce, v lekcii Hra tetris v MonoGame: Kostka , sa na to pozrieme.


 

Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 484x (11.85 MB)
Aplikácia je vrátane zdrojových kódov v jazyku C#

 

Predchádzajúci článok
Zvuky, hudba, klávesnica a myš v MonoGame
Všetky články v sekcii
Od nuly k tetrisu v MonoGame
Preskočiť článok
(neodporúčame)
Hra tetris v MonoGame: Kostka
Článok pre vás napísal David Hartinger
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
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 sa informačné technológie naučil na Unicorn University - prestížnej súkromnej vysokej škole IT a ekonómie.
Aktivity